forge-server 0.1.0
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/hooks/worktree-create.sh +64 -0
- package/.claude/hooks/worktree-remove.sh +57 -0
- package/.claude/settings.local.json +29 -0
- package/.forge/knowledge/conventions.yaml +1 -0
- package/.forge/knowledge/decisions.yaml +1 -0
- package/.forge/knowledge/gotchas.yaml +1 -0
- package/.forge/knowledge/patterns.yaml +1 -0
- package/.forge/manifest.yaml +6 -0
- package/CLAUDE.md +144 -0
- package/bin/setup-forge.sh +132 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +553 -0
- package/dist/cli.js.map +1 -0
- package/dist/context/codebase.d.ts +57 -0
- package/dist/context/codebase.d.ts.map +1 -0
- package/dist/context/codebase.js +301 -0
- package/dist/context/codebase.js.map +1 -0
- package/dist/context/injector.d.ts +147 -0
- package/dist/context/injector.d.ts.map +1 -0
- package/dist/context/injector.js +533 -0
- package/dist/context/injector.js.map +1 -0
- package/dist/context/memory.d.ts +32 -0
- package/dist/context/memory.d.ts.map +1 -0
- package/dist/context/memory.js +140 -0
- package/dist/context/memory.js.map +1 -0
- package/dist/context/session-index.d.ts +54 -0
- package/dist/context/session-index.d.ts.map +1 -0
- package/dist/context/session-index.js +265 -0
- package/dist/context/session-index.js.map +1 -0
- package/dist/context/session.d.ts +42 -0
- package/dist/context/session.d.ts.map +1 -0
- package/dist/context/session.js +121 -0
- package/dist/context/session.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +37 -0
- package/dist/index.js.map +1 -0
- package/dist/ingestion/chunker.d.ts +19 -0
- package/dist/ingestion/chunker.d.ts.map +1 -0
- package/dist/ingestion/chunker.js +189 -0
- package/dist/ingestion/chunker.js.map +1 -0
- package/dist/ingestion/embedder.d.ts +45 -0
- package/dist/ingestion/embedder.d.ts.map +1 -0
- package/dist/ingestion/embedder.js +152 -0
- package/dist/ingestion/embedder.js.map +1 -0
- package/dist/ingestion/git-analyzer.d.ts +77 -0
- package/dist/ingestion/git-analyzer.d.ts.map +1 -0
- package/dist/ingestion/git-analyzer.js +437 -0
- package/dist/ingestion/git-analyzer.js.map +1 -0
- package/dist/ingestion/indexer.d.ts +79 -0
- package/dist/ingestion/indexer.d.ts.map +1 -0
- package/dist/ingestion/indexer.js +766 -0
- package/dist/ingestion/indexer.js.map +1 -0
- package/dist/ingestion/markdown-chunker.d.ts +19 -0
- package/dist/ingestion/markdown-chunker.d.ts.map +1 -0
- package/dist/ingestion/markdown-chunker.js +243 -0
- package/dist/ingestion/markdown-chunker.js.map +1 -0
- package/dist/ingestion/markdown-knowledge.d.ts +21 -0
- package/dist/ingestion/markdown-knowledge.d.ts.map +1 -0
- package/dist/ingestion/markdown-knowledge.js +129 -0
- package/dist/ingestion/markdown-knowledge.js.map +1 -0
- package/dist/ingestion/parser.d.ts +20 -0
- package/dist/ingestion/parser.d.ts.map +1 -0
- package/dist/ingestion/parser.js +429 -0
- package/dist/ingestion/parser.js.map +1 -0
- package/dist/ingestion/watcher.d.ts +28 -0
- package/dist/ingestion/watcher.d.ts.map +1 -0
- package/dist/ingestion/watcher.js +147 -0
- package/dist/ingestion/watcher.js.map +1 -0
- package/dist/knowledge/hydrator.d.ts +37 -0
- package/dist/knowledge/hydrator.d.ts.map +1 -0
- package/dist/knowledge/hydrator.js +220 -0
- package/dist/knowledge/hydrator.js.map +1 -0
- package/dist/knowledge/registry.d.ts +129 -0
- package/dist/knowledge/registry.d.ts.map +1 -0
- package/dist/knowledge/registry.js +361 -0
- package/dist/knowledge/registry.js.map +1 -0
- package/dist/knowledge/search.d.ts +114 -0
- package/dist/knowledge/search.d.ts.map +1 -0
- package/dist/knowledge/search.js +428 -0
- package/dist/knowledge/search.js.map +1 -0
- package/dist/knowledge/store.d.ts +76 -0
- package/dist/knowledge/store.d.ts.map +1 -0
- package/dist/knowledge/store.js +230 -0
- package/dist/knowledge/store.js.map +1 -0
- package/dist/learning/confidence.d.ts +30 -0
- package/dist/learning/confidence.d.ts.map +1 -0
- package/dist/learning/confidence.js +165 -0
- package/dist/learning/confidence.js.map +1 -0
- package/dist/learning/patterns.d.ts +52 -0
- package/dist/learning/patterns.d.ts.map +1 -0
- package/dist/learning/patterns.js +290 -0
- package/dist/learning/patterns.js.map +1 -0
- package/dist/learning/trajectory.d.ts +55 -0
- package/dist/learning/trajectory.d.ts.map +1 -0
- package/dist/learning/trajectory.js +200 -0
- package/dist/learning/trajectory.js.map +1 -0
- package/dist/memory/memory-compat.d.ts +100 -0
- package/dist/memory/memory-compat.d.ts.map +1 -0
- package/dist/memory/memory-compat.js +146 -0
- package/dist/memory/memory-compat.js.map +1 -0
- package/dist/memory/observation-store.d.ts +57 -0
- package/dist/memory/observation-store.d.ts.map +1 -0
- package/dist/memory/observation-store.js +154 -0
- package/dist/memory/observation-store.js.map +1 -0
- package/dist/memory/session-tracker.d.ts +81 -0
- package/dist/memory/session-tracker.d.ts.map +1 -0
- package/dist/memory/session-tracker.js +262 -0
- package/dist/memory/session-tracker.js.map +1 -0
- package/dist/pipeline/engine.d.ts +179 -0
- package/dist/pipeline/engine.d.ts.map +1 -0
- package/dist/pipeline/engine.js +691 -0
- package/dist/pipeline/engine.js.map +1 -0
- package/dist/pipeline/events.d.ts +54 -0
- package/dist/pipeline/events.d.ts.map +1 -0
- package/dist/pipeline/events.js +157 -0
- package/dist/pipeline/events.js.map +1 -0
- package/dist/pipeline/parallel.d.ts +83 -0
- package/dist/pipeline/parallel.d.ts.map +1 -0
- package/dist/pipeline/parallel.js +277 -0
- package/dist/pipeline/parallel.js.map +1 -0
- package/dist/pipeline/state-machine.d.ts +65 -0
- package/dist/pipeline/state-machine.d.ts.map +1 -0
- package/dist/pipeline/state-machine.js +176 -0
- package/dist/pipeline/state-machine.js.map +1 -0
- package/dist/query/graph-queries.d.ts +84 -0
- package/dist/query/graph-queries.d.ts.map +1 -0
- package/dist/query/graph-queries.js +216 -0
- package/dist/query/graph-queries.js.map +1 -0
- package/dist/query/hybrid-search.d.ts +34 -0
- package/dist/query/hybrid-search.d.ts.map +1 -0
- package/dist/query/hybrid-search.js +263 -0
- package/dist/query/hybrid-search.js.map +1 -0
- package/dist/query/intent-detector.d.ts +35 -0
- package/dist/query/intent-detector.d.ts.map +1 -0
- package/dist/query/intent-detector.js +115 -0
- package/dist/query/intent-detector.js.map +1 -0
- package/dist/query/ranking.d.ts +57 -0
- package/dist/query/ranking.d.ts.map +1 -0
- package/dist/query/ranking.js +109 -0
- package/dist/query/ranking.js.map +1 -0
- package/dist/server.d.ts +3 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +291 -0
- package/dist/server.js.map +1 -0
- package/dist/storage/falkordb-store.d.ts +73 -0
- package/dist/storage/falkordb-store.d.ts.map +1 -0
- package/dist/storage/falkordb-store.js +346 -0
- package/dist/storage/falkordb-store.js.map +1 -0
- package/dist/storage/file-cache.d.ts +32 -0
- package/dist/storage/file-cache.d.ts.map +1 -0
- package/dist/storage/file-cache.js +115 -0
- package/dist/storage/file-cache.js.map +1 -0
- package/dist/storage/interfaces.d.ts +151 -0
- package/dist/storage/interfaces.d.ts.map +1 -0
- package/dist/storage/interfaces.js +7 -0
- package/dist/storage/interfaces.js.map +1 -0
- package/dist/storage/qdrant-store.d.ts +110 -0
- package/dist/storage/qdrant-store.d.ts.map +1 -0
- package/dist/storage/qdrant-store.js +467 -0
- package/dist/storage/qdrant-store.js.map +1 -0
- package/dist/storage/schema.d.ts +4 -0
- package/dist/storage/schema.d.ts.map +1 -0
- package/dist/storage/schema.js +136 -0
- package/dist/storage/schema.js.map +1 -0
- package/dist/storage/sqlite.d.ts +35 -0
- package/dist/storage/sqlite.d.ts.map +1 -0
- package/dist/storage/sqlite.js +132 -0
- package/dist/storage/sqlite.js.map +1 -0
- package/dist/tools/collaboration-tools.d.ts +111 -0
- package/dist/tools/collaboration-tools.d.ts.map +1 -0
- package/dist/tools/collaboration-tools.js +174 -0
- package/dist/tools/collaboration-tools.js.map +1 -0
- package/dist/tools/context-tools.d.ts +293 -0
- package/dist/tools/context-tools.d.ts.map +1 -0
- package/dist/tools/context-tools.js +437 -0
- package/dist/tools/context-tools.js.map +1 -0
- package/dist/tools/graph-tools.d.ts +129 -0
- package/dist/tools/graph-tools.d.ts.map +1 -0
- package/dist/tools/graph-tools.js +237 -0
- package/dist/tools/graph-tools.js.map +1 -0
- package/dist/tools/ingestion-tools.d.ts +96 -0
- package/dist/tools/ingestion-tools.d.ts.map +1 -0
- package/dist/tools/ingestion-tools.js +90 -0
- package/dist/tools/ingestion-tools.js.map +1 -0
- package/dist/tools/learning-tools.d.ts +168 -0
- package/dist/tools/learning-tools.d.ts.map +1 -0
- package/dist/tools/learning-tools.js +158 -0
- package/dist/tools/learning-tools.js.map +1 -0
- package/dist/tools/memory-tools.d.ts +183 -0
- package/dist/tools/memory-tools.d.ts.map +1 -0
- package/dist/tools/memory-tools.js +197 -0
- package/dist/tools/memory-tools.js.map +1 -0
- package/dist/tools/phase-tools.d.ts +954 -0
- package/dist/tools/phase-tools.d.ts.map +1 -0
- package/dist/tools/phase-tools.js +1215 -0
- package/dist/tools/phase-tools.js.map +1 -0
- package/dist/tools/pipeline-tools.d.ts +140 -0
- package/dist/tools/pipeline-tools.d.ts.map +1 -0
- package/dist/tools/pipeline-tools.js +162 -0
- package/dist/tools/pipeline-tools.js.map +1 -0
- package/dist/tools/registration-tools.d.ts +220 -0
- package/dist/tools/registration-tools.d.ts.map +1 -0
- package/dist/tools/registration-tools.js +391 -0
- package/dist/tools/registration-tools.js.map +1 -0
- package/dist/util/circuit-breaker.d.ts +75 -0
- package/dist/util/circuit-breaker.d.ts.map +1 -0
- package/dist/util/circuit-breaker.js +159 -0
- package/dist/util/circuit-breaker.js.map +1 -0
- package/dist/util/config.d.ts +23 -0
- package/dist/util/config.d.ts.map +1 -0
- package/dist/util/config.js +164 -0
- package/dist/util/config.js.map +1 -0
- package/dist/util/logger.d.ts +13 -0
- package/dist/util/logger.d.ts.map +1 -0
- package/dist/util/logger.js +45 -0
- package/dist/util/logger.js.map +1 -0
- package/dist/util/token-counter.d.ts +24 -0
- package/dist/util/token-counter.d.ts.map +1 -0
- package/dist/util/token-counter.js +48 -0
- package/dist/util/token-counter.js.map +1 -0
- package/dist/util/types.d.ts +525 -0
- package/dist/util/types.d.ts.map +1 -0
- package/dist/util/types.js +5 -0
- package/dist/util/types.js.map +1 -0
- package/docker-compose.yml +20 -0
- package/docs/plans/2026-02-27-swarm-coordination/architecture.md +203 -0
- package/docs/plans/2026-02-27-swarm-coordination/vision.md +57 -0
- package/docs/plans/completed/2026-02-26-forge-plugin-bundling/architecture.md +1 -0
- package/docs/plans/completed/2026-02-26-forge-plugin-bundling/vision.md +300 -0
- package/docs/plans/completed/2026-02-27-forge-swarm-learning/architecture.md +480 -0
- package/docs/plans/completed/2026-02-27-forge-swarm-learning/verification-checklist.md +462 -0
- package/docs/plans/completed/2026-02-27-git-history-atlassian/git-jira-plan.md +181 -0
- package/package.json +39 -0
- package/plugin/.claude-plugin/plugin.json +8 -0
- package/plugin/.mcp.json +15 -0
- package/plugin/README.md +134 -0
- package/plugin/agents/architect.md +367 -0
- package/plugin/agents/backend-specialist.md +263 -0
- package/plugin/agents/brainstormer.md +122 -0
- package/plugin/agents/data-specialist.md +266 -0
- package/plugin/agents/designer.md +408 -0
- package/plugin/agents/frontend-specialist.md +241 -0
- package/plugin/agents/inspector.md +406 -0
- package/plugin/agents/knowledge-keeper.md +443 -0
- package/plugin/agents/platform-engineer.md +326 -0
- package/plugin/agents/product-manager.md +268 -0
- package/plugin/agents/product-owner.md +438 -0
- package/plugin/agents/pulse-checker.md +73 -0
- package/plugin/agents/qa-strategist.md +500 -0
- package/plugin/agents/self-improver.md +310 -0
- package/plugin/agents/strategist.md +360 -0
- package/plugin/agents/supervisor.md +380 -0
- package/plugin/commands/brainstorm.md +25 -0
- package/plugin/commands/forge.md +88 -0
- package/plugin/docs/atlassian-integration.md +110 -0
- package/plugin/docs/workflow.md +126 -0
- package/plugin/skills/agent-development/.skillfish.json +10 -0
- package/plugin/skills/agent-development/SKILL.md +415 -0
- package/plugin/skills/agent-development/examples/agent-creation-prompt.md +238 -0
- package/plugin/skills/agent-development/examples/complete-agent-examples.md +427 -0
- package/plugin/skills/agent-development/references/agent-creation-system-prompt.md +207 -0
- package/plugin/skills/agent-development/references/system-prompt-design.md +411 -0
- package/plugin/skills/agent-development/references/triggering-examples.md +491 -0
- package/plugin/skills/agent-development/scripts/validate-agent.sh +217 -0
- package/plugin/skills/agent-handoff/SKILL.md +335 -0
- package/plugin/skills/anti-stub/SKILL.md +317 -0
- package/plugin/skills/brainstorm/SKILL.md +31 -0
- package/plugin/skills/debugging/SKILL.md +276 -0
- package/plugin/skills/fix/SKILL.md +62 -0
- package/plugin/skills/frontend-design/.skillfish.json +10 -0
- package/plugin/skills/frontend-design/SKILL.md +42 -0
- package/plugin/skills/gotchas/SKILL.md +61 -0
- package/plugin/skills/graph-orchestrator/SKILL.md +38 -0
- package/plugin/skills/history/SKILL.md +58 -0
- package/plugin/skills/impact/SKILL.md +59 -0
- package/plugin/skills/implementation-execution/SKILL.md +291 -0
- package/plugin/skills/index-repo/SKILL.md +55 -0
- package/plugin/skills/interviewing/SKILL.md +225 -0
- package/plugin/skills/knowledge-curation/SKILL.md +393 -0
- package/plugin/skills/learn/SKILL.md +69 -0
- package/plugin/skills/mcp-integration/.skillfish.json +10 -0
- package/plugin/skills/mcp-integration/SKILL.md +554 -0
- package/plugin/skills/mcp-integration/examples/http-server.json +20 -0
- package/plugin/skills/mcp-integration/examples/sse-server.json +19 -0
- package/plugin/skills/mcp-integration/examples/stdio-server.json +26 -0
- package/plugin/skills/mcp-integration/references/authentication.md +549 -0
- package/plugin/skills/mcp-integration/references/server-types.md +536 -0
- package/plugin/skills/mcp-integration/references/tool-usage.md +538 -0
- package/plugin/skills/nestjs/.skillfish.json +10 -0
- package/plugin/skills/nestjs/SKILL.md +669 -0
- package/plugin/skills/nestjs/drizzle-reference.md +1894 -0
- package/plugin/skills/nestjs/reference.md +1447 -0
- package/plugin/skills/nestjs/workflow-optimization.md +229 -0
- package/plugin/skills/parallel-dispatch/SKILL.md +308 -0
- package/plugin/skills/project-discovery/SKILL.md +304 -0
- package/plugin/skills/search/SKILL.md +56 -0
- package/plugin/skills/security-audit/SKILL.md +362 -0
- package/plugin/skills/skill-development/.skillfish.json +10 -0
- package/plugin/skills/skill-development/SKILL.md +637 -0
- package/plugin/skills/skill-development/references/skill-creator-original.md +209 -0
- package/plugin/skills/tdd/SKILL.md +273 -0
- package/plugin/skills/terminal-presentation/SKILL.md +395 -0
- package/plugin/skills/test-strategy/SKILL.md +365 -0
- package/plugin/skills/verification-protocol/SKILL.md +256 -0
- package/plugin/skills/visual-explainer/CHANGELOG.md +97 -0
- package/plugin/skills/visual-explainer/LICENSE +21 -0
- package/plugin/skills/visual-explainer/README.md +137 -0
- package/plugin/skills/visual-explainer/SKILL.md +352 -0
- package/plugin/skills/visual-explainer/banner.png +0 -0
- package/plugin/skills/visual-explainer/package.json +11 -0
- package/plugin/skills/visual-explainer/prompts/diff-review.md +68 -0
- package/plugin/skills/visual-explainer/prompts/fact-check.md +63 -0
- package/plugin/skills/visual-explainer/prompts/generate-slides.md +18 -0
- package/plugin/skills/visual-explainer/prompts/generate-web-diagram.md +10 -0
- package/plugin/skills/visual-explainer/prompts/plan-review.md +86 -0
- package/plugin/skills/visual-explainer/prompts/project-recap.md +61 -0
- package/plugin/skills/visual-explainer/references/css-patterns.md +1188 -0
- package/plugin/skills/visual-explainer/references/libraries.md +470 -0
- package/plugin/skills/visual-explainer/references/responsive-nav.md +212 -0
- package/plugin/skills/visual-explainer/references/slide-patterns.md +1403 -0
- package/plugin/skills/visual-explainer/templates/architecture.html +596 -0
- package/plugin/skills/visual-explainer/templates/data-table.html +540 -0
- package/plugin/skills/visual-explainer/templates/mermaid-flowchart.html +435 -0
- package/plugin/skills/visual-explainer/templates/slide-deck.html +913 -0
- package/src/cli.ts +655 -0
- package/src/context/.gitkeep +0 -0
- package/src/context/codebase.ts +393 -0
- package/src/context/injector.ts +797 -0
- package/src/context/memory.ts +187 -0
- package/src/context/session-index.ts +327 -0
- package/src/context/session.ts +152 -0
- package/src/index.ts +47 -0
- package/src/ingestion/.gitkeep +0 -0
- package/src/ingestion/chunker.ts +277 -0
- package/src/ingestion/embedder.ts +167 -0
- package/src/ingestion/git-analyzer.ts +545 -0
- package/src/ingestion/indexer.ts +984 -0
- package/src/ingestion/markdown-chunker.ts +337 -0
- package/src/ingestion/markdown-knowledge.ts +175 -0
- package/src/ingestion/parser.ts +475 -0
- package/src/ingestion/watcher.ts +182 -0
- package/src/knowledge/.gitkeep +0 -0
- package/src/knowledge/hydrator.ts +246 -0
- package/src/knowledge/registry.ts +463 -0
- package/src/knowledge/search.ts +565 -0
- package/src/knowledge/store.ts +262 -0
- package/src/learning/.gitkeep +0 -0
- package/src/learning/confidence.ts +193 -0
- package/src/learning/patterns.ts +360 -0
- package/src/learning/trajectory.ts +268 -0
- package/src/memory/.gitkeep +0 -0
- package/src/memory/memory-compat.ts +233 -0
- package/src/memory/observation-store.ts +224 -0
- package/src/memory/session-tracker.ts +332 -0
- package/src/pipeline/.gitkeep +0 -0
- package/src/pipeline/engine.ts +1139 -0
- package/src/pipeline/events.ts +253 -0
- package/src/pipeline/parallel.ts +394 -0
- package/src/pipeline/state-machine.ts +199 -0
- package/src/query/.gitkeep +0 -0
- package/src/query/graph-queries.ts +262 -0
- package/src/query/hybrid-search.ts +337 -0
- package/src/query/intent-detector.ts +131 -0
- package/src/query/ranking.ts +161 -0
- package/src/server.ts +352 -0
- package/src/storage/.gitkeep +0 -0
- package/src/storage/falkordb-store.ts +388 -0
- package/src/storage/file-cache.ts +141 -0
- package/src/storage/interfaces.ts +201 -0
- package/src/storage/qdrant-store.ts +557 -0
- package/src/storage/schema.ts +139 -0
- package/src/storage/sqlite.ts +168 -0
- package/src/tools/.gitkeep +0 -0
- package/src/tools/collaboration-tools.ts +208 -0
- package/src/tools/context-tools.ts +493 -0
- package/src/tools/graph-tools.ts +295 -0
- package/src/tools/ingestion-tools.ts +122 -0
- package/src/tools/learning-tools.ts +181 -0
- package/src/tools/memory-tools.ts +234 -0
- package/src/tools/phase-tools.ts +1452 -0
- package/src/tools/pipeline-tools.ts +188 -0
- package/src/tools/registration-tools.ts +450 -0
- package/src/util/.gitkeep +0 -0
- package/src/util/circuit-breaker.ts +193 -0
- package/src/util/config.ts +177 -0
- package/src/util/logger.ts +53 -0
- package/src/util/token-counter.ts +52 -0
- package/src/util/types.ts +710 -0
- package/tests/context/.gitkeep +0 -0
- package/tests/integration/.gitkeep +0 -0
- package/tests/knowledge/.gitkeep +0 -0
- package/tests/learning/.gitkeep +0 -0
- package/tests/pipeline/.gitkeep +0 -0
- package/tests/tools/.gitkeep +0 -0
- package/tsconfig.json +21 -0
- package/vitest.config.ts +10 -0
- package/vscode-extension/.vscodeignore +7 -0
- package/vscode-extension/README.md +43 -0
- package/vscode-extension/out/edge-collector.js +274 -0
- package/vscode-extension/out/edge-collector.js.map +1 -0
- package/vscode-extension/out/extension.js +264 -0
- package/vscode-extension/out/extension.js.map +1 -0
- package/vscode-extension/out/forge-client.js +318 -0
- package/vscode-extension/out/forge-client.js.map +1 -0
- package/vscode-extension/package-lock.json +59 -0
- package/vscode-extension/package.json +71 -0
- package/vscode-extension/src/edge-collector.ts +320 -0
- package/vscode-extension/src/extension.ts +269 -0
- package/vscode-extension/src/forge-client.ts +364 -0
- package/vscode-extension/tsconfig.json +19 -0
|
@@ -0,0 +1,393 @@
|
|
|
1
|
+
// CodebaseContext — B7
|
|
2
|
+
//
|
|
3
|
+
// Wraps the local hybrid-search + file-cache modules so that the ContextInjector
|
|
4
|
+
// can retrieve codebase context without coupling to the query internals directly.
|
|
5
|
+
//
|
|
6
|
+
// When either graph or vector store is unavailable the class degrades gracefully:
|
|
7
|
+
// - If vectorStore is null/unhealthy: returns empty pivot files and a warning.
|
|
8
|
+
// - If graphStore is null/unhealthy: falls back to vector-only search.
|
|
9
|
+
// - If fileCache is null: returns file paths without content.
|
|
10
|
+
|
|
11
|
+
import type { VectorStore, GraphStore, FileContentCache } from '../storage/interfaces.js';
|
|
12
|
+
import type { RepoSharing } from '../util/types.js';
|
|
13
|
+
import { hybridSearch } from '../query/hybrid-search.js';
|
|
14
|
+
import { estimateTokens } from '../util/token-counter.js';
|
|
15
|
+
import { logger } from '../util/logger.js';
|
|
16
|
+
import { readFileSync, existsSync } from 'node:fs';
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Resolves the sharing mode for a given repo ID.
|
|
20
|
+
* Returns undefined if the repo is not found.
|
|
21
|
+
*/
|
|
22
|
+
export type SharingResolver = (repoId: string) => RepoSharing | undefined;
|
|
23
|
+
|
|
24
|
+
// ---------------------------------------------------------------------------
|
|
25
|
+
// Public types
|
|
26
|
+
// ---------------------------------------------------------------------------
|
|
27
|
+
|
|
28
|
+
export interface CodebaseContextOptions {
|
|
29
|
+
repoId?: string;
|
|
30
|
+
detail?: 'full' | 'skeleton' | 'paths';
|
|
31
|
+
maxResults?: number;
|
|
32
|
+
includeTests?: boolean;
|
|
33
|
+
includeCrossRepo?: boolean;
|
|
34
|
+
maxTokens?: number;
|
|
35
|
+
vectorWeight?: number; // from intent strategy, default 0.6
|
|
36
|
+
graphWeight?: number; // from intent strategy, default 0.3
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export interface PivotFile {
|
|
40
|
+
filePath: string;
|
|
41
|
+
content: string;
|
|
42
|
+
relevanceScore: number;
|
|
43
|
+
matchReason: string;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export interface SupportingSkeleton {
|
|
47
|
+
filePath: string;
|
|
48
|
+
skeleton: string;
|
|
49
|
+
relevanceScore: number;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export interface CodebaseContextResult {
|
|
53
|
+
pivotFiles: PivotFile[];
|
|
54
|
+
supportingSkeletons: SupportingSkeleton[];
|
|
55
|
+
warnings: string[];
|
|
56
|
+
tokenCount: number;
|
|
57
|
+
executionTimeMs: number;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// ---------------------------------------------------------------------------
|
|
61
|
+
// CodebaseContext
|
|
62
|
+
// ---------------------------------------------------------------------------
|
|
63
|
+
|
|
64
|
+
export class CodebaseContext {
|
|
65
|
+
constructor(
|
|
66
|
+
private readonly graphStore: GraphStore | null,
|
|
67
|
+
private readonly vectorStore: VectorStore | null,
|
|
68
|
+
private readonly fileCache: FileContentCache | null,
|
|
69
|
+
private readonly sharingResolver?: SharingResolver,
|
|
70
|
+
) {}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Retrieve codebase context for a natural-language query.
|
|
74
|
+
* Uses hybrid search (vector + graph) when both stores are available.
|
|
75
|
+
* Falls back to vector-only when graph is down.
|
|
76
|
+
* Returns empty results with warnings when vector store is unavailable.
|
|
77
|
+
*/
|
|
78
|
+
async getContext(
|
|
79
|
+
query: string,
|
|
80
|
+
options: CodebaseContextOptions = {},
|
|
81
|
+
): Promise<CodebaseContextResult> {
|
|
82
|
+
const startTime = Date.now();
|
|
83
|
+
const warnings: string[] = [];
|
|
84
|
+
const {
|
|
85
|
+
repoId,
|
|
86
|
+
detail = 'full',
|
|
87
|
+
maxResults = 10,
|
|
88
|
+
includeTests = false,
|
|
89
|
+
includeCrossRepo = true,
|
|
90
|
+
maxTokens = 8000,
|
|
91
|
+
vectorWeight,
|
|
92
|
+
graphWeight,
|
|
93
|
+
} = options;
|
|
94
|
+
|
|
95
|
+
// Guard: no vector store means no code search at all
|
|
96
|
+
if (!this.vectorStore) {
|
|
97
|
+
return {
|
|
98
|
+
pivotFiles: [],
|
|
99
|
+
supportingSkeletons: [],
|
|
100
|
+
warnings: ['Vector store not configured — codebase context unavailable'],
|
|
101
|
+
tokenCount: 0,
|
|
102
|
+
executionTimeMs: Date.now() - startTime,
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
const vectorHealthy = await this.vectorStore.isHealthy();
|
|
107
|
+
if (!vectorHealthy) {
|
|
108
|
+
return {
|
|
109
|
+
pivotFiles: [],
|
|
110
|
+
supportingSkeletons: [],
|
|
111
|
+
warnings: ['Vector store (Qdrant) is unavailable — codebase context unavailable'],
|
|
112
|
+
tokenCount: 0,
|
|
113
|
+
executionTimeMs: Date.now() - startTime,
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
const graphHealthy = this.graphStore
|
|
118
|
+
? await this.graphStore.isHealthy()
|
|
119
|
+
: false;
|
|
120
|
+
if (!graphHealthy) {
|
|
121
|
+
warnings.push('Graph store (FalkorDB) is unavailable — using vector-only search');
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// Use a no-op graph store when graph is unhealthy to satisfy hybridSearch signature
|
|
125
|
+
const effectiveGraph = graphHealthy && this.graphStore
|
|
126
|
+
? this.graphStore
|
|
127
|
+
: createNullGraphStore();
|
|
128
|
+
|
|
129
|
+
let searchResults;
|
|
130
|
+
try {
|
|
131
|
+
searchResults = await hybridSearch(
|
|
132
|
+
this.vectorStore,
|
|
133
|
+
effectiveGraph,
|
|
134
|
+
{
|
|
135
|
+
query,
|
|
136
|
+
repoId,
|
|
137
|
+
limit: maxResults * 2, // fetch extra to fill budget after skeletons split
|
|
138
|
+
includeTests,
|
|
139
|
+
vectorWeight,
|
|
140
|
+
graphWeight,
|
|
141
|
+
},
|
|
142
|
+
);
|
|
143
|
+
} catch (err) {
|
|
144
|
+
logger.warn('CodebaseContext: hybrid search failed', { error: String(err) });
|
|
145
|
+
return {
|
|
146
|
+
pivotFiles: [],
|
|
147
|
+
supportingSkeletons: [],
|
|
148
|
+
warnings: [`Search failed: ${String(err)}`],
|
|
149
|
+
tokenCount: 0,
|
|
150
|
+
executionTimeMs: Date.now() - startTime,
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// Sharing enforcement: filter out results from private repos that aren't
|
|
155
|
+
// the caller's own repo. Only applies when includeCrossRepo is true and
|
|
156
|
+
// a sharingResolver is available.
|
|
157
|
+
if (includeCrossRepo && this.sharingResolver && repoId) {
|
|
158
|
+
const sharingCache = new Map<string, RepoSharing | undefined>();
|
|
159
|
+
searchResults.results = searchResults.results.filter(r => {
|
|
160
|
+
// Own repo results always pass
|
|
161
|
+
if (r.repoId === repoId) return true;
|
|
162
|
+
|
|
163
|
+
// Look up sharing for the result's repo
|
|
164
|
+
if (!sharingCache.has(r.repoId)) {
|
|
165
|
+
sharingCache.set(r.repoId, this.sharingResolver!(r.repoId));
|
|
166
|
+
}
|
|
167
|
+
const sharing = sharingCache.get(r.repoId);
|
|
168
|
+
|
|
169
|
+
// Only include results from team/public repos
|
|
170
|
+
return sharing === 'team' || sharing === 'public';
|
|
171
|
+
});
|
|
172
|
+
} else if (includeCrossRepo && this.sharingResolver && !repoId) {
|
|
173
|
+
// No caller repo context — only allow team/public repos
|
|
174
|
+
const sharingCache = new Map<string, RepoSharing | undefined>();
|
|
175
|
+
searchResults.results = searchResults.results.filter(r => {
|
|
176
|
+
if (!sharingCache.has(r.repoId)) {
|
|
177
|
+
sharingCache.set(r.repoId, this.sharingResolver!(r.repoId));
|
|
178
|
+
}
|
|
179
|
+
const sharing = sharingCache.get(r.repoId);
|
|
180
|
+
return sharing === 'team' || sharing === 'public';
|
|
181
|
+
});
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
const pivotFiles: PivotFile[] = [];
|
|
185
|
+
const supportingSkeletons: SupportingSkeleton[] = [];
|
|
186
|
+
let usedTokens = 0;
|
|
187
|
+
const tokenBudget = maxTokens;
|
|
188
|
+
|
|
189
|
+
for (const result of searchResults.results.slice(0, maxResults)) {
|
|
190
|
+
const { filePath, finalScore, matchReason } = result;
|
|
191
|
+
|
|
192
|
+
if (detail === 'paths') {
|
|
193
|
+
// No content — just return file paths as minimal pivot entries
|
|
194
|
+
pivotFiles.push({
|
|
195
|
+
filePath,
|
|
196
|
+
content: '',
|
|
197
|
+
relevanceScore: Math.round(finalScore * 1000) / 1000,
|
|
198
|
+
matchReason,
|
|
199
|
+
});
|
|
200
|
+
continue;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
// Try to read file content
|
|
204
|
+
const content = this.readFile(filePath, repoId);
|
|
205
|
+
if (content === null) {
|
|
206
|
+
// File not readable — add as skeleton with just the path
|
|
207
|
+
supportingSkeletons.push({
|
|
208
|
+
filePath,
|
|
209
|
+
skeleton: `// File not readable: ${filePath}`,
|
|
210
|
+
relevanceScore: Math.round(finalScore * 1000) / 1000,
|
|
211
|
+
});
|
|
212
|
+
continue;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
const contentTokens = estimateTokens(content);
|
|
216
|
+
|
|
217
|
+
if (detail === 'full' && usedTokens + contentTokens <= tokenBudget) {
|
|
218
|
+
pivotFiles.push({
|
|
219
|
+
filePath,
|
|
220
|
+
content,
|
|
221
|
+
relevanceScore: Math.round(finalScore * 1000) / 1000,
|
|
222
|
+
matchReason,
|
|
223
|
+
});
|
|
224
|
+
usedTokens += contentTokens;
|
|
225
|
+
} else if (detail === 'skeleton' || usedTokens + contentTokens > tokenBudget) {
|
|
226
|
+
// Build a structural skeleton (exports and top-level declarations only)
|
|
227
|
+
const skeleton = buildSkeleton(content, filePath);
|
|
228
|
+
const skeletonTokens = estimateTokens(skeleton);
|
|
229
|
+
|
|
230
|
+
if (usedTokens + skeletonTokens <= tokenBudget) {
|
|
231
|
+
supportingSkeletons.push({
|
|
232
|
+
filePath,
|
|
233
|
+
skeleton,
|
|
234
|
+
relevanceScore: Math.round(finalScore * 1000) / 1000,
|
|
235
|
+
});
|
|
236
|
+
usedTokens += skeletonTokens;
|
|
237
|
+
}
|
|
238
|
+
// If even the skeleton is too big, omit this file entirely
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
const tokenCount = pivotFiles.reduce(
|
|
243
|
+
(sum, f) => sum + estimateTokens(f.content),
|
|
244
|
+
0,
|
|
245
|
+
) + supportingSkeletons.reduce(
|
|
246
|
+
(sum, s) => sum + estimateTokens(s.skeleton),
|
|
247
|
+
0,
|
|
248
|
+
);
|
|
249
|
+
|
|
250
|
+
logger.debug('CodebaseContext: context assembled', {
|
|
251
|
+
query: query.slice(0, 50),
|
|
252
|
+
pivots: pivotFiles.length,
|
|
253
|
+
skeletons: supportingSkeletons.length,
|
|
254
|
+
tokenCount,
|
|
255
|
+
durationMs: Date.now() - startTime,
|
|
256
|
+
});
|
|
257
|
+
|
|
258
|
+
return {
|
|
259
|
+
pivotFiles,
|
|
260
|
+
supportingSkeletons,
|
|
261
|
+
warnings,
|
|
262
|
+
tokenCount,
|
|
263
|
+
executionTimeMs: Date.now() - startTime,
|
|
264
|
+
};
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
/** True when at least the vector store is configured and reachable. */
|
|
268
|
+
isAvailable(): boolean {
|
|
269
|
+
// Synchronous best-effort check — callers that need precision should await getContext()
|
|
270
|
+
return this.vectorStore !== null;
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
// ---------------------------------------------------------------------------
|
|
274
|
+
// Private helpers
|
|
275
|
+
// ---------------------------------------------------------------------------
|
|
276
|
+
|
|
277
|
+
/**
|
|
278
|
+
* Read file content. Checks cache first; falls back to disk read.
|
|
279
|
+
* Returns null if the file cannot be read.
|
|
280
|
+
*/
|
|
281
|
+
private readFile(filePath: string, repoId?: string): string | null {
|
|
282
|
+
// Try cache first
|
|
283
|
+
if (this.fileCache && repoId) {
|
|
284
|
+
const cached = this.fileCache.get(repoId, filePath);
|
|
285
|
+
if (cached) return cached.content;
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
// Disk read
|
|
289
|
+
try {
|
|
290
|
+
if (!existsSync(filePath)) return null;
|
|
291
|
+
const content = readFileSync(filePath, 'utf8');
|
|
292
|
+
|
|
293
|
+
// Populate cache for future hits
|
|
294
|
+
if (this.fileCache && repoId) {
|
|
295
|
+
this.fileCache.set(repoId, filePath, content, '');
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
return content;
|
|
299
|
+
} catch {
|
|
300
|
+
return null;
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
// ---------------------------------------------------------------------------
|
|
306
|
+
// Skeleton builder
|
|
307
|
+
// ---------------------------------------------------------------------------
|
|
308
|
+
|
|
309
|
+
/**
|
|
310
|
+
* Build a token-efficient skeleton of a TypeScript/JavaScript file.
|
|
311
|
+
* Extracts export declarations, class names, function signatures, and
|
|
312
|
+
* interface/type definitions without the full bodies.
|
|
313
|
+
*/
|
|
314
|
+
function buildSkeleton(content: string, filePath: string): string {
|
|
315
|
+
const lines = content.split('\n');
|
|
316
|
+
const skeleton: string[] = [`// Skeleton: ${filePath}`];
|
|
317
|
+
|
|
318
|
+
const KEEP_PATTERNS = [
|
|
319
|
+
/^export\s+(default\s+)?class\s+/,
|
|
320
|
+
/^export\s+(default\s+)?function\s+/,
|
|
321
|
+
/^export\s+(default\s+)?async\s+function\s+/,
|
|
322
|
+
/^export\s+(default\s+)?interface\s+/,
|
|
323
|
+
/^export\s+(default\s+)?type\s+/,
|
|
324
|
+
/^export\s+(const|let|var)\s+/,
|
|
325
|
+
/^export\s+enum\s+/,
|
|
326
|
+
/^import\s+/,
|
|
327
|
+
/^\/\*\*/, // JSDoc block start
|
|
328
|
+
/^\s+\/\*\*/, // indented JSDoc
|
|
329
|
+
/^class\s+/,
|
|
330
|
+
/^function\s+/,
|
|
331
|
+
/^async\s+function\s+/,
|
|
332
|
+
/^\s+(public|private|protected)\s+(static\s+)?(async\s+)?\w+\s*\(/,
|
|
333
|
+
/^\s+(public|private|protected)\s+(readonly\s+)?\w+[\?!]?\s*:/,
|
|
334
|
+
];
|
|
335
|
+
|
|
336
|
+
let inDocBlock = false;
|
|
337
|
+
let depth = 0;
|
|
338
|
+
|
|
339
|
+
for (let i = 0; i < lines.length; i++) {
|
|
340
|
+
const line = lines[i]!;
|
|
341
|
+
const trimmed = line.trim();
|
|
342
|
+
|
|
343
|
+
// Track JSDoc blocks
|
|
344
|
+
if (trimmed.startsWith('/**')) {
|
|
345
|
+
inDocBlock = true;
|
|
346
|
+
}
|
|
347
|
+
if (inDocBlock) {
|
|
348
|
+
skeleton.push(line);
|
|
349
|
+
if (trimmed.includes('*/')) inDocBlock = false;
|
|
350
|
+
continue;
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
// Track brace depth to identify top-level vs nested
|
|
354
|
+
const openBraces = (line.match(/\{/g) ?? []).length;
|
|
355
|
+
const closeBraces = (line.match(/\}/g) ?? []).length;
|
|
356
|
+
|
|
357
|
+
// Include lines that match key patterns OR are at top level (depth 0)
|
|
358
|
+
const isKeyLine = KEEP_PATTERNS.some((p) => p.test(line));
|
|
359
|
+
|
|
360
|
+
if (depth <= 1 && (isKeyLine || trimmed === '')) {
|
|
361
|
+
// For function/class bodies, show only the signature line
|
|
362
|
+
if (trimmed.endsWith('{') && !trimmed.startsWith('//')) {
|
|
363
|
+
skeleton.push(line.replace(/\{$/, '{ ... }'));
|
|
364
|
+
} else {
|
|
365
|
+
skeleton.push(line);
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
depth += openBraces - closeBraces;
|
|
370
|
+
if (depth < 0) depth = 0;
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
return skeleton.join('\n');
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
// ---------------------------------------------------------------------------
|
|
377
|
+
// Null graph store for vector-only degradation
|
|
378
|
+
// ---------------------------------------------------------------------------
|
|
379
|
+
|
|
380
|
+
function createNullGraphStore(): GraphStore {
|
|
381
|
+
return {
|
|
382
|
+
connect: async () => {},
|
|
383
|
+
disconnect: async () => {},
|
|
384
|
+
isHealthy: async () => false,
|
|
385
|
+
query: async () => ({ nodes: [], edges: [], raw: [] }),
|
|
386
|
+
upsertNode: async () => {},
|
|
387
|
+
upsertEdge: async () => {},
|
|
388
|
+
deleteFile: async () => {},
|
|
389
|
+
deleteRepo: async () => {},
|
|
390
|
+
getCounts: async () => ({ totalNodes: 0, totalEdges: 0, byLabel: {} }),
|
|
391
|
+
ensureIndexes: async () => {},
|
|
392
|
+
};
|
|
393
|
+
}
|