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,1630 @@
|
|
|
1
|
+
# AI Agent-Optimized Search Implementation Plan
|
|
2
|
+
|
|
3
|
+
> **For Claude:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task.
|
|
4
|
+
|
|
5
|
+
**Goal:** Transform semantic search from human-focused snippet delivery to AI agent-optimized knowledge retrieval with pattern detection, progressive context delivery, and agent feedback loops.
|
|
6
|
+
|
|
7
|
+
**Architecture:** Multi-phase enhancement preserving existing search core. Phase 1 adds agent-optimized output formats and progressive context APIs. Phase 2 builds static analysis infrastructure and code graph. Phase 3 implements pattern detection and mining. Phase 4 adds agent query understanding. Phase 5 creates feedback loops for continuous improvement.
|
|
8
|
+
|
|
9
|
+
**Tech Stack:** TypeScript, @babel/parser for AST, LanceDB for pattern storage, MCP (Model Context Protocol) for agent integration, existing Transformers.js embeddings
|
|
10
|
+
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
## Phase 1: Agent-Optimized Output Format (MVP)
|
|
14
|
+
|
|
15
|
+
### Task 1.1: Enhanced Result Schema
|
|
16
|
+
|
|
17
|
+
**Goal:** Add structured code unit extraction to search results for AI agents.
|
|
18
|
+
|
|
19
|
+
**Files:**
|
|
20
|
+
- Modify: `src/types/search.ts`
|
|
21
|
+
- Modify: `src/services/search.service.ts`
|
|
22
|
+
- Create: `src/services/code-unit.service.ts`
|
|
23
|
+
- Create: `tests/services/code-unit.service.test.ts`
|
|
24
|
+
|
|
25
|
+
**Step 1: Write failing test for code unit extraction**
|
|
26
|
+
|
|
27
|
+
Create `tests/services/code-unit.service.test.ts`:
|
|
28
|
+
|
|
29
|
+
```typescript
|
|
30
|
+
import { describe, it, expect } from 'vitest';
|
|
31
|
+
import { CodeUnitService } from '../src/services/code-unit.service.js';
|
|
32
|
+
|
|
33
|
+
describe('CodeUnitService', () => {
|
|
34
|
+
it('should extract full function from TypeScript code', () => {
|
|
35
|
+
const code = `
|
|
36
|
+
export function validateToken(token: string): boolean {
|
|
37
|
+
if (!token) return false;
|
|
38
|
+
return token.length > 0;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export function parseToken(token: string): object {
|
|
42
|
+
return JSON.parse(token);
|
|
43
|
+
}
|
|
44
|
+
`;
|
|
45
|
+
|
|
46
|
+
const service = new CodeUnitService();
|
|
47
|
+
const unit = service.extractCodeUnit(code, 'validateToken', 'typescript');
|
|
48
|
+
|
|
49
|
+
expect(unit).toBeDefined();
|
|
50
|
+
expect(unit.type).toBe('function');
|
|
51
|
+
expect(unit.name).toBe('validateToken');
|
|
52
|
+
expect(unit.signature).toBe('validateToken(token: string): boolean');
|
|
53
|
+
expect(unit.fullContent).toContain('export function validateToken');
|
|
54
|
+
expect(unit.startLine).toBe(2);
|
|
55
|
+
expect(unit.endLine).toBe(5);
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
it('should extract class with methods', () => {
|
|
59
|
+
const code = `
|
|
60
|
+
export class UserService {
|
|
61
|
+
constructor(private repo: UserRepo) {}
|
|
62
|
+
|
|
63
|
+
async create(data: CreateUserData): Promise<User> {
|
|
64
|
+
return this.repo.save(data);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
`;
|
|
68
|
+
|
|
69
|
+
const service = new CodeUnitService();
|
|
70
|
+
const unit = service.extractCodeUnit(code, 'UserService', 'typescript');
|
|
71
|
+
|
|
72
|
+
expect(unit.type).toBe('class');
|
|
73
|
+
expect(unit.name).toBe('UserService');
|
|
74
|
+
expect(unit.fullContent).toContain('class UserService');
|
|
75
|
+
});
|
|
76
|
+
});
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
**Step 2: Run test to verify it fails**
|
|
80
|
+
|
|
81
|
+
```bash
|
|
82
|
+
npm run test code-unit.service.test.ts
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
Expected: FAIL - "Cannot find module '../src/services/code-unit.service.js'"
|
|
86
|
+
|
|
87
|
+
**Step 3: Create CodeUnitService with minimal implementation**
|
|
88
|
+
|
|
89
|
+
Create `src/services/code-unit.service.ts`:
|
|
90
|
+
|
|
91
|
+
```typescript
|
|
92
|
+
export interface CodeUnit {
|
|
93
|
+
type: 'function' | 'class' | 'interface' | 'type' | 'const' | 'documentation' | 'example';
|
|
94
|
+
name: string;
|
|
95
|
+
signature: string;
|
|
96
|
+
fullContent: string;
|
|
97
|
+
startLine: number;
|
|
98
|
+
endLine: number;
|
|
99
|
+
language: string;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
export class CodeUnitService {
|
|
103
|
+
extractCodeUnit(code: string, symbolName: string, language: string): CodeUnit | undefined {
|
|
104
|
+
const lines = code.split('\n');
|
|
105
|
+
|
|
106
|
+
// Find the line containing the symbol
|
|
107
|
+
let startLine = -1;
|
|
108
|
+
let type: CodeUnit['type'] = 'function';
|
|
109
|
+
|
|
110
|
+
for (let i = 0; i < lines.length; i++) {
|
|
111
|
+
const line = lines[i] ?? '';
|
|
112
|
+
|
|
113
|
+
if (line.includes(`function ${symbolName}`)) {
|
|
114
|
+
startLine = i + 1; // 1-indexed
|
|
115
|
+
type = 'function';
|
|
116
|
+
break;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
if (line.includes(`class ${symbolName}`)) {
|
|
120
|
+
startLine = i + 1;
|
|
121
|
+
type = 'class';
|
|
122
|
+
break;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
if (startLine === -1) return undefined;
|
|
127
|
+
|
|
128
|
+
// Find end line (naive: next empty line or next top-level declaration)
|
|
129
|
+
let endLine = startLine;
|
|
130
|
+
let braceCount = 0;
|
|
131
|
+
let foundFirstBrace = false;
|
|
132
|
+
|
|
133
|
+
for (let i = startLine - 1; i < lines.length; i++) {
|
|
134
|
+
const line = lines[i] ?? '';
|
|
135
|
+
|
|
136
|
+
for (const char of line) {
|
|
137
|
+
if (char === '{') {
|
|
138
|
+
braceCount++;
|
|
139
|
+
foundFirstBrace = true;
|
|
140
|
+
}
|
|
141
|
+
if (char === '}') braceCount--;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
if (foundFirstBrace && braceCount === 0) {
|
|
145
|
+
endLine = i + 1;
|
|
146
|
+
break;
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
const fullContent = lines.slice(startLine - 1, endLine).join('\n');
|
|
151
|
+
|
|
152
|
+
// Extract signature (first line, cleaned)
|
|
153
|
+
const firstLine = lines[startLine - 1] ?? '';
|
|
154
|
+
const signature = this.extractSignature(firstLine, symbolName, type);
|
|
155
|
+
|
|
156
|
+
return {
|
|
157
|
+
type,
|
|
158
|
+
name: symbolName,
|
|
159
|
+
signature,
|
|
160
|
+
fullContent,
|
|
161
|
+
startLine,
|
|
162
|
+
endLine,
|
|
163
|
+
language
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
private extractSignature(line: string, name: string, type: string): string {
|
|
168
|
+
// Remove 'export', 'async', trim whitespace
|
|
169
|
+
let sig = line.replace(/^\s*export\s+/, '').replace(/^\s*async\s+/, '').trim();
|
|
170
|
+
|
|
171
|
+
if (type === 'function') {
|
|
172
|
+
// Extract just "functionName(params): returnType"
|
|
173
|
+
const match = sig.match(/function\s+(\w+\([^)]*\):\s*\w+)/);
|
|
174
|
+
if (match && match[1]) return match[1];
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
if (type === 'class') {
|
|
178
|
+
return `class ${name}`;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
return sig;
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
**Step 4: Run test to verify it passes**
|
|
187
|
+
|
|
188
|
+
```bash
|
|
189
|
+
npm run test code-unit.service.test.ts
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
Expected: PASS
|
|
193
|
+
|
|
194
|
+
**Step 5: Add code unit to SearchResult type**
|
|
195
|
+
|
|
196
|
+
Modify `src/types/search.ts`:
|
|
197
|
+
|
|
198
|
+
```typescript
|
|
199
|
+
import type { DocumentMetadata } from './document.js';
|
|
200
|
+
import type { StoreId } from './brands.js';
|
|
201
|
+
|
|
202
|
+
export interface CodeUnit {
|
|
203
|
+
type: 'function' | 'class' | 'interface' | 'type' | 'const' | 'documentation' | 'example';
|
|
204
|
+
name: string;
|
|
205
|
+
signature: string;
|
|
206
|
+
fullContent: string;
|
|
207
|
+
startLine: number;
|
|
208
|
+
endLine: number;
|
|
209
|
+
language: string;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
export interface SearchResult {
|
|
213
|
+
readonly id: string;
|
|
214
|
+
readonly score: number;
|
|
215
|
+
readonly content: string;
|
|
216
|
+
readonly metadata: DocumentMetadata;
|
|
217
|
+
readonly highlight?: string | undefined;
|
|
218
|
+
|
|
219
|
+
// NEW: Structured code unit for AI agents
|
|
220
|
+
readonly codeUnit?: CodeUnit | undefined;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
// ... rest of existing types
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
**Step 6: Run typecheck**
|
|
227
|
+
|
|
228
|
+
```bash
|
|
229
|
+
npm run typecheck
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
Expected: PASS
|
|
233
|
+
|
|
234
|
+
**Step 7: Commit**
|
|
235
|
+
|
|
236
|
+
```bash
|
|
237
|
+
git add src/services/code-unit.service.ts tests/services/code-unit.service.test.ts src/types/search.ts
|
|
238
|
+
git commit -m "feat(search): add code unit extraction service for AI agents"
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
---
|
|
242
|
+
|
|
243
|
+
### Task 1.2: Progressive Context Layers
|
|
244
|
+
|
|
245
|
+
**Goal:** Implement three-tier context delivery (summary, contextual, full) for token budget optimization.
|
|
246
|
+
|
|
247
|
+
**Files:**
|
|
248
|
+
- Modify: `src/types/search.ts`
|
|
249
|
+
- Modify: `src/services/search.service.ts`
|
|
250
|
+
- Modify: `src/cli/commands/search.ts`
|
|
251
|
+
- Create: `tests/services/search.progressive-context.test.ts`
|
|
252
|
+
|
|
253
|
+
**Step 1: Write failing test for progressive context**
|
|
254
|
+
|
|
255
|
+
Create `tests/services/search.progressive-context.test.ts`:
|
|
256
|
+
|
|
257
|
+
```typescript
|
|
258
|
+
import { describe, it, expect } from 'vitest';
|
|
259
|
+
import type { SearchResult } from '../src/types/search.js';
|
|
260
|
+
|
|
261
|
+
describe('Progressive Context', () => {
|
|
262
|
+
it('should return minimal summary by default', () => {
|
|
263
|
+
const result: SearchResult = {
|
|
264
|
+
id: 'test-1',
|
|
265
|
+
score: 0.95,
|
|
266
|
+
content: 'function code here...',
|
|
267
|
+
metadata: {
|
|
268
|
+
type: 'chunk',
|
|
269
|
+
storeId: 'test-store' as any,
|
|
270
|
+
path: 'src/auth.ts',
|
|
271
|
+
indexedAt: new Date(),
|
|
272
|
+
},
|
|
273
|
+
summary: {
|
|
274
|
+
type: 'function',
|
|
275
|
+
name: 'validateToken',
|
|
276
|
+
signature: 'validateToken(token: string): boolean',
|
|
277
|
+
purpose: 'Validates JWT token',
|
|
278
|
+
location: 'src/auth.ts:45',
|
|
279
|
+
relevanceReason: 'Matches query about token validation'
|
|
280
|
+
}
|
|
281
|
+
};
|
|
282
|
+
|
|
283
|
+
expect(result.summary).toBeDefined();
|
|
284
|
+
expect(result.context).toBeUndefined();
|
|
285
|
+
expect(result.full).toBeUndefined();
|
|
286
|
+
});
|
|
287
|
+
|
|
288
|
+
it('should include context when detail=contextual', () => {
|
|
289
|
+
// Test that context layer is populated
|
|
290
|
+
expect(true).toBe(true); // Placeholder until implemented
|
|
291
|
+
});
|
|
292
|
+
});
|
|
293
|
+
```
|
|
294
|
+
|
|
295
|
+
**Step 2: Run test**
|
|
296
|
+
|
|
297
|
+
```bash
|
|
298
|
+
npm run test search.progressive-context.test.ts
|
|
299
|
+
```
|
|
300
|
+
|
|
301
|
+
Expected: FAIL - "Property 'summary' does not exist on type 'SearchResult'"
|
|
302
|
+
|
|
303
|
+
**Step 3: Add progressive context to SearchResult type**
|
|
304
|
+
|
|
305
|
+
Modify `src/types/search.ts`:
|
|
306
|
+
|
|
307
|
+
```typescript
|
|
308
|
+
export interface ResultSummary {
|
|
309
|
+
readonly type: 'function' | 'class' | 'interface' | 'pattern' | 'documentation';
|
|
310
|
+
readonly name: string;
|
|
311
|
+
readonly signature: string;
|
|
312
|
+
readonly purpose: string;
|
|
313
|
+
readonly location: string;
|
|
314
|
+
readonly relevanceReason: string;
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
export interface ResultContext {
|
|
318
|
+
readonly interfaces: readonly string[];
|
|
319
|
+
readonly keyImports: readonly string[];
|
|
320
|
+
readonly relatedConcepts: readonly string[];
|
|
321
|
+
readonly usage: {
|
|
322
|
+
readonly calledBy: number;
|
|
323
|
+
readonly calls: number;
|
|
324
|
+
};
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
export interface ResultFull {
|
|
328
|
+
readonly completeCode: string;
|
|
329
|
+
readonly relatedCode: ReadonlyArray<{
|
|
330
|
+
readonly file: string;
|
|
331
|
+
readonly summary: string;
|
|
332
|
+
readonly relationship: string;
|
|
333
|
+
}>;
|
|
334
|
+
readonly documentation: string;
|
|
335
|
+
readonly tests?: string | undefined;
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
export interface SearchResult {
|
|
339
|
+
readonly id: string;
|
|
340
|
+
readonly score: number;
|
|
341
|
+
readonly content: string;
|
|
342
|
+
readonly metadata: DocumentMetadata;
|
|
343
|
+
readonly highlight?: string | undefined;
|
|
344
|
+
readonly codeUnit?: CodeUnit | undefined;
|
|
345
|
+
|
|
346
|
+
// NEW: Progressive context layers
|
|
347
|
+
readonly summary?: ResultSummary | undefined;
|
|
348
|
+
readonly context?: ResultContext | undefined;
|
|
349
|
+
readonly full?: ResultFull | undefined;
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
export type DetailLevel = 'minimal' | 'contextual' | 'full';
|
|
353
|
+
|
|
354
|
+
export interface SearchQuery {
|
|
355
|
+
readonly query: string;
|
|
356
|
+
readonly stores?: readonly StoreId[] | undefined;
|
|
357
|
+
readonly mode?: SearchMode | undefined;
|
|
358
|
+
readonly threshold?: number | undefined;
|
|
359
|
+
readonly limit?: number | undefined;
|
|
360
|
+
readonly filter?: Record<string, unknown> | undefined;
|
|
361
|
+
readonly includeContent?: boolean | undefined;
|
|
362
|
+
readonly contextLines?: number | undefined;
|
|
363
|
+
|
|
364
|
+
// NEW: Detail level for progressive context
|
|
365
|
+
readonly detail?: DetailLevel | undefined;
|
|
366
|
+
}
|
|
367
|
+
```
|
|
368
|
+
|
|
369
|
+
**Step 4: Run typecheck**
|
|
370
|
+
|
|
371
|
+
```bash
|
|
372
|
+
npm run typecheck
|
|
373
|
+
```
|
|
374
|
+
|
|
375
|
+
Expected: PASS
|
|
376
|
+
|
|
377
|
+
**Step 5: Implement summary generation in SearchService**
|
|
378
|
+
|
|
379
|
+
Modify `src/services/search.service.ts`:
|
|
380
|
+
|
|
381
|
+
```typescript
|
|
382
|
+
import { CodeUnitService } from './code-unit.service.js';
|
|
383
|
+
|
|
384
|
+
export class SearchService {
|
|
385
|
+
private readonly lanceStore: LanceStore;
|
|
386
|
+
private readonly embeddingEngine: EmbeddingEngine;
|
|
387
|
+
private readonly rrfConfig: RRFConfig;
|
|
388
|
+
private readonly codeUnitService: CodeUnitService;
|
|
389
|
+
|
|
390
|
+
constructor(
|
|
391
|
+
lanceStore: LanceStore,
|
|
392
|
+
embeddingEngine: EmbeddingEngine,
|
|
393
|
+
rrfConfig: RRFConfig = { k: 20, vectorWeight: 0.6, ftsWeight: 0.4 }
|
|
394
|
+
) {
|
|
395
|
+
this.lanceStore = lanceStore;
|
|
396
|
+
this.embeddingEngine = embeddingEngine;
|
|
397
|
+
this.rrfConfig = rrfConfig;
|
|
398
|
+
this.codeUnitService = new CodeUnitService();
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
async search(query: SearchQuery): Promise<SearchResponse> {
|
|
402
|
+
const startTime = Date.now();
|
|
403
|
+
const mode = query.mode ?? 'hybrid';
|
|
404
|
+
const limit = query.limit ?? 10;
|
|
405
|
+
const stores = query.stores ?? [];
|
|
406
|
+
const detail = query.detail ?? 'minimal';
|
|
407
|
+
|
|
408
|
+
let allResults: SearchResult[] = [];
|
|
409
|
+
|
|
410
|
+
const fetchLimit = limit * 3;
|
|
411
|
+
|
|
412
|
+
if (mode === 'vector') {
|
|
413
|
+
allResults = await this.vectorSearch(query.query, stores, fetchLimit, query.threshold);
|
|
414
|
+
} else if (mode === 'fts') {
|
|
415
|
+
allResults = await this.ftsSearch(query.query, stores, fetchLimit);
|
|
416
|
+
} else {
|
|
417
|
+
allResults = await this.hybridSearch(query.query, stores, fetchLimit, query.threshold);
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
const dedupedResults = this.deduplicateBySource(allResults, query.query);
|
|
421
|
+
|
|
422
|
+
// Enhance results with progressive context
|
|
423
|
+
const enhancedResults = dedupedResults.slice(0, limit).map(r =>
|
|
424
|
+
this.addProgressiveContext(r, query.query, detail)
|
|
425
|
+
);
|
|
426
|
+
|
|
427
|
+
return {
|
|
428
|
+
query: query.query,
|
|
429
|
+
mode,
|
|
430
|
+
stores,
|
|
431
|
+
results: enhancedResults,
|
|
432
|
+
totalResults: enhancedResults.length,
|
|
433
|
+
timeMs: Date.now() - startTime,
|
|
434
|
+
};
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
private addProgressiveContext(
|
|
438
|
+
result: SearchResult,
|
|
439
|
+
query: string,
|
|
440
|
+
detail: DetailLevel
|
|
441
|
+
): SearchResult {
|
|
442
|
+
const enhanced = { ...result };
|
|
443
|
+
|
|
444
|
+
// Layer 1: Always add summary
|
|
445
|
+
const path = result.metadata.path ?? result.metadata.url ?? 'unknown';
|
|
446
|
+
const fileType = result.metadata['fileType'] as string | undefined;
|
|
447
|
+
|
|
448
|
+
// Try to extract code unit
|
|
449
|
+
const codeUnit = this.extractCodeUnitFromResult(result);
|
|
450
|
+
|
|
451
|
+
enhanced.summary = {
|
|
452
|
+
type: this.inferType(fileType, codeUnit),
|
|
453
|
+
name: codeUnit?.name ?? this.extractSymbolName(result.content),
|
|
454
|
+
signature: codeUnit?.signature ?? '',
|
|
455
|
+
purpose: this.generatePurpose(result.content, query),
|
|
456
|
+
location: `${path}${codeUnit ? ':' + codeUnit.startLine : ''}`,
|
|
457
|
+
relevanceReason: this.generateRelevanceReason(result, query)
|
|
458
|
+
};
|
|
459
|
+
|
|
460
|
+
// Layer 2: Add context if requested
|
|
461
|
+
if (detail === 'contextual' || detail === 'full') {
|
|
462
|
+
enhanced.context = {
|
|
463
|
+
interfaces: this.extractInterfaces(result.content),
|
|
464
|
+
keyImports: this.extractImports(result.content),
|
|
465
|
+
relatedConcepts: this.extractConcepts(result.content, query),
|
|
466
|
+
usage: {
|
|
467
|
+
calledBy: 0, // TODO: Implement from code graph
|
|
468
|
+
calls: 0
|
|
469
|
+
}
|
|
470
|
+
};
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
// Layer 3: Add full context if requested
|
|
474
|
+
if (detail === 'full') {
|
|
475
|
+
enhanced.full = {
|
|
476
|
+
completeCode: codeUnit?.fullContent ?? result.content,
|
|
477
|
+
relatedCode: [], // TODO: Implement from code graph
|
|
478
|
+
documentation: this.extractDocumentation(result.content),
|
|
479
|
+
tests: undefined
|
|
480
|
+
};
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
return enhanced;
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
private extractCodeUnitFromResult(result: SearchResult): CodeUnit | undefined {
|
|
487
|
+
const path = result.metadata.path;
|
|
488
|
+
if (!path) return undefined;
|
|
489
|
+
|
|
490
|
+
const ext = path.split('.').pop() ?? '';
|
|
491
|
+
const language = ext === 'ts' || ext === 'tsx' ? 'typescript' :
|
|
492
|
+
ext === 'js' || ext === 'jsx' ? 'javascript' : ext;
|
|
493
|
+
|
|
494
|
+
// Try to find a symbol name in the content
|
|
495
|
+
const symbolName = this.extractSymbolName(result.content);
|
|
496
|
+
if (!symbolName) return undefined;
|
|
497
|
+
|
|
498
|
+
return this.codeUnitService.extractCodeUnit(result.content, symbolName, language);
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
private extractSymbolName(content: string): string {
|
|
502
|
+
// Extract function or class name
|
|
503
|
+
const funcMatch = content.match(/(?:export\s+)?(?:async\s+)?function\s+(\w+)/);
|
|
504
|
+
if (funcMatch && funcMatch[1]) return funcMatch[1];
|
|
505
|
+
|
|
506
|
+
const classMatch = content.match(/(?:export\s+)?class\s+(\w+)/);
|
|
507
|
+
if (classMatch && classMatch[1]) return classMatch[1];
|
|
508
|
+
|
|
509
|
+
const constMatch = content.match(/(?:export\s+)?const\s+(\w+)/);
|
|
510
|
+
if (constMatch && constMatch[1]) return constMatch[1];
|
|
511
|
+
|
|
512
|
+
return '';
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
private inferType(fileType: string | undefined, codeUnit: CodeUnit | undefined): ResultSummary['type'] {
|
|
516
|
+
if (codeUnit) return codeUnit.type as ResultSummary['type'];
|
|
517
|
+
if (fileType === 'documentation' || fileType === 'documentation-primary') return 'documentation';
|
|
518
|
+
return 'function';
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
private generatePurpose(content: string, query: string): string {
|
|
522
|
+
// Extract first line of JSDoc comment if present
|
|
523
|
+
const docMatch = content.match(/\/\*\*\s*\n\s*\*\s*([^\n]+)/);
|
|
524
|
+
if (docMatch && docMatch[1]) return docMatch[1].trim();
|
|
525
|
+
|
|
526
|
+
// Fallback: first line that looks like a purpose
|
|
527
|
+
const lines = content.split('\n');
|
|
528
|
+
for (const line of lines) {
|
|
529
|
+
const cleaned = line.trim();
|
|
530
|
+
if (cleaned.length > 20 && cleaned.length < 100 && !cleaned.startsWith('//')) {
|
|
531
|
+
return cleaned;
|
|
532
|
+
}
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
return 'Code related to query';
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
private generateRelevanceReason(result: SearchResult, query: string): string {
|
|
539
|
+
const queryTerms = query.toLowerCase().split(/\s+/).filter(t => t.length > 2);
|
|
540
|
+
const contentLower = result.content.toLowerCase();
|
|
541
|
+
|
|
542
|
+
const matchedTerms = queryTerms.filter(term => contentLower.includes(term));
|
|
543
|
+
|
|
544
|
+
if (matchedTerms.length > 0) {
|
|
545
|
+
return `Matches: ${matchedTerms.join(', ')}`;
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
return 'Semantically similar to query';
|
|
549
|
+
}
|
|
550
|
+
|
|
551
|
+
private extractInterfaces(content: string): string[] {
|
|
552
|
+
const interfaces: string[] = [];
|
|
553
|
+
const matches = content.matchAll(/interface\s+(\w+)/g);
|
|
554
|
+
for (const match of matches) {
|
|
555
|
+
if (match[1]) interfaces.push(match[1]);
|
|
556
|
+
}
|
|
557
|
+
return interfaces;
|
|
558
|
+
}
|
|
559
|
+
|
|
560
|
+
private extractImports(content: string): string[] {
|
|
561
|
+
const imports: string[] = [];
|
|
562
|
+
const matches = content.matchAll(/import\s+.*?from\s+['"]([^'"]+)['"]/g);
|
|
563
|
+
for (const match of matches) {
|
|
564
|
+
if (match[1]) imports.push(match[1]);
|
|
565
|
+
}
|
|
566
|
+
return imports.slice(0, 5); // Top 5
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
private extractConcepts(content: string, query: string): string[] {
|
|
570
|
+
// Simple keyword extraction
|
|
571
|
+
const words = content.toLowerCase().match(/\b[a-z]{4,}\b/g) ?? [];
|
|
572
|
+
const frequency = new Map<string, number>();
|
|
573
|
+
|
|
574
|
+
for (const word of words) {
|
|
575
|
+
frequency.set(word, (frequency.get(word) ?? 0) + 1);
|
|
576
|
+
}
|
|
577
|
+
|
|
578
|
+
return Array.from(frequency.entries())
|
|
579
|
+
.sort((a, b) => b[1] - a[1])
|
|
580
|
+
.slice(0, 5)
|
|
581
|
+
.map(([word]) => word);
|
|
582
|
+
}
|
|
583
|
+
|
|
584
|
+
private extractDocumentation(content: string): string {
|
|
585
|
+
const docMatch = content.match(/\/\*\*([\s\S]*?)\*\//);
|
|
586
|
+
if (docMatch && docMatch[1]) {
|
|
587
|
+
return docMatch[1]
|
|
588
|
+
.split('\n')
|
|
589
|
+
.map(line => line.replace(/^\s*\*\s?/, '').trim())
|
|
590
|
+
.filter(line => line.length > 0)
|
|
591
|
+
.join('\n');
|
|
592
|
+
}
|
|
593
|
+
return '';
|
|
594
|
+
}
|
|
595
|
+
|
|
596
|
+
// ... rest of existing methods
|
|
597
|
+
}
|
|
598
|
+
```
|
|
599
|
+
|
|
600
|
+
**Step 6: Run typecheck**
|
|
601
|
+
|
|
602
|
+
```bash
|
|
603
|
+
npm run typecheck
|
|
604
|
+
```
|
|
605
|
+
|
|
606
|
+
Expected: PASS
|
|
607
|
+
|
|
608
|
+
**Step 7: Add --detail CLI flag**
|
|
609
|
+
|
|
610
|
+
Modify `src/cli/commands/search.ts`:
|
|
611
|
+
|
|
612
|
+
```typescript
|
|
613
|
+
export function createSearchCommand(getOptions: () => GlobalOptions): Command {
|
|
614
|
+
const search = new Command('search')
|
|
615
|
+
.description('Search indexed documents using vector similarity + full-text matching')
|
|
616
|
+
.argument('<query>', 'Search query')
|
|
617
|
+
.option('-s, --stores <stores>', 'Limit search to specific stores (comma-separated IDs or names)')
|
|
618
|
+
.option('-m, --mode <mode>', 'vector (embeddings only), fts (text only), hybrid (both, default)', 'hybrid')
|
|
619
|
+
.option('-n, --limit <count>', 'Maximum results to return (default: 10)', '10')
|
|
620
|
+
.option('-t, --threshold <score>', 'Minimum score 0-1; omit low-relevance results')
|
|
621
|
+
.option('--include-content', 'Show full document content, not just preview snippet')
|
|
622
|
+
.option('--detail <level>', 'Context detail: minimal, contextual, full (default: minimal)', 'minimal')
|
|
623
|
+
.action(async (query: string, options: {
|
|
624
|
+
stores?: string;
|
|
625
|
+
mode?: SearchMode;
|
|
626
|
+
limit?: string;
|
|
627
|
+
threshold?: string;
|
|
628
|
+
includeContent?: boolean;
|
|
629
|
+
detail?: DetailLevel;
|
|
630
|
+
}) => {
|
|
631
|
+
const globalOpts = getOptions();
|
|
632
|
+
const services = await createServices(globalOpts.config, globalOpts.dataDir);
|
|
633
|
+
|
|
634
|
+
let storeIds = (await services.store.list()).map((s) => s.id);
|
|
635
|
+
|
|
636
|
+
if (options.stores !== undefined) {
|
|
637
|
+
const requestedStores = options.stores.split(',').map((s) => s.trim());
|
|
638
|
+
const resolvedStores = [];
|
|
639
|
+
|
|
640
|
+
for (const requested of requestedStores) {
|
|
641
|
+
const store = await services.store.getByIdOrName(requested);
|
|
642
|
+
if (store !== undefined) {
|
|
643
|
+
resolvedStores.push(store.id);
|
|
644
|
+
} else {
|
|
645
|
+
console.error(`Error: Store not found: ${requested}`);
|
|
646
|
+
process.exit(3);
|
|
647
|
+
}
|
|
648
|
+
}
|
|
649
|
+
|
|
650
|
+
storeIds = resolvedStores;
|
|
651
|
+
}
|
|
652
|
+
|
|
653
|
+
if (storeIds.length === 0) {
|
|
654
|
+
console.error('No stores to search. Create a store first.');
|
|
655
|
+
process.exit(1);
|
|
656
|
+
}
|
|
657
|
+
|
|
658
|
+
for (const storeId of storeIds) {
|
|
659
|
+
await services.lance.initialize(storeId);
|
|
660
|
+
}
|
|
661
|
+
|
|
662
|
+
const results = await services.search.search({
|
|
663
|
+
query,
|
|
664
|
+
stores: storeIds,
|
|
665
|
+
mode: options.mode ?? 'hybrid',
|
|
666
|
+
limit: parseInt(options.limit ?? '10', 10),
|
|
667
|
+
threshold: options.threshold !== undefined ? parseFloat(options.threshold) : undefined,
|
|
668
|
+
includeContent: options.includeContent,
|
|
669
|
+
detail: options.detail ?? 'minimal',
|
|
670
|
+
});
|
|
671
|
+
|
|
672
|
+
if (globalOpts.format === 'json') {
|
|
673
|
+
console.log(JSON.stringify(results, null, 2));
|
|
674
|
+
} else if (globalOpts.quiet === true) {
|
|
675
|
+
for (const r of results.results) {
|
|
676
|
+
const path = r.metadata.path ?? r.metadata.url ?? 'unknown';
|
|
677
|
+
console.log(path);
|
|
678
|
+
}
|
|
679
|
+
} else {
|
|
680
|
+
console.log(`\nSearch: "${query}"`);
|
|
681
|
+
console.log(`Mode: ${results.mode} | Detail: ${options.detail} | Stores: ${results.stores.length} | Results: ${results.totalResults} | Time: ${results.timeMs}ms\n`);
|
|
682
|
+
|
|
683
|
+
if (results.results.length === 0) {
|
|
684
|
+
console.log('No results found.\n');
|
|
685
|
+
} else {
|
|
686
|
+
for (let i = 0; i < results.results.length; i++) {
|
|
687
|
+
const r = results.results[i]!;
|
|
688
|
+
|
|
689
|
+
if (r.summary) {
|
|
690
|
+
console.log(`${i + 1}. [${r.score.toFixed(2)}] ${r.summary.type}: ${r.summary.name}`);
|
|
691
|
+
console.log(` ${r.summary.location}`);
|
|
692
|
+
console.log(` ${r.summary.purpose}`);
|
|
693
|
+
|
|
694
|
+
if (r.context && options.detail !== 'minimal') {
|
|
695
|
+
console.log(` Imports: ${r.context.keyImports.slice(0, 3).join(', ')}`);
|
|
696
|
+
console.log(` Related: ${r.context.relatedConcepts.slice(0, 3).join(', ')}`);
|
|
697
|
+
}
|
|
698
|
+
|
|
699
|
+
console.log();
|
|
700
|
+
} else {
|
|
701
|
+
// Fallback to old format
|
|
702
|
+
const path = r.metadata.path ?? r.metadata.url ?? 'unknown';
|
|
703
|
+
console.log(`${i + 1}. [${r.score.toFixed(2)}] ${path}`);
|
|
704
|
+
const preview = r.highlight ?? r.content.slice(0, 150).replace(/\n/g, ' ') + (r.content.length > 150 ? '...' : '');
|
|
705
|
+
console.log(` ${preview}\n`);
|
|
706
|
+
}
|
|
707
|
+
}
|
|
708
|
+
}
|
|
709
|
+
}
|
|
710
|
+
});
|
|
711
|
+
|
|
712
|
+
return search;
|
|
713
|
+
}
|
|
714
|
+
```
|
|
715
|
+
|
|
716
|
+
**Step 8: Run typecheck**
|
|
717
|
+
|
|
718
|
+
```bash
|
|
719
|
+
npm run typecheck
|
|
720
|
+
```
|
|
721
|
+
|
|
722
|
+
Expected: PASS
|
|
723
|
+
|
|
724
|
+
**Step 9: Test manually**
|
|
725
|
+
|
|
726
|
+
```bash
|
|
727
|
+
npm run build
|
|
728
|
+
./dist/index.js search "validate token" --detail contextual
|
|
729
|
+
```
|
|
730
|
+
|
|
731
|
+
Expected: Output shows summary with context information
|
|
732
|
+
|
|
733
|
+
**Step 10: Commit**
|
|
734
|
+
|
|
735
|
+
```bash
|
|
736
|
+
git add src/types/search.ts src/services/search.service.ts src/cli/commands/search.ts tests/services/search.progressive-context.test.ts
|
|
737
|
+
git commit -m "feat(search): add progressive context layers for token optimization"
|
|
738
|
+
```
|
|
739
|
+
|
|
740
|
+
---
|
|
741
|
+
|
|
742
|
+
### Task 1.3: MCP Server Integration
|
|
743
|
+
|
|
744
|
+
**Goal:** Expose search capabilities via Model Context Protocol for AI agent integration.
|
|
745
|
+
|
|
746
|
+
**Files:**
|
|
747
|
+
- Create: `src/mcp/server.ts`
|
|
748
|
+
- Create: `src/mcp/tools/search.ts`
|
|
749
|
+
- Create: `src/mcp/tools/get-context.ts`
|
|
750
|
+
- Modify: `package.json`
|
|
751
|
+
- Create: `src/cli/commands/mcp.ts`
|
|
752
|
+
- Modify: `src/cli/program.ts`
|
|
753
|
+
- Create: `tests/mcp/server.test.ts`
|
|
754
|
+
|
|
755
|
+
**Step 1: Install MCP SDK**
|
|
756
|
+
|
|
757
|
+
```bash
|
|
758
|
+
npm install @modelcontextprotocol/sdk
|
|
759
|
+
```
|
|
760
|
+
|
|
761
|
+
**Step 2: Write failing test for MCP server**
|
|
762
|
+
|
|
763
|
+
Create `tests/mcp/server.test.ts`:
|
|
764
|
+
|
|
765
|
+
```typescript
|
|
766
|
+
import { describe, it, expect, beforeEach } from 'vitest';
|
|
767
|
+
import { createMCPServer } from '../src/mcp/server.js';
|
|
768
|
+
|
|
769
|
+
describe('MCP Server', () => {
|
|
770
|
+
it('should create server with search tool', () => {
|
|
771
|
+
const server = createMCPServer({
|
|
772
|
+
dataDir: '/tmp/test',
|
|
773
|
+
config: undefined
|
|
774
|
+
});
|
|
775
|
+
|
|
776
|
+
expect(server).toBeDefined();
|
|
777
|
+
// MCP SDK doesn't expose tools list directly, so we test via integration
|
|
778
|
+
});
|
|
779
|
+
|
|
780
|
+
it('should handle search tool call', async () => {
|
|
781
|
+
// Integration test - will implement after server is created
|
|
782
|
+
expect(true).toBe(true);
|
|
783
|
+
});
|
|
784
|
+
});
|
|
785
|
+
```
|
|
786
|
+
|
|
787
|
+
**Step 3: Run test**
|
|
788
|
+
|
|
789
|
+
```bash
|
|
790
|
+
npm run test mcp/server.test.ts
|
|
791
|
+
```
|
|
792
|
+
|
|
793
|
+
Expected: FAIL - "Cannot find module '../src/mcp/server.js'"
|
|
794
|
+
|
|
795
|
+
**Step 4: Create MCP server**
|
|
796
|
+
|
|
797
|
+
Create `src/mcp/server.ts`:
|
|
798
|
+
|
|
799
|
+
```typescript
|
|
800
|
+
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
|
|
801
|
+
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
802
|
+
import {
|
|
803
|
+
CallToolRequestSchema,
|
|
804
|
+
ListToolsRequestSchema,
|
|
805
|
+
} from '@modelcontextprotocol/sdk/types.js';
|
|
806
|
+
import { createServices } from '../services/index.js';
|
|
807
|
+
import type { SearchQuery, DetailLevel } from '../types/search.js';
|
|
808
|
+
|
|
809
|
+
interface MCPServerOptions {
|
|
810
|
+
dataDir?: string;
|
|
811
|
+
config?: string;
|
|
812
|
+
}
|
|
813
|
+
|
|
814
|
+
export function createMCPServer(options: MCPServerOptions) {
|
|
815
|
+
const server = new Server(
|
|
816
|
+
{
|
|
817
|
+
name: 'bluera-knowledge',
|
|
818
|
+
version: '1.0.0',
|
|
819
|
+
},
|
|
820
|
+
{
|
|
821
|
+
capabilities: {
|
|
822
|
+
tools: {},
|
|
823
|
+
},
|
|
824
|
+
}
|
|
825
|
+
);
|
|
826
|
+
|
|
827
|
+
// List available tools
|
|
828
|
+
server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
829
|
+
return {
|
|
830
|
+
tools: [
|
|
831
|
+
{
|
|
832
|
+
name: 'search',
|
|
833
|
+
description: 'Search all indexed knowledge stores with pattern detection and AI-optimized results. Returns structured code units with progressive context layers.',
|
|
834
|
+
inputSchema: {
|
|
835
|
+
type: 'object',
|
|
836
|
+
properties: {
|
|
837
|
+
query: {
|
|
838
|
+
type: 'string',
|
|
839
|
+
description: 'Search query (can include type signatures, constraints, or natural language)'
|
|
840
|
+
},
|
|
841
|
+
intent: {
|
|
842
|
+
type: 'string',
|
|
843
|
+
enum: ['find-pattern', 'find-implementation', 'find-usage', 'find-definition', 'find-documentation'],
|
|
844
|
+
description: 'Search intent for better ranking'
|
|
845
|
+
},
|
|
846
|
+
detail: {
|
|
847
|
+
type: 'string',
|
|
848
|
+
enum: ['minimal', 'contextual', 'full'],
|
|
849
|
+
default: 'minimal',
|
|
850
|
+
description: 'Context detail level: minimal (summary only), contextual (+ imports/types), full (+ complete code)'
|
|
851
|
+
},
|
|
852
|
+
limit: {
|
|
853
|
+
type: 'number',
|
|
854
|
+
default: 10,
|
|
855
|
+
description: 'Maximum number of results'
|
|
856
|
+
},
|
|
857
|
+
stores: {
|
|
858
|
+
type: 'array',
|
|
859
|
+
items: { type: 'string' },
|
|
860
|
+
description: 'Specific store IDs to search (optional)'
|
|
861
|
+
}
|
|
862
|
+
},
|
|
863
|
+
required: ['query']
|
|
864
|
+
}
|
|
865
|
+
},
|
|
866
|
+
{
|
|
867
|
+
name: 'get_full_context',
|
|
868
|
+
description: 'Get complete code and context for a specific search result by ID. Use this after search to get full implementation details.',
|
|
869
|
+
inputSchema: {
|
|
870
|
+
type: 'object',
|
|
871
|
+
properties: {
|
|
872
|
+
resultId: {
|
|
873
|
+
type: 'string',
|
|
874
|
+
description: 'Result ID from previous search'
|
|
875
|
+
}
|
|
876
|
+
},
|
|
877
|
+
required: ['resultId']
|
|
878
|
+
}
|
|
879
|
+
}
|
|
880
|
+
]
|
|
881
|
+
};
|
|
882
|
+
});
|
|
883
|
+
|
|
884
|
+
// Handle tool calls
|
|
885
|
+
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
886
|
+
const { name, arguments: args } = request.params;
|
|
887
|
+
|
|
888
|
+
if (name === 'search') {
|
|
889
|
+
const services = await createServices(options.config, options.dataDir);
|
|
890
|
+
|
|
891
|
+
const query = args.query as string;
|
|
892
|
+
const detail = (args.detail as DetailLevel) ?? 'minimal';
|
|
893
|
+
const limit = (args.limit as number) ?? 10;
|
|
894
|
+
const stores = args.stores as string[] | undefined;
|
|
895
|
+
|
|
896
|
+
// Get all stores if none specified
|
|
897
|
+
let storeIds = stores ?? (await services.store.list()).map(s => s.id);
|
|
898
|
+
|
|
899
|
+
// Initialize stores
|
|
900
|
+
for (const storeId of storeIds) {
|
|
901
|
+
await services.lance.initialize(storeId);
|
|
902
|
+
}
|
|
903
|
+
|
|
904
|
+
// Perform search
|
|
905
|
+
const searchQuery: SearchQuery = {
|
|
906
|
+
query,
|
|
907
|
+
stores: storeIds,
|
|
908
|
+
mode: 'hybrid',
|
|
909
|
+
limit,
|
|
910
|
+
detail
|
|
911
|
+
};
|
|
912
|
+
|
|
913
|
+
const results = await services.search.search(searchQuery);
|
|
914
|
+
|
|
915
|
+
// Calculate estimated tokens
|
|
916
|
+
const estimatedTokens = results.results.reduce((sum, r) => {
|
|
917
|
+
let tokens = 100; // Base for summary
|
|
918
|
+
if (r.context) tokens += 200;
|
|
919
|
+
if (r.full) tokens += 800;
|
|
920
|
+
return sum + tokens;
|
|
921
|
+
}, 0);
|
|
922
|
+
|
|
923
|
+
return {
|
|
924
|
+
content: [
|
|
925
|
+
{
|
|
926
|
+
type: 'text',
|
|
927
|
+
text: JSON.stringify({
|
|
928
|
+
results: results.results.map(r => ({
|
|
929
|
+
id: r.id,
|
|
930
|
+
score: r.score,
|
|
931
|
+
summary: r.summary,
|
|
932
|
+
context: r.context,
|
|
933
|
+
full: r.full
|
|
934
|
+
})),
|
|
935
|
+
totalResults: results.totalResults,
|
|
936
|
+
estimatedTokens,
|
|
937
|
+
mode: results.mode,
|
|
938
|
+
timeMs: results.timeMs
|
|
939
|
+
}, null, 2)
|
|
940
|
+
}
|
|
941
|
+
]
|
|
942
|
+
};
|
|
943
|
+
}
|
|
944
|
+
|
|
945
|
+
if (name === 'get_full_context') {
|
|
946
|
+
// TODO: Implement result caching and retrieval by ID
|
|
947
|
+
return {
|
|
948
|
+
content: [
|
|
949
|
+
{
|
|
950
|
+
type: 'text',
|
|
951
|
+
text: JSON.stringify({
|
|
952
|
+
error: 'Not yet implemented'
|
|
953
|
+
})
|
|
954
|
+
}
|
|
955
|
+
]
|
|
956
|
+
};
|
|
957
|
+
}
|
|
958
|
+
|
|
959
|
+
throw new Error(`Unknown tool: ${name}`);
|
|
960
|
+
});
|
|
961
|
+
|
|
962
|
+
return server;
|
|
963
|
+
}
|
|
964
|
+
|
|
965
|
+
export async function runMCPServer(options: MCPServerOptions) {
|
|
966
|
+
const server = createMCPServer(options);
|
|
967
|
+
const transport = new StdioServerTransport();
|
|
968
|
+
await server.connect(transport);
|
|
969
|
+
|
|
970
|
+
console.error('Bluera Knowledge MCP server running on stdio');
|
|
971
|
+
}
|
|
972
|
+
```
|
|
973
|
+
|
|
974
|
+
**Step 5: Create MCP CLI command**
|
|
975
|
+
|
|
976
|
+
Create `src/cli/commands/mcp.ts`:
|
|
977
|
+
|
|
978
|
+
```typescript
|
|
979
|
+
import { Command } from 'commander';
|
|
980
|
+
import type { GlobalOptions } from '../program.js';
|
|
981
|
+
import { runMCPServer } from '../../mcp/server.js';
|
|
982
|
+
|
|
983
|
+
export function createMCPCommand(getOptions: () => GlobalOptions): Command {
|
|
984
|
+
const mcp = new Command('mcp')
|
|
985
|
+
.description('Start MCP (Model Context Protocol) server for AI agent integration')
|
|
986
|
+
.action(async () => {
|
|
987
|
+
const opts = getOptions();
|
|
988
|
+
|
|
989
|
+
await runMCPServer({
|
|
990
|
+
dataDir: opts.dataDir,
|
|
991
|
+
config: opts.config
|
|
992
|
+
});
|
|
993
|
+
});
|
|
994
|
+
|
|
995
|
+
return mcp;
|
|
996
|
+
}
|
|
997
|
+
```
|
|
998
|
+
|
|
999
|
+
**Step 6: Register MCP command**
|
|
1000
|
+
|
|
1001
|
+
Modify `src/cli/program.ts`:
|
|
1002
|
+
|
|
1003
|
+
```typescript
|
|
1004
|
+
import { createMCPCommand } from './commands/mcp.js';
|
|
1005
|
+
|
|
1006
|
+
// In createProgram function, add:
|
|
1007
|
+
program.addCommand(createMCPCommand(getGlobalOptions));
|
|
1008
|
+
```
|
|
1009
|
+
|
|
1010
|
+
**Step 7: Run typecheck**
|
|
1011
|
+
|
|
1012
|
+
```bash
|
|
1013
|
+
npm run typecheck
|
|
1014
|
+
```
|
|
1015
|
+
|
|
1016
|
+
Expected: PASS
|
|
1017
|
+
|
|
1018
|
+
**Step 8: Build and test MCP server**
|
|
1019
|
+
|
|
1020
|
+
```bash
|
|
1021
|
+
npm run build
|
|
1022
|
+
./dist/index.js mcp
|
|
1023
|
+
```
|
|
1024
|
+
|
|
1025
|
+
In another terminal:
|
|
1026
|
+
```bash
|
|
1027
|
+
echo '{"jsonrpc":"2.0","id":1,"method":"tools/list","params":{}}' | ./dist/index.js mcp
|
|
1028
|
+
```
|
|
1029
|
+
|
|
1030
|
+
Expected: JSON response with tools list
|
|
1031
|
+
|
|
1032
|
+
**Step 9: Commit**
|
|
1033
|
+
|
|
1034
|
+
```bash
|
|
1035
|
+
git add src/mcp/ src/cli/commands/mcp.ts src/cli/program.ts package.json tests/mcp/
|
|
1036
|
+
git commit -m "feat(mcp): add MCP server for AI agent integration"
|
|
1037
|
+
```
|
|
1038
|
+
|
|
1039
|
+
---
|
|
1040
|
+
|
|
1041
|
+
## Phase 2: Static Analysis & Code Graph
|
|
1042
|
+
|
|
1043
|
+
### Task 2.1: TypeScript AST Parser
|
|
1044
|
+
|
|
1045
|
+
**Goal:** Parse TypeScript/JavaScript files to extract structured code elements (functions, classes, imports).
|
|
1046
|
+
|
|
1047
|
+
**Files:**
|
|
1048
|
+
- Create: `src/analysis/ast-parser.ts`
|
|
1049
|
+
- Create: `tests/analysis/ast-parser.test.ts`
|
|
1050
|
+
- Modify: `package.json`
|
|
1051
|
+
|
|
1052
|
+
**Step 1: Install Babel parser**
|
|
1053
|
+
|
|
1054
|
+
```bash
|
|
1055
|
+
npm install @babel/parser @babel/traverse @babel/types
|
|
1056
|
+
npm install -D @types/babel__traverse @types/babel__core
|
|
1057
|
+
```
|
|
1058
|
+
|
|
1059
|
+
**Step 2: Write failing test**
|
|
1060
|
+
|
|
1061
|
+
Create `tests/analysis/ast-parser.test.ts`:
|
|
1062
|
+
|
|
1063
|
+
```typescript
|
|
1064
|
+
import { describe, it, expect } from 'vitest';
|
|
1065
|
+
import { ASTParser } from '../src/analysis/ast-parser.js';
|
|
1066
|
+
|
|
1067
|
+
describe('ASTParser', () => {
|
|
1068
|
+
it('should extract functions from TypeScript code', () => {
|
|
1069
|
+
const code = `
|
|
1070
|
+
export async function validateToken(token: string): Promise<boolean> {
|
|
1071
|
+
return token.length > 0;
|
|
1072
|
+
}
|
|
1073
|
+
|
|
1074
|
+
function helperFunction() {
|
|
1075
|
+
return true;
|
|
1076
|
+
}
|
|
1077
|
+
`;
|
|
1078
|
+
|
|
1079
|
+
const parser = new ASTParser();
|
|
1080
|
+
const nodes = parser.parse(code, 'typescript');
|
|
1081
|
+
|
|
1082
|
+
expect(nodes).toHaveLength(2);
|
|
1083
|
+
expect(nodes[0]?.type).toBe('function');
|
|
1084
|
+
expect(nodes[0]?.name).toBe('validateToken');
|
|
1085
|
+
expect(nodes[0]?.exported).toBe(true);
|
|
1086
|
+
expect(nodes[0]?.async).toBe(true);
|
|
1087
|
+
expect(nodes[1]?.name).toBe('helperFunction');
|
|
1088
|
+
expect(nodes[1]?.exported).toBe(false);
|
|
1089
|
+
});
|
|
1090
|
+
|
|
1091
|
+
it('should extract classes with methods', () => {
|
|
1092
|
+
const code = `
|
|
1093
|
+
export class UserService {
|
|
1094
|
+
constructor(private repo: UserRepo) {}
|
|
1095
|
+
|
|
1096
|
+
async create(data: CreateUserData): Promise<User> {
|
|
1097
|
+
return this.repo.save(data);
|
|
1098
|
+
}
|
|
1099
|
+
|
|
1100
|
+
delete(id: string): void {
|
|
1101
|
+
this.repo.delete(id);
|
|
1102
|
+
}
|
|
1103
|
+
}
|
|
1104
|
+
`;
|
|
1105
|
+
|
|
1106
|
+
const parser = new ASTParser();
|
|
1107
|
+
const nodes = parser.parse(code, 'typescript');
|
|
1108
|
+
|
|
1109
|
+
const classNode = nodes.find(n => n.type === 'class');
|
|
1110
|
+
expect(classNode).toBeDefined();
|
|
1111
|
+
expect(classNode?.name).toBe('UserService');
|
|
1112
|
+
expect(classNode?.methods).toHaveLength(3); // constructor + create + delete
|
|
1113
|
+
});
|
|
1114
|
+
|
|
1115
|
+
it('should extract imports', () => {
|
|
1116
|
+
const code = `
|
|
1117
|
+
import { User } from './models/user.js';
|
|
1118
|
+
import type { Repository } from '../types.js';
|
|
1119
|
+
import express from 'express';
|
|
1120
|
+
`;
|
|
1121
|
+
|
|
1122
|
+
const parser = new ASTParser();
|
|
1123
|
+
const imports = parser.extractImports(code);
|
|
1124
|
+
|
|
1125
|
+
expect(imports).toHaveLength(3);
|
|
1126
|
+
expect(imports[0]?.source).toBe('./models/user.js');
|
|
1127
|
+
expect(imports[0]?.specifiers).toContain('User');
|
|
1128
|
+
expect(imports[2]?.specifiers).toContain('express');
|
|
1129
|
+
});
|
|
1130
|
+
});
|
|
1131
|
+
```
|
|
1132
|
+
|
|
1133
|
+
**Step 3: Run test**
|
|
1134
|
+
|
|
1135
|
+
```bash
|
|
1136
|
+
npm run test ast-parser.test.ts
|
|
1137
|
+
```
|
|
1138
|
+
|
|
1139
|
+
Expected: FAIL - "Cannot find module '../src/analysis/ast-parser.js'"
|
|
1140
|
+
|
|
1141
|
+
**Step 4: Implement AST parser**
|
|
1142
|
+
|
|
1143
|
+
Create `src/analysis/ast-parser.ts`:
|
|
1144
|
+
|
|
1145
|
+
```typescript
|
|
1146
|
+
import { parse } from '@babel/parser';
|
|
1147
|
+
import traverse from '@babel/traverse';
|
|
1148
|
+
import * as t from '@babel/types';
|
|
1149
|
+
|
|
1150
|
+
export interface CodeNode {
|
|
1151
|
+
type: 'function' | 'class' | 'interface' | 'type' | 'const';
|
|
1152
|
+
name: string;
|
|
1153
|
+
exported: boolean;
|
|
1154
|
+
async?: boolean;
|
|
1155
|
+
startLine: number;
|
|
1156
|
+
endLine: number;
|
|
1157
|
+
signature?: string;
|
|
1158
|
+
methods?: Array<{
|
|
1159
|
+
name: string;
|
|
1160
|
+
async: boolean;
|
|
1161
|
+
signature: string;
|
|
1162
|
+
}>;
|
|
1163
|
+
}
|
|
1164
|
+
|
|
1165
|
+
export interface ImportInfo {
|
|
1166
|
+
source: string;
|
|
1167
|
+
specifiers: string[];
|
|
1168
|
+
isType: boolean;
|
|
1169
|
+
}
|
|
1170
|
+
|
|
1171
|
+
export class ASTParser {
|
|
1172
|
+
parse(code: string, language: 'typescript' | 'javascript'): CodeNode[] {
|
|
1173
|
+
const plugins: any[] = ['jsx'];
|
|
1174
|
+
if (language === 'typescript') {
|
|
1175
|
+
plugins.push('typescript');
|
|
1176
|
+
}
|
|
1177
|
+
|
|
1178
|
+
const ast = parse(code, {
|
|
1179
|
+
sourceType: 'module',
|
|
1180
|
+
plugins
|
|
1181
|
+
});
|
|
1182
|
+
|
|
1183
|
+
const nodes: CodeNode[] = [];
|
|
1184
|
+
|
|
1185
|
+
traverse(ast, {
|
|
1186
|
+
FunctionDeclaration: (path) => {
|
|
1187
|
+
const node = path.node;
|
|
1188
|
+
if (!node.id) return;
|
|
1189
|
+
|
|
1190
|
+
const exported = path.parent.type === 'ExportNamedDeclaration' ||
|
|
1191
|
+
path.parent.type === 'ExportDefaultDeclaration';
|
|
1192
|
+
|
|
1193
|
+
nodes.push({
|
|
1194
|
+
type: 'function',
|
|
1195
|
+
name: node.id.name,
|
|
1196
|
+
exported,
|
|
1197
|
+
async: node.async,
|
|
1198
|
+
startLine: node.loc?.start.line ?? 0,
|
|
1199
|
+
endLine: node.loc?.end.line ?? 0,
|
|
1200
|
+
signature: this.extractFunctionSignature(node)
|
|
1201
|
+
});
|
|
1202
|
+
},
|
|
1203
|
+
|
|
1204
|
+
ClassDeclaration: (path) => {
|
|
1205
|
+
const node = path.node;
|
|
1206
|
+
if (!node.id) return;
|
|
1207
|
+
|
|
1208
|
+
const exported = path.parent.type === 'ExportNamedDeclaration' ||
|
|
1209
|
+
path.parent.type === 'ExportDefaultDeclaration';
|
|
1210
|
+
|
|
1211
|
+
const methods: CodeNode['methods'] = [];
|
|
1212
|
+
|
|
1213
|
+
for (const member of node.body.body) {
|
|
1214
|
+
if (t.isClassMethod(member) && t.isIdentifier(member.key)) {
|
|
1215
|
+
methods.push({
|
|
1216
|
+
name: member.key.name,
|
|
1217
|
+
async: member.async,
|
|
1218
|
+
signature: this.extractMethodSignature(member)
|
|
1219
|
+
});
|
|
1220
|
+
}
|
|
1221
|
+
}
|
|
1222
|
+
|
|
1223
|
+
nodes.push({
|
|
1224
|
+
type: 'class',
|
|
1225
|
+
name: node.id.name,
|
|
1226
|
+
exported,
|
|
1227
|
+
startLine: node.loc?.start.line ?? 0,
|
|
1228
|
+
endLine: node.loc?.end.line ?? 0,
|
|
1229
|
+
methods
|
|
1230
|
+
});
|
|
1231
|
+
},
|
|
1232
|
+
|
|
1233
|
+
TSInterfaceDeclaration: (path) => {
|
|
1234
|
+
const node = path.node;
|
|
1235
|
+
|
|
1236
|
+
const exported = path.parent.type === 'ExportNamedDeclaration';
|
|
1237
|
+
|
|
1238
|
+
nodes.push({
|
|
1239
|
+
type: 'interface',
|
|
1240
|
+
name: node.id.name,
|
|
1241
|
+
exported,
|
|
1242
|
+
startLine: node.loc?.start.line ?? 0,
|
|
1243
|
+
endLine: node.loc?.end.line ?? 0
|
|
1244
|
+
});
|
|
1245
|
+
}
|
|
1246
|
+
});
|
|
1247
|
+
|
|
1248
|
+
return nodes;
|
|
1249
|
+
}
|
|
1250
|
+
|
|
1251
|
+
extractImports(code: string): ImportInfo[] {
|
|
1252
|
+
const ast = parse(code, {
|
|
1253
|
+
sourceType: 'module',
|
|
1254
|
+
plugins: ['typescript', 'jsx']
|
|
1255
|
+
});
|
|
1256
|
+
|
|
1257
|
+
const imports: ImportInfo[] = [];
|
|
1258
|
+
|
|
1259
|
+
traverse(ast, {
|
|
1260
|
+
ImportDeclaration: (path) => {
|
|
1261
|
+
const node = path.node;
|
|
1262
|
+
const specifiers: string[] = [];
|
|
1263
|
+
|
|
1264
|
+
for (const spec of node.specifiers) {
|
|
1265
|
+
if (t.isImportDefaultSpecifier(spec)) {
|
|
1266
|
+
specifiers.push(spec.local.name);
|
|
1267
|
+
} else if (t.isImportSpecifier(spec)) {
|
|
1268
|
+
specifiers.push(spec.local.name);
|
|
1269
|
+
} else if (t.isImportNamespaceSpecifier(spec)) {
|
|
1270
|
+
specifiers.push(spec.local.name);
|
|
1271
|
+
}
|
|
1272
|
+
}
|
|
1273
|
+
|
|
1274
|
+
imports.push({
|
|
1275
|
+
source: node.source.value,
|
|
1276
|
+
specifiers,
|
|
1277
|
+
isType: node.importKind === 'type'
|
|
1278
|
+
});
|
|
1279
|
+
}
|
|
1280
|
+
});
|
|
1281
|
+
|
|
1282
|
+
return imports;
|
|
1283
|
+
}
|
|
1284
|
+
|
|
1285
|
+
private extractFunctionSignature(node: t.FunctionDeclaration): string {
|
|
1286
|
+
const params = node.params.map(p => {
|
|
1287
|
+
if (t.isIdentifier(p)) return p.name;
|
|
1288
|
+
return 'param';
|
|
1289
|
+
}).join(', ');
|
|
1290
|
+
|
|
1291
|
+
return `${node.id?.name}(${params})`;
|
|
1292
|
+
}
|
|
1293
|
+
|
|
1294
|
+
private extractMethodSignature(node: t.ClassMethod): string {
|
|
1295
|
+
const params = node.params.map(p => {
|
|
1296
|
+
if (t.isIdentifier(p)) return p.name;
|
|
1297
|
+
return 'param';
|
|
1298
|
+
}).join(', ');
|
|
1299
|
+
|
|
1300
|
+
const name = t.isIdentifier(node.key) ? node.key.name : 'method';
|
|
1301
|
+
return `${name}(${params})`;
|
|
1302
|
+
}
|
|
1303
|
+
}
|
|
1304
|
+
```
|
|
1305
|
+
|
|
1306
|
+
**Step 5: Run tests**
|
|
1307
|
+
|
|
1308
|
+
```bash
|
|
1309
|
+
npm run test ast-parser.test.ts
|
|
1310
|
+
```
|
|
1311
|
+
|
|
1312
|
+
Expected: PASS
|
|
1313
|
+
|
|
1314
|
+
**Step 6: Run typecheck**
|
|
1315
|
+
|
|
1316
|
+
```bash
|
|
1317
|
+
npm run typecheck
|
|
1318
|
+
```
|
|
1319
|
+
|
|
1320
|
+
Expected: PASS
|
|
1321
|
+
|
|
1322
|
+
**Step 7: Commit**
|
|
1323
|
+
|
|
1324
|
+
```bash
|
|
1325
|
+
git add src/analysis/ast-parser.ts tests/analysis/ast-parser.test.ts package.json
|
|
1326
|
+
git commit -m "feat(analysis): add TypeScript/JavaScript AST parser"
|
|
1327
|
+
```
|
|
1328
|
+
|
|
1329
|
+
---
|
|
1330
|
+
|
|
1331
|
+
### Task 2.2: Code Graph Builder
|
|
1332
|
+
|
|
1333
|
+
**Goal:** Build relationship graph (calls, imports) from parsed code.
|
|
1334
|
+
|
|
1335
|
+
**Files:**
|
|
1336
|
+
- Create: `src/analysis/code-graph.ts`
|
|
1337
|
+
- Create: `tests/analysis/code-graph.test.ts`
|
|
1338
|
+
|
|
1339
|
+
**Step 1: Write failing test**
|
|
1340
|
+
|
|
1341
|
+
Create `tests/analysis/code-graph.test.ts`:
|
|
1342
|
+
|
|
1343
|
+
```typescript
|
|
1344
|
+
import { describe, it, expect } from 'vitest';
|
|
1345
|
+
import { CodeGraph } from '../src/analysis/code-graph.js';
|
|
1346
|
+
import type { CodeNode } from '../src/analysis/ast-parser.js';
|
|
1347
|
+
|
|
1348
|
+
describe('CodeGraph', () => {
|
|
1349
|
+
it('should build graph from code nodes', () => {
|
|
1350
|
+
const nodes: CodeNode[] = [
|
|
1351
|
+
{
|
|
1352
|
+
type: 'function',
|
|
1353
|
+
name: 'validateToken',
|
|
1354
|
+
exported: true,
|
|
1355
|
+
startLine: 1,
|
|
1356
|
+
endLine: 5
|
|
1357
|
+
},
|
|
1358
|
+
{
|
|
1359
|
+
type: 'function',
|
|
1360
|
+
name: 'parseToken',
|
|
1361
|
+
exported: false,
|
|
1362
|
+
startLine: 7,
|
|
1363
|
+
endLine: 10
|
|
1364
|
+
}
|
|
1365
|
+
];
|
|
1366
|
+
|
|
1367
|
+
const graph = new CodeGraph();
|
|
1368
|
+
graph.addNodes(nodes, 'src/auth.ts');
|
|
1369
|
+
|
|
1370
|
+
expect(graph.getNode('src/auth.ts:validateToken')).toBeDefined();
|
|
1371
|
+
expect(graph.getNode('src/auth.ts:parseToken')).toBeDefined();
|
|
1372
|
+
});
|
|
1373
|
+
|
|
1374
|
+
it('should track import relationships', () => {
|
|
1375
|
+
const graph = new CodeGraph();
|
|
1376
|
+
|
|
1377
|
+
graph.addImport('src/controllers/user.ts', './services/user.js', ['UserService']);
|
|
1378
|
+
|
|
1379
|
+
const edges = graph.getEdges('src/controllers/user.ts');
|
|
1380
|
+
expect(edges).toHaveLength(1);
|
|
1381
|
+
expect(edges[0]?.type).toBe('imports');
|
|
1382
|
+
expect(edges[0]?.to).toContain('UserService');
|
|
1383
|
+
});
|
|
1384
|
+
|
|
1385
|
+
it('should detect call relationships from code', () => {
|
|
1386
|
+
const code = `
|
|
1387
|
+
export function handler(req) {
|
|
1388
|
+
const valid = validateToken(req.token);
|
|
1389
|
+
if (!valid) return error();
|
|
1390
|
+
return success();
|
|
1391
|
+
}
|
|
1392
|
+
`;
|
|
1393
|
+
|
|
1394
|
+
const graph = new CodeGraph();
|
|
1395
|
+
graph.analyzeCallRelationships(code, 'src/handler.ts', 'handler');
|
|
1396
|
+
|
|
1397
|
+
const edges = graph.getEdges('src/handler.ts:handler');
|
|
1398
|
+
const callEdges = edges.filter(e => e.type === 'calls');
|
|
1399
|
+
|
|
1400
|
+
expect(callEdges.length).toBeGreaterThan(0);
|
|
1401
|
+
expect(callEdges.some(e => e.to.includes('validateToken'))).toBe(true);
|
|
1402
|
+
});
|
|
1403
|
+
});
|
|
1404
|
+
```
|
|
1405
|
+
|
|
1406
|
+
**Step 2: Run test**
|
|
1407
|
+
|
|
1408
|
+
```bash
|
|
1409
|
+
npm run test code-graph.test.ts
|
|
1410
|
+
```
|
|
1411
|
+
|
|
1412
|
+
Expected: FAIL - "Cannot find module '../src/analysis/code-graph.js'"
|
|
1413
|
+
|
|
1414
|
+
**Step 3: Implement code graph**
|
|
1415
|
+
|
|
1416
|
+
Create `src/analysis/code-graph.ts`:
|
|
1417
|
+
|
|
1418
|
+
```typescript
|
|
1419
|
+
import type { CodeNode } from './ast-parser.js';
|
|
1420
|
+
|
|
1421
|
+
export interface GraphNode {
|
|
1422
|
+
id: string;
|
|
1423
|
+
file: string;
|
|
1424
|
+
type: 'function' | 'class' | 'interface' | 'type' | 'const';
|
|
1425
|
+
name: string;
|
|
1426
|
+
exported: boolean;
|
|
1427
|
+
startLine: number;
|
|
1428
|
+
endLine: number;
|
|
1429
|
+
signature?: string;
|
|
1430
|
+
}
|
|
1431
|
+
|
|
1432
|
+
export interface GraphEdge {
|
|
1433
|
+
from: string;
|
|
1434
|
+
to: string;
|
|
1435
|
+
type: 'calls' | 'imports' | 'extends' | 'implements';
|
|
1436
|
+
confidence: number;
|
|
1437
|
+
}
|
|
1438
|
+
|
|
1439
|
+
export class CodeGraph {
|
|
1440
|
+
private nodes = new Map<string, GraphNode>();
|
|
1441
|
+
private edges = new Map<string, GraphEdge[]>();
|
|
1442
|
+
|
|
1443
|
+
addNodes(nodes: CodeNode[], file: string): void {
|
|
1444
|
+
for (const node of nodes) {
|
|
1445
|
+
const id = `${file}:${node.name}`;
|
|
1446
|
+
|
|
1447
|
+
this.nodes.set(id, {
|
|
1448
|
+
id,
|
|
1449
|
+
file,
|
|
1450
|
+
type: node.type,
|
|
1451
|
+
name: node.name,
|
|
1452
|
+
exported: node.exported,
|
|
1453
|
+
startLine: node.startLine,
|
|
1454
|
+
endLine: node.endLine,
|
|
1455
|
+
signature: node.signature
|
|
1456
|
+
});
|
|
1457
|
+
|
|
1458
|
+
// Initialize edges array for this node
|
|
1459
|
+
if (!this.edges.has(id)) {
|
|
1460
|
+
this.edges.set(id, []);
|
|
1461
|
+
}
|
|
1462
|
+
}
|
|
1463
|
+
}
|
|
1464
|
+
|
|
1465
|
+
addImport(fromFile: string, toFile: string, specifiers: string[]): void {
|
|
1466
|
+
// Normalize the toFile path (resolve relative imports)
|
|
1467
|
+
const resolvedTo = this.resolveImportPath(fromFile, toFile);
|
|
1468
|
+
|
|
1469
|
+
for (const spec of specifiers) {
|
|
1470
|
+
const edge: GraphEdge = {
|
|
1471
|
+
from: fromFile,
|
|
1472
|
+
to: `${resolvedTo}:${spec}`,
|
|
1473
|
+
type: 'imports',
|
|
1474
|
+
confidence: 1.0
|
|
1475
|
+
};
|
|
1476
|
+
|
|
1477
|
+
const edges = this.edges.get(fromFile) ?? [];
|
|
1478
|
+
edges.push(edge);
|
|
1479
|
+
this.edges.set(fromFile, edges);
|
|
1480
|
+
}
|
|
1481
|
+
}
|
|
1482
|
+
|
|
1483
|
+
analyzeCallRelationships(code: string, file: string, functionName: string): void {
|
|
1484
|
+
const nodeId = `${file}:${functionName}`;
|
|
1485
|
+
|
|
1486
|
+
// Simple regex-based call detection (can be enhanced with AST later)
|
|
1487
|
+
const callPattern = /\b([a-zA-Z_$][a-zA-Z0-9_$]*)\s*\(/g;
|
|
1488
|
+
const calls = new Set<string>();
|
|
1489
|
+
|
|
1490
|
+
let match;
|
|
1491
|
+
while ((match = callPattern.exec(code)) !== null) {
|
|
1492
|
+
if (match[1]) {
|
|
1493
|
+
calls.add(match[1]);
|
|
1494
|
+
}
|
|
1495
|
+
}
|
|
1496
|
+
|
|
1497
|
+
const edges = this.edges.get(nodeId) ?? [];
|
|
1498
|
+
|
|
1499
|
+
for (const calledFunction of calls) {
|
|
1500
|
+
// Try to find the called function in the graph
|
|
1501
|
+
const targetNode = this.findNodeByName(calledFunction);
|
|
1502
|
+
|
|
1503
|
+
if (targetNode) {
|
|
1504
|
+
edges.push({
|
|
1505
|
+
from: nodeId,
|
|
1506
|
+
to: targetNode.id,
|
|
1507
|
+
type: 'calls',
|
|
1508
|
+
confidence: 0.8 // Lower confidence for regex-based detection
|
|
1509
|
+
});
|
|
1510
|
+
} else {
|
|
1511
|
+
// Unknown function, possibly from import
|
|
1512
|
+
edges.push({
|
|
1513
|
+
from: nodeId,
|
|
1514
|
+
to: `unknown:${calledFunction}`,
|
|
1515
|
+
type: 'calls',
|
|
1516
|
+
confidence: 0.5
|
|
1517
|
+
});
|
|
1518
|
+
}
|
|
1519
|
+
}
|
|
1520
|
+
|
|
1521
|
+
this.edges.set(nodeId, edges);
|
|
1522
|
+
}
|
|
1523
|
+
|
|
1524
|
+
getNode(id: string): GraphNode | undefined {
|
|
1525
|
+
return this.nodes.get(id);
|
|
1526
|
+
}
|
|
1527
|
+
|
|
1528
|
+
getEdges(nodeId: string): GraphEdge[] {
|
|
1529
|
+
return this.edges.get(nodeId) ?? [];
|
|
1530
|
+
}
|
|
1531
|
+
|
|
1532
|
+
getAllNodes(): GraphNode[] {
|
|
1533
|
+
return Array.from(this.nodes.values());
|
|
1534
|
+
}
|
|
1535
|
+
|
|
1536
|
+
private findNodeByName(name: string): GraphNode | undefined {
|
|
1537
|
+
for (const node of this.nodes.values()) {
|
|
1538
|
+
if (node.name === name) {
|
|
1539
|
+
return node;
|
|
1540
|
+
}
|
|
1541
|
+
}
|
|
1542
|
+
return undefined;
|
|
1543
|
+
}
|
|
1544
|
+
|
|
1545
|
+
private resolveImportPath(fromFile: string, importPath: string): string {
|
|
1546
|
+
// Simple resolution - can be enhanced
|
|
1547
|
+
if (importPath.startsWith('.')) {
|
|
1548
|
+
// Relative import
|
|
1549
|
+
const fromDir = fromFile.split('/').slice(0, -1).join('/');
|
|
1550
|
+
const parts = importPath.split('/');
|
|
1551
|
+
|
|
1552
|
+
let resolved = fromDir;
|
|
1553
|
+
for (const part of parts) {
|
|
1554
|
+
if (part === '..') {
|
|
1555
|
+
resolved = resolved.split('/').slice(0, -1).join('/');
|
|
1556
|
+
} else if (part !== '.') {
|
|
1557
|
+
resolved += '/' + part;
|
|
1558
|
+
}
|
|
1559
|
+
}
|
|
1560
|
+
|
|
1561
|
+
return resolved.replace(/\.js$/, '');
|
|
1562
|
+
}
|
|
1563
|
+
|
|
1564
|
+
// Package import
|
|
1565
|
+
return importPath;
|
|
1566
|
+
}
|
|
1567
|
+
|
|
1568
|
+
toJSON(): { nodes: GraphNode[]; edges: Array<{ from: string; to: string; type: string }> } {
|
|
1569
|
+
const allEdges: GraphEdge[] = [];
|
|
1570
|
+
for (const edges of this.edges.values()) {
|
|
1571
|
+
allEdges.push(...edges);
|
|
1572
|
+
}
|
|
1573
|
+
|
|
1574
|
+
return {
|
|
1575
|
+
nodes: Array.from(this.nodes.values()),
|
|
1576
|
+
edges: allEdges.map(e => ({ from: e.from, to: e.to, type: e.type }))
|
|
1577
|
+
};
|
|
1578
|
+
}
|
|
1579
|
+
}
|
|
1580
|
+
```
|
|
1581
|
+
|
|
1582
|
+
**Step 4: Run tests**
|
|
1583
|
+
|
|
1584
|
+
```bash
|
|
1585
|
+
npm run test code-graph.test.ts
|
|
1586
|
+
```
|
|
1587
|
+
|
|
1588
|
+
Expected: PASS
|
|
1589
|
+
|
|
1590
|
+
**Step 5: Run typecheck**
|
|
1591
|
+
|
|
1592
|
+
```bash
|
|
1593
|
+
npm run typecheck
|
|
1594
|
+
```
|
|
1595
|
+
|
|
1596
|
+
Expected: PASS
|
|
1597
|
+
|
|
1598
|
+
**Step 6: Commit**
|
|
1599
|
+
|
|
1600
|
+
```bash
|
|
1601
|
+
git add src/analysis/code-graph.ts tests/analysis/code-graph.test.ts
|
|
1602
|
+
git commit -m "feat(analysis): add code graph builder for relationship tracking"
|
|
1603
|
+
```
|
|
1604
|
+
|
|
1605
|
+
---
|
|
1606
|
+
|
|
1607
|
+
## Execution Notes
|
|
1608
|
+
|
|
1609
|
+
**Total estimated time for Phase 1 (MVP):**
|
|
1610
|
+
- Task 1.1: 2-3 days
|
|
1611
|
+
- Task 1.2: 3-4 days
|
|
1612
|
+
- Task 1.3: 3-4 days
|
|
1613
|
+
- Task 2.1: 2-3 days
|
|
1614
|
+
- Task 2.2: 2-3 days
|
|
1615
|
+
|
|
1616
|
+
**Total: ~12-17 days (2.5-3.5 weeks)**
|
|
1617
|
+
|
|
1618
|
+
**Phase 2 (Pattern Detection) and beyond:** Detailed plans available upon request after Phase 1 completion.
|
|
1619
|
+
|
|
1620
|
+
**Testing Strategy:**
|
|
1621
|
+
- Unit tests for all services (TDD approach)
|
|
1622
|
+
- Integration tests for MCP server with real Claude Code
|
|
1623
|
+
- Manual testing with test corpus (Vue.js, Express, Hono)
|
|
1624
|
+
- Agent outcome tracking once MCP is live
|
|
1625
|
+
|
|
1626
|
+
**Success Metrics for Phase 1:**
|
|
1627
|
+
- Search results include structured code units (100% of code files)
|
|
1628
|
+
- Progressive context reduces tokens by 40% on average
|
|
1629
|
+
- MCP server successfully integrates with Claude Code
|
|
1630
|
+
- Code graph captures 90%+ of function relationships
|