import { Server } from '@modelcontextprotocol/sdk/server/index.js'; import { z } from 'zod'; interface CodeNode { type: 'function' | 'class' | 'interface' | 'type' | 'const'; name: string; exported: boolean; async?: boolean; startLine: number; endLine: number; signature?: string; methods?: Array<{ name: string; async: boolean; signature: string; startLine: number; endLine: number; }>; } interface GraphNode { id: string; file: string; type: 'function' | 'class' | 'interface' | 'type' | 'const' | 'method'; name: string; exported: boolean; startLine: number; endLine: number; signature?: string; } interface GraphEdge { from: string; to: string; type: 'calls' | 'imports' | 'extends' | 'implements'; confidence: number; } declare class CodeGraph { private readonly nodes; private readonly edges; addNodes(nodes: CodeNode[], file: string): void; addImport(fromFile: string, toFile: string, specifiers: string[]): void; analyzeCallRelationships(code: string, file: string, functionName: string): void; getNode(id: string): GraphNode | undefined; getEdges(nodeId: string): GraphEdge[]; /** * Add an edge to the graph (used when restoring from serialized data) */ addEdge(edge: GraphEdge): void; /** * Add a graph node directly (used when restoring from serialized data) */ addGraphNode(node: GraphNode): void; /** * Get edges where this node is the target (callers of this function) */ getIncomingEdges(nodeId: string): GraphEdge[]; /** * Count how many nodes call this node */ getCalledByCount(nodeId: string): number; /** * Count how many nodes this node calls */ getCallsCount(nodeId: string): number; getAllNodes(): GraphNode[]; private findNodeByName; private resolveImportPath; toJSON(): { nodes: GraphNode[]; edges: Array<{ from: string; to: string; type: string; confidence: number; }>; }; } declare const ParsePythonResultSchema: z.ZodObject<{ nodes: z.ZodArray; name: z.ZodString; exported: z.ZodBoolean; startLine: z.ZodNumber; endLine: z.ZodNumber; async: z.ZodOptional; signature: z.ZodOptional; calls: z.ZodOptional>; methods: z.ZodOptional; }, z.core.$strip>>>; }, z.core.$strip>>; imports: z.ZodArray>; }, z.core.$strip>>; }, z.core.$strip>; type ParsePythonResult = z.infer; declare class PythonBridge { private process; private readonly pending; private stoppingIntentionally; private stdoutReadline; private stderrReadline; start(): Promise; parsePython(code: string, filePath: string, timeoutMs?: number): Promise; stop(): Promise; private rejectAllPending; } declare const StoreIdBrand: unique symbol; declare const DocumentIdBrand: unique symbol; type StoreId = string & { readonly [StoreIdBrand]: typeof StoreIdBrand; }; type DocumentId = string & { readonly [DocumentIdBrand]: typeof DocumentIdBrand; }; /** * Events emitted when cached data becomes stale. * Used to notify dependent services (e.g., SearchService) when * source data (e.g., code graphs) has been updated or deleted. */ interface CacheInvalidationEvent { /** Type of invalidation */ type: 'graph-updated' | 'graph-deleted'; /** The store whose data changed */ storeId: StoreId; } /** * Listener function for cache invalidation events. */ type CacheInvalidationListener = (event: CacheInvalidationEvent) => void; /** * Service for building, persisting, and querying code graphs. * Code graphs track relationships between code elements (functions, classes, etc.) * for enhanced search context. */ declare class CodeGraphService { private readonly dataDir; private readonly parser; private readonly parserFactory; private readonly graphCache; private readonly cacheListeners; constructor(dataDir: string, pythonBridge?: PythonBridge); /** * Subscribe to cache invalidation events. * Returns an unsubscribe function. */ onCacheInvalidation(listener: CacheInvalidationListener): () => void; /** * Emit a cache invalidation event to all listeners. */ private emitCacheInvalidation; /** * Build a code graph from source files. */ buildGraph(files: Array<{ path: string; content: string; }>): Promise; /** * Save a code graph for a store. */ saveGraph(storeId: StoreId, graph: CodeGraph): Promise; /** * Delete the code graph file for a store. * Silently succeeds if the file doesn't exist. */ deleteGraph(storeId: StoreId): Promise; /** * Load a code graph for a store. * Returns undefined if no graph exists. */ loadGraph(storeId: StoreId): Promise; /** * Get usage stats for a code element. */ getUsageStats(graph: CodeGraph, filePath: string, symbolName: string): { calledBy: number; calls: number; }; /** * Get related code (callers and callees) for a code element. */ getRelatedCode(graph: CodeGraph, filePath: string, symbolName: string): Array<{ id: string; relationship: string; }>; /** * Clear cached graphs. */ clearCache(): void; private getGraphPath; /** * Type guard for SerializedGraph structure. */ private isSerializedGraph; /** * Type guard for valid node types. */ private isValidNodeType; /** * Validate and return a node type, or undefined if invalid. */ private validateNodeType; /** * Type guard for valid edge types. */ private isValidEdgeType; /** * Validate and return an edge type, or undefined if invalid. */ private validateEdgeType; } interface EmbeddingConfig { readonly model: string; readonly batchSize: number; readonly dtype: 'fp32' | 'fp16' | 'q8' | 'q4'; readonly pooling: 'mean' | 'cls' | 'none'; readonly normalize: boolean; readonly queryPrefix: string; readonly docPrefix: string; readonly maxInFlightBatches: number; } interface IndexingConfig { readonly concurrency: number; readonly chunkSize: number; readonly chunkOverlap: number; readonly ignorePatterns: readonly string[]; readonly prependPath: boolean; readonly maxFileSizeBytes: number; } interface SearchConfig { readonly defaultMode: 'vector' | 'fts' | 'hybrid'; readonly defaultLimit: number; } interface CrawlConfig { readonly userAgent: string; readonly timeout: number; readonly maxConcurrency: number; } interface ServerConfig { readonly port: number; readonly host: string; } interface RerankerConfig { readonly enabled: boolean; readonly model: string; readonly topK: number; readonly returnK: number; } interface AppConfig { readonly version: number; readonly dataDir: string; readonly embedding: EmbeddingConfig; readonly indexing: IndexingConfig; readonly search: SearchConfig; readonly crawl: CrawlConfig; readonly server: ServerConfig; readonly reranker: RerankerConfig; } declare class ConfigService { private readonly configPath; private readonly dataDir; private readonly projectRoot; private config; constructor(configPath?: string, dataDir?: string, projectRoot?: string); /** * Get the resolved project root directory. */ resolveProjectRoot(): string; load(): Promise; save(config: AppConfig): Promise; resolveDataDir(): string; resolveConfigPath(): string; private expandPath; } /** * Type-safe manifest with branded StoreId. * Use this in service code for proper type safety. */ interface TypedStoreManifest { version: 1; storeId: StoreId; indexedAt: string; files: Record; } /** * Type-safe file state with branded DocumentIds. */ interface TypedFileState { mtime: number; size: number; hash: string; documentIds: DocumentId[]; } /** * Service for managing store manifests. * * Manifests track the state of indexed files to enable incremental re-indexing. * They are stored in the data directory under manifests/{storeId}.manifest.json. */ declare class ManifestService { private readonly manifestsDir; constructor(dataDir: string); /** * Initialize the manifests directory. */ initialize(): Promise; /** * Get the file path for a store's manifest. */ getManifestPath(storeId: StoreId): string; /** * Load a store's manifest. * Returns an empty manifest if one doesn't exist. * Throws on parse/validation errors (fail fast). */ load(storeId: StoreId): Promise; /** * Save a store's manifest atomically. */ save(manifest: TypedStoreManifest): Promise; /** * Delete a store's manifest. * Called when a store is deleted or during full re-index. */ delete(storeId: StoreId): Promise; /** * Check if a file exists. */ private fileExists; /** * Convert a parsed manifest to a typed manifest with branded types. */ private toTypedManifest; } declare class EmbeddingEngine { private extractor; private initPromise; private _dimensions; private disposed; private readonly config; constructor(config?: EmbeddingConfig); /** * Guard against use-after-dispose */ private assertNotDisposed; /** * Initialize the embedding pipeline (concurrency-safe). * Multiple concurrent calls will share the same initialization promise. */ initialize(): Promise; /** * Embed a search query. Applies queryPrefix for asymmetric models. */ embedQuery(text: string): Promise; /** * Embed a document for indexing. Applies docPrefix for asymmetric models. */ embedDocument(text: string): Promise; /** * Internal: embed text without prefix. */ private embedText; /** * Embed a batch of documents with optional parallelism. * When maxInFlightBatches > 1, processes multiple batches concurrently. */ embedBatch(texts: string[]): Promise; /** * Process batches sequentially (original behavior). */ private embedBatchesSequential; /** * Process batches with controlled concurrency. */ private embedBatchesConcurrent; /** * Process a single batch and return embeddings. */ private processSingleBatch; /** * Get cached embedding dimensions. Throws if embed() hasn't been called yet. * Use ensureDimensions() if you need to guarantee dimensions are available. */ getDimensions(): number; /** * Check if the embedding pipeline is initialized. */ isInitialized(): boolean; /** * Check if this engine has been disposed. */ isDisposed(): boolean; /** * Reset the engine to uninitialized state, allowing reuse after disposal. * If currently initialized, disposes the pipeline first. */ reset(): Promise; /** * Ensure dimensions are available, initializing the model if needed. * Returns the embedding dimensions for the current model. */ ensureDimensions(): Promise; /** * Dispose the embedding pipeline to free resources. * Should be called before process exit to prevent ONNX runtime cleanup issues on macOS. * After disposal, this engine cannot be used again. */ dispose(): Promise; } declare const DocumentTypeSchema: z.ZodEnum<{ file: "file"; web: "web"; chunk: "chunk"; }>; type DocumentType = z.infer; interface DocumentMetadata { readonly path?: string | undefined; readonly url?: string | undefined; readonly type: DocumentType; readonly storeId: StoreId; readonly indexedAt: string; readonly fileHash?: string | undefined; readonly chunkIndex?: number | undefined; readonly totalChunks?: number | undefined; readonly [key: string]: unknown; } interface Document { readonly id: DocumentId; readonly content: string; readonly vector: Float32Array; readonly metadata: DocumentMetadata; } declare class LanceStore { private connection; private readonly tables; private readonly dataDir; private _dimensions; private embeddingFunction; constructor(dataDir: string); /** * Set the embedding function for auto-embedding queries. * Must be called before initialize() for new tables. * The embedding function is initialized and its dimensions are used for schema creation. */ setEmbeddingFunction(config: EmbeddingConfig): Promise; /** * Check if embedding function is available for auto-embedding queries. */ hasEmbeddingFunction(): boolean; /** * Set the embedding dimensions. Must be called before initialize(). * This allows dimensions to be derived from the embedding model at runtime. * Idempotent: subsequent calls are ignored if dimensions are already set. */ setDimensions(dimensions: number): void; initialize(storeId: StoreId): Promise; addDocuments(storeId: StoreId, documents: Document[]): Promise; deleteDocuments(storeId: StoreId, documentIds: DocumentId[]): Promise; clearAllDocuments(storeId: StoreId): Promise; search(storeId: StoreId, vector: Float32Array, limit: number, _threshold?: number): Promise>; /** * Search using a text query with automatic embedding. * Requires setEmbeddingFunction() to have been called. * Uses the embedding function to compute query embeddings consistently with document embeddings. */ searchText(storeId: StoreId, query: string, limit: number): Promise>; createFtsIndex(storeId: StoreId): Promise; /** * Check if a table has the fts_content column (v3 schema). * Tables created before the FTS improvement only have content. */ private hasFtsContentColumn; fullTextSearch(storeId: StoreId, query: string, limit: number): Promise>; /** * Get all documents from a store (for training data generation). * Returns documents in batches to avoid memory issues with large stores. */ getAllDocuments(storeId: StoreId, options?: { limit?: number; offset?: number; }): Promise>; /** * Count total documents in a store. */ countDocuments(storeId: StoreId): Promise; deleteStore(storeId: StoreId): Promise; close(): void; /** * Async close for API consistency. Calls sync close() internally. * Do NOT call process.exit() after this - let the event loop drain * naturally so native threads can complete cleanup. */ closeAsync(): Promise; private getTableName; private getTable; } /** * Optional details for enhanced progress reporting. * All fields are optional - consumers use what they need. */ interface ProgressDetails { /** Current file being processed (basename) */ currentFile?: string; /** Milliseconds elapsed since operation start */ elapsedMs?: number; /** Throughput for ETA calculation */ filesPerSecond?: number; /** Store-level progress for multi-store operations */ storeProgress?: { current: number; total: number; storeName: string; }; } interface ProgressEvent { type: 'start' | 'progress' | 'complete' | 'error'; current: number; total: number; message: string; /** Enhanced details - optional, existing consumers can ignore */ details?: ProgressDetails; } type ProgressCallback = (event: ProgressEvent) => void; type Result = { readonly success: true; readonly data: T; } | { readonly success: false; readonly error: E; }; /** * Ingestion configuration for controlling clone and indexing behavior. * Allows per-store customization of what files to include/exclude. */ declare const IngestConfigSchema: z.ZodObject<{ partialClone: z.ZodOptional; excludeGlobs: z.ZodOptional>; skipMinified: z.ZodOptional; skipBinaries: z.ZodOptional; maxFileSizeBytes: z.ZodOptional; maxFiles: z.ZodOptional; }, z.core.$strip>; type IngestConfig = z.infer; /** * Discriminated union of all store definition types. * Use the `type` field to narrow the type. */ declare const StoreDefinitionSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{ name: z.ZodString; description: z.ZodOptional; tags: z.ZodOptional>; type: z.ZodLiteral<"file">; path: z.ZodString; pathType: z.ZodOptional>; ingest: z.ZodOptional; excludeGlobs: z.ZodOptional>; skipMinified: z.ZodOptional; skipBinaries: z.ZodOptional; maxFileSizeBytes: z.ZodOptional; maxFiles: z.ZodOptional; }, z.core.$strip>>; }, z.core.$strip>, z.ZodObject<{ name: z.ZodString; description: z.ZodOptional; tags: z.ZodOptional>; type: z.ZodLiteral<"repo">; url: z.ZodString; branch: z.ZodOptional; depth: z.ZodOptional; ingest: z.ZodOptional; excludeGlobs: z.ZodOptional>; skipMinified: z.ZodOptional; skipBinaries: z.ZodOptional; maxFileSizeBytes: z.ZodOptional; maxFiles: z.ZodOptional; }, z.core.$strip>>; }, z.core.$strip>, z.ZodObject<{ name: z.ZodString; description: z.ZodOptional; tags: z.ZodOptional>; type: z.ZodLiteral<"web">; url: z.ZodURL; depth: z.ZodDefault; maxPages: z.ZodOptional; crawlInstructions: z.ZodOptional; extractInstructions: z.ZodOptional; }, z.core.$strip>], "type">; type StoreDefinition = z.infer; /** * Root configuration schema for store definitions. * Version field enables future schema migrations. */ declare const StoreDefinitionsConfigSchema: z.ZodObject<{ version: z.ZodLiteral<1>; stores: z.ZodArray; tags: z.ZodOptional>; type: z.ZodLiteral<"file">; path: z.ZodString; pathType: z.ZodOptional>; ingest: z.ZodOptional; excludeGlobs: z.ZodOptional>; skipMinified: z.ZodOptional; skipBinaries: z.ZodOptional; maxFileSizeBytes: z.ZodOptional; maxFiles: z.ZodOptional; }, z.core.$strip>>; }, z.core.$strip>, z.ZodObject<{ name: z.ZodString; description: z.ZodOptional; tags: z.ZodOptional>; type: z.ZodLiteral<"repo">; url: z.ZodString; branch: z.ZodOptional; depth: z.ZodOptional; ingest: z.ZodOptional; excludeGlobs: z.ZodOptional>; skipMinified: z.ZodOptional; skipBinaries: z.ZodOptional; maxFileSizeBytes: z.ZodOptional; maxFiles: z.ZodOptional; }, z.core.$strip>>; }, z.core.$strip>, z.ZodObject<{ name: z.ZodString; description: z.ZodOptional; tags: z.ZodOptional>; type: z.ZodLiteral<"web">; url: z.ZodURL; depth: z.ZodDefault; maxPages: z.ZodOptional; crawlInstructions: z.ZodOptional; extractInstructions: z.ZodOptional; }, z.core.$strip>], "type">>; }, z.core.$strip>; type StoreDefinitionsConfig = z.infer; type StoreType = 'file' | 'repo' | 'web'; type StoreStatus = 'ready' | 'indexing' | 'error'; /** * Path storage type for file and repo stores. * - 'relative': Path is relative to projectRoot, portable across machines * - 'absolute': Path is absolute, used for paths outside projectRoot * - undefined: Pre-v3 store, treated as 'absolute' */ type PathType = 'relative' | 'absolute'; interface BaseStore { readonly id: StoreId; readonly name: string; readonly description?: string | undefined; readonly tags?: readonly string[] | undefined; readonly status?: StoreStatus | undefined; readonly createdAt: Date; readonly updatedAt: Date; /** * Schema version for detecting incompatible stores. * Stores with schemaVersion < CURRENT_SCHEMA_VERSION require reindex. */ readonly schemaVersion: number; /** * Embedding model ID used to index this store. * Used for provenance tracking and mismatch detection. */ readonly modelId: string; } interface FileStore extends BaseStore { readonly type: 'file'; readonly path: string; readonly pathType?: PathType | undefined; readonly ingest?: IngestConfig | undefined; } interface RepoStore extends BaseStore { readonly type: 'repo'; readonly path: string; readonly pathType?: PathType | undefined; readonly url?: string | undefined; readonly branch?: string | undefined; readonly depth?: number | undefined; readonly ingest?: IngestConfig | undefined; } interface WebStore extends BaseStore { readonly type: 'web'; readonly url: string; readonly depth: number; readonly maxPages?: number | undefined; readonly crawlInstructions?: string | undefined; readonly extractInstructions?: string | undefined; } type Store = FileStore | RepoStore | WebStore; interface FilterStats { candidates: number; accepted: number; skippedExtension: number; skippedDirSegment: number; skippedIgnorePattern: number; skippedMinified: number; skippedExcluded: number; skippedLargeFiles: number; skippedLargeBytes: number; skippedBinary: number; skippedStatError: number; } interface IndexResult { filesIndexed: number; chunksCreated: number; timeMs: number; filterStats?: FilterStats; } interface IndexOptions { chunkSize?: number; chunkOverlap?: number; codeGraphService?: CodeGraphService; concurrency?: number; manifestService?: ManifestService; ignorePatterns?: readonly string[]; prependPath?: boolean; maxFileSizeBytes?: number; } interface IncrementalIndexResult extends IndexResult { filesAdded: number; filesModified: number; filesDeleted: number; filesUnchanged: number; } declare class IndexService { private readonly lanceStore; private readonly embeddingEngine; private readonly chunker; private readonly codeGraphService; private readonly manifestService; private readonly driftService; private readonly concurrency; private readonly ignoreDirs; private readonly ignoreFilePatterns; private readonly prependPath; private readonly maxFileSizeBytes; constructor(lanceStore: LanceStore, embeddingEngine: EmbeddingEngine, options?: IndexOptions); indexStore(store: Store, onProgress?: ProgressCallback): Promise>; /** * Incrementally index a store, only processing changed files. * Requires manifestService to be configured. * * @param store - The store to index * @param onProgress - Optional progress callback * @returns Result with incremental index statistics */ indexStoreIncremental(store: Store, onProgress?: ProgressCallback): Promise>; private indexFileStore; /** * Process a single file: read, chunk, embed, and return documents. * Extracted for parallel processing. */ private processFile; /** * Get tracked files from git. Returns null if not a git repo or on error. */ private getTrackedFiles; /** * Discover files for indexing. Uses git ls-files for git repos, filesystem walk otherwise. * Always applies extension filter, ignore patterns, and size limit. * Optionally applies per-store ingest configuration. */ private discoverFiles; /** * Apply extension filter, ignore patterns, size limit, and per-store ingest filters. * Reuses existing ignoreFilePatterns from parseIgnorePatternsForScanning. */ private filterFiles; private scanDirectory; /** * Classify file type for ranking purposes. * Documentation files rank higher than source code for documentation queries. * Phase 4: Enhanced to detect internal implementation files. */ private classifyFileType; /** * Detect if a source file is internal implementation code. * Internal code should rank lower than public-facing APIs and docs. */ private isInternalImplementation; } interface RerankerCandidate { readonly id: string; readonly content: string; readonly score: number; } interface RerankerResult { readonly id: string; readonly originalScore: number; readonly rerankerScore: number; } interface RerankerOutput { readonly results: readonly RerankerResult[]; readonly timeMs: number; } declare class RerankerService { private model; private tokenizer; private initPromise; private disposed; private readonly config; constructor(config: RerankerConfig); /** * Guard against use-after-dispose */ private assertNotDisposed; /** * Check if reranking is enabled */ isEnabled(): boolean; /** * Initialize the reranker model (concurrency-safe). * Multiple concurrent calls will share the same initialization promise. */ initialize(): Promise; /** * Rerank candidates by scoring query-document pairs with the cross-encoder. * Returns results sorted by reranker score (descending). */ rerank(query: string, candidates: readonly RerankerCandidate[]): Promise; /** * Score a single query-document pair using the cross-encoder. */ private scoreQueryDocPair; /** * Check if the reranker is initialized. */ isInitialized(): boolean; /** * Check if this service has been disposed. */ isDisposed(): boolean; /** * Reset the service to uninitialized state, allowing reuse after disposal. */ reset(): Promise; /** * Log aggregate reranker debug stats. Only meaningful when BK_DEBUG_RERANKER=1. */ static logDebugStats(): void; /** * Dispose the reranker to free resources. */ dispose(): Promise; } type SearchMode = 'vector' | 'fts' | 'hybrid'; /** * Search intent hints for context-aware ranking. * These align with the MCP API contract. */ type SearchIntent = 'find-pattern' | 'find-implementation' | 'find-usage' | 'find-definition' | 'find-documentation' | 'find-files'; interface CodeUnit { type: 'function' | 'class' | 'interface' | 'type' | 'const' | 'documentation' | 'example'; name: string; signature: string; fullContent: string; startLine: number; endLine: number; language: string; } interface ResultSummary { readonly type: 'function' | 'class' | 'interface' | 'pattern' | 'documentation'; readonly name: string; readonly signature: string; readonly purpose: string; readonly location: string; readonly relevanceReason: string; readonly relatedFiles?: readonly string[]; } interface ResultContext { readonly interfaces: readonly string[]; readonly keyImports: readonly string[]; readonly relatedConcepts: readonly string[]; readonly usage: { readonly calledBy: number; readonly calls: number; }; } interface ResultFull { readonly completeCode: string; readonly relatedCode: ReadonlyArray<{ readonly file: string; readonly summary: string; readonly relationship: string; }>; readonly documentation: string; readonly tests?: string | undefined; } type DetailLevel = 'minimal' | 'contextual' | 'full'; interface SearchQuery { readonly query: string; readonly stores?: readonly StoreId[] | undefined; readonly mode?: SearchMode | undefined; readonly limit?: number | undefined; readonly threshold?: number | undefined; readonly minRelevance?: number | undefined; readonly intent?: SearchIntent | undefined; readonly detail?: DetailLevel | undefined; } interface SearchResult { readonly id: DocumentId; score: number; readonly content: string; readonly highlight?: string | undefined; readonly metadata: DocumentMetadata; readonly codeUnit?: CodeUnit | undefined; readonly summary?: ResultSummary | undefined; readonly context?: ResultContext | undefined; readonly full?: ResultFull | undefined; readonly rankingMetadata?: { readonly vectorRank?: number; readonly ftsRank?: number; readonly vectorRRF: number; readonly ftsRRF: number; readonly fileTypeBoost: number; readonly frameworkBoost: number; readonly urlKeywordBoost: number; readonly pathKeywordBoost: number; readonly depthBoost: number; readonly entryPointBoost: number; readonly rawVectorScore?: number; } | undefined; } type SearchConfidence = 'high' | 'medium' | 'low'; interface SearchResponse { readonly query: string; readonly mode: SearchMode; readonly stores: readonly StoreId[]; readonly results: readonly SearchResult[]; readonly totalResults: number; readonly timeMs: number; readonly confidence?: SearchConfidence | undefined; readonly maxRawScore?: number | undefined; readonly rerankTimeMs?: number | undefined; } declare class SearchService { private readonly lanceStore; private readonly codeUnitService; private readonly codeGraphService; private readonly rerankerService; private readonly graphCache; private readonly searchConfig; private readonly unsubscribeCacheInvalidation; constructor(lanceStore: LanceStore, codeGraphService?: CodeGraphService, searchConfig?: SearchConfig, rerankerService?: RerankerService); /** * Clean up resources (unsubscribe from events). * Call this when destroying the service. */ cleanup(): void; /** * Load code graph for a store, with caching. * Returns null if no graph is available. */ private loadGraphForStore; /** * Calculate confidence level based on max raw vector similarity score. * Configurable via environment variables, with sensible defaults for CLI usage. */ private calculateConfidence; search(query: SearchQuery): Promise; /** * Deduplicate results by source file path. * Keeps the best chunk for each unique source, considering both score and query relevance. */ private deduplicateBySource; /** * Count how many query terms appear in the content. */ private countQueryTerms; /** * Normalize scores to 0-1 range and optionally filter by threshold. * This ensures threshold values match displayed scores (UX consistency). * * Edge case handling: * - If there's only 1 result or all results have the same score, normalization * would make them all 1.0. In this case, we keep the raw scores to allow * threshold filtering to work meaningfully on absolute quality. */ private normalizeAndFilterScores; /** * Generate query variants for multi-query expansion. * Strips intent prefixes to create a keyword-focused variant. * Returns original + variants (deduplicated). */ private expandQuery; /** * Run vector search across multiple query variants and merge results. * Deduplicates by document ID, keeping the highest score. */ private multiQueryVectorSearch; /** * Fetch raw vector search results without normalization. * Returns results with raw cosine similarity scores [0-1]. * Uses LanceDB's embedding function for query embedding, * ensuring consistent query/document embedding through a single code path. */ private vectorSearchRaw; private ftsSearch; /** * Internal hybrid search result with additional metadata for confidence calculation. */ private hybridSearchWithMetadata; searchAllStores(query: SearchQuery, storeIds: StoreId[]): Promise; /** * Get a score multiplier based on file type and query intent. * Documentation files get a strong boost to surface them higher. * Phase 4: Strengthened boosts for better documentation ranking. * Phase 1: Intent-based adjustments for context-aware ranking. */ private getFileTypeBoost; /** * Get a score multiplier based on URL keyword matching. * Boosts results where URL path contains significant query keywords. * This helps queries like "troubleshooting" rank /troubleshooting pages first. */ private getUrlKeywordBoost; /** * Get a score multiplier based on file path keyword matching. * Boosts results where file path contains significant query keywords. * This helps queries like "dispatcher" rank async_dispatcher.py higher. */ private getPathKeywordBoost; /** * Get a depth-based score multiplier, gated by query intent. * Root-level files (depth 0) are boosted for how-to and conceptual queries * where high-level docs/READMEs are most relevant. * Returns 1.0 (no-op) for implementation/debugging/comparison intents * to avoid disturbing candidate ordering before reranker. */ private getDepthBoost; /** * Get an entry-point score multiplier, gated by query intent. * Entry-point files (index.ts, main.py, etc.) are boosted for how-to * and implementation queries where API surfaces are most relevant. * Returns 1.0 (no-op) for debugging/comparison/conceptual intents. */ private getEntryPointBoost; /** * Get a score multiplier based on framework context. * If query mentions a framework, boost results from that framework's files. */ private getFrameworkContextBoost; private addProgressiveContext; private extractCodeUnitFromResult; private extractSymbolName; private inferType; private generatePurpose; private generateRelevanceReason; private extractInterfaces; private extractImports; private extractConcepts; private extractDocumentation; /** * Get usage stats from code graph. * Returns default values if no graph is available. */ private getUsageFromGraph; /** * Get related code from graph. * Returns callers and callees for the symbol. */ private getRelatedCodeFromGraph; /** * Parse a node ID into file path and symbol name. */ private parseNodeId; } /** * Service for managing .gitignore patterns for Bluera Knowledge. * * When stores are created, this service ensures the project's .gitignore * is updated to: * - Ignore the .bluera/ data directory (not committed) * - Allow committing .bluera/bluera-knowledge/stores.config.json (for team sharing) */ declare class GitignoreService { private readonly gitignorePath; constructor(projectRoot: string); /** * Check if all required patterns are present in .gitignore */ hasRequiredPatterns(): Promise; /** * Ensure required .gitignore patterns are present. * * - Creates .gitignore if it doesn't exist * - Appends missing patterns if .gitignore exists * - Does nothing if all patterns are already present * * @returns Object with updated flag and descriptive message */ ensureGitignorePatterns(): Promise<{ updated: boolean; message: string; }>; /** * Get the path to the .gitignore file */ getGitignorePath(): string; } /** * Service for managing git-committable store definitions. * * Store definitions are saved to `.bluera/bluera-knowledge/stores.config.json` * within the project root. This file is designed to be committed to version * control, allowing teams to share store configurations. * * The actual store data (vector embeddings, cloned repos) lives in the data * directory and should be gitignored. */ declare class StoreDefinitionService { private readonly configPath; private readonly projectRoot; private config; constructor(projectRoot?: string); /** * Load store definitions from config file. * Returns empty config if file doesn't exist. * Throws on parse/validation errors (fail fast per CLAUDE.md). */ load(): Promise; /** * Save store definitions to config file. */ save(config: StoreDefinitionsConfig): Promise; /** * Add a store definition. * Throws if a definition with the same name already exists. */ addDefinition(definition: StoreDefinition): Promise; /** * Remove a store definition by name. * Returns true if removed, false if not found. */ removeDefinition(name: string): Promise; /** * Update an existing store definition. * Only updates the provided fields, preserving others. * Throws if definition not found. */ updateDefinition(name: string, updates: { description?: string; tags?: string[]; }): Promise; /** * Get a store definition by name. * Returns undefined if not found. */ getByName(name: string): Promise; /** * Check if any definitions exist. */ hasDefinitions(): Promise; /** * Resolve a file store path relative to project root. */ resolvePath(path: string): string; /** * Get the config file path. */ getConfigPath(): string; /** * Get the project root. */ getProjectRoot(): string; /** * Clear the cached config (useful for testing). */ clearCache(): void; } interface CreateStoreInput { name: string; type: StoreType; path?: string | undefined; url?: string | undefined; description?: string | undefined; tags?: string[] | undefined; branch?: string | undefined; depth?: number | undefined; ingest?: IngestConfig | undefined; maxPages?: number | undefined; crawlInstructions?: string | undefined; extractInstructions?: string | undefined; } interface StoreServiceOptions { /** Optional definition service for auto-updating git-committable config */ definitionService?: StoreDefinitionService; /** Optional gitignore service for ensuring .gitignore patterns */ gitignoreService?: GitignoreService; /** Optional project root for resolving relative paths */ projectRoot?: string; /** Embedding model ID for provenance tracking (required for new stores) */ embeddingModelId: string; } interface OperationOptions { /** Skip syncing to store definitions (used by stores:sync command) */ skipDefinitionSync?: boolean; } declare class StoreService { private readonly dataDir; private readonly definitionService; private readonly gitignoreService; private readonly projectRoot; private readonly embeddingModelId; private registry; private registryMtime; /** Flag for lazy schema migration - bump to v3 on next save */ private needsMigration; constructor(dataDir: string, options: StoreServiceOptions); /** * Get the current embedding model ID used for new stores. * Used by model compatibility validation. */ getCurrentModelId(): string; /** * Convert an absolute path to stored format. * Returns relative path + pathType if inside projectRoot, otherwise absolute. * * @param absolutePath - The absolute path to convert * @param isClonedRepo - If true, always store absolute (managed by BK under dataDir) */ private toStoredPath; /** * Resolve a stored path to absolute runtime path. * Handles both relative (v3+) and absolute (v2 and earlier) paths. * * @param storedPath - The path as stored in stores.json * @param pathType - The path type (undefined for pre-v3 stores, treated as absolute) */ private resolveStoredPath; initialize(): Promise; /** * Reload registry from disk if it has been modified by another process (e.g., CLI). * This enables MCP server to see stores created by CLI without restart. */ private ensureRegistryFresh; /** * Convert a Store and CreateStoreInput to a StoreDefinition for persistence. * Returns undefined for stores that shouldn't be persisted (e.g., local repo stores). */ private createDefinitionFromStore; /** * Create a StoreDefinition from an existing store (without original input). * Used when updating/renaming stores where we don't have the original input. * Returns undefined for stores that shouldn't be persisted (e.g., local repo stores). */ private createDefinitionFromExistingStore; create(input: CreateStoreInput, options?: OperationOptions): Promise>; list(type?: StoreType): Promise; get(id: StoreId): Promise; getByName(name: string): Promise; getByIdOrName(idOrName: string): Promise; update(id: StoreId, updates: Partial>, options?: OperationOptions): Promise>; delete(id: StoreId, options?: OperationOptions): Promise>; /** * Upgrade a schema v1 store to v2 by adding the modelId field. * Called after successful reindexing to ensure store is searchable. * No-op if store already has modelId (already v2+). */ upgradeStoreSchema(id: StoreId): Promise; private loadRegistry; private saveRegistry; } interface ServiceContainer { config: ConfigService; store: StoreService; search: SearchService; index: IndexService; lance: LanceStore; embeddings: EmbeddingEngine; codeGraph: CodeGraphService; pythonBridge: PythonBridge; manifest: ManifestService; } /** * Configuration options for the MCP server */ interface MCPServerOptions { dataDir?: string | undefined; config?: string | undefined; projectRoot?: string | undefined; } /** * Create MCP server with pre-initialized services. * * Services are initialized ONCE at server startup and reused for all tool calls. * This reduces per-call latency from 1-15s to <500ms. */ declare function createMCPServer(options: MCPServerOptions, services: ServiceContainer): Server; /** * Run MCP server with lazy service initialization. * * Services are initialized ONCE at startup: * - Lightweight services (config, store, lance wrapper): immediate * - Heavy services (embeddings model): deferred until first use * * This reduces server startup from ~5s to <500ms. */ declare function runMCPServer(options: MCPServerOptions): Promise; export { createMCPServer, runMCPServer };