import { Database as Database$1 } from 'sql.js'; /** * Shared types for reponova */ /** A node in the knowledge graph */ interface GraphNode { id: string; label: string; type: string; source_file?: string; repo?: string; community?: string; start_line?: number; end_line?: number; /** Function/method signature extracted from AST */ signature?: string; /** Docstring extracted from AST */ docstring?: string; /** Base classes (for class nodes) */ bases?: string[]; properties?: Record; } /** An edge in the knowledge graph */ interface GraphEdge { source: string; target: string; type: string; confidence?: number; properties?: Record; } /** Community detected by community detection */ interface GraphCommunity { id: string; name: string; members: string[]; size: number; } /** Full graph structure */ interface GraphData { nodes: GraphNode[]; edges: GraphEdge[]; communities?: GraphCommunity[]; metadata?: GraphMetadata; } /** Graph metadata */ interface GraphMetadata { reponova_version?: string; built_at?: string; /** Relative path from graphDir to configDir (for reconstructing repo absolute paths) */ config_dir?: string; /** Repo mappings — name + path relative to configDir (as in the YAML config) */ repos?: Array<{ name: string; path: string; }>; /** single = 1 repo (no prefix on source_file), multi = N repos (repo prefix on source_file) */ mode?: "single" | "multi"; node_count?: number; edge_count?: number; /** Runtime build config summary — used by MCP server, check, and status */ build_config?: BuildConfigFingerprint; } /** * Minimal build config fingerprint stored in graph.json metadata. * Contains only what the MCP runtime needs to initialize the correct * embeddings provider at startup. */ interface BuildConfigFingerprint { embeddings: { enabled: boolean; provider?: string; }; } /** Adjacency map for BFS/Dijkstra */ interface AdjacencyMap { /** node_id → list of outgoing edges */ outgoing: Map; /** node_id → list of incoming edges */ incoming: Map; } interface AdjacencyEntry { nodeId: string; edgeType: string; weight: number; } /** * Configuration file schema — flat, no more BuildConfig wrapper. * All build-related fields are at the root level. */ interface Config { output: string; repos: RepoConfig[]; models: ModelsConfig; providers: Record; /** Glob patterns for source code files (empty = auto-detect) */ patterns: string[]; /** Glob patterns to exclude from source code detection */ exclude: string[]; /** Exclude common non-source directories (node_modules, venv, .git, etc.) */ exclude_common: boolean; /** Enable incremental builds */ incremental: boolean; docs: DocsConfig; /** Per-plugin configuration (keyed by plugin id) */ plugins: Record; embeddings: EmbeddingsConfig; enrich: EnrichConfig; /** Generate interactive HTML visualizations */ html: boolean; /** Minimum node degree to include in HTML visualization */ html_min_degree?: number; outlines: OutlineConfig; server: ServerConfig; } interface RepoConfig { name: string; path: string; } /** Centralized model management */ interface ModelsConfig { cache_dir: string; gpu: "auto" | "cpu" | "cuda" | "metal" | "vulkan"; threads: number; download_on_first_use: boolean; } interface DocsConfig { enabled: boolean; patterns: string[]; exclude: string[]; max_file_size_kb: number; } /** Per-plugin config: common fields + arbitrary custom properties */ interface PluginConfig { /** Full npm package name. If omitted, resolved as @reponova/lang-. */ package?: string; enabled: boolean; patterns: string[]; exclude: string[]; [key: string]: unknown; } type ProviderType = "openai" | "llama-cpp" | "onnx"; interface ProviderConfig { type: ProviderType; model?: string; base_url?: string; api_key?: string; timeout?: number; context_size?: number; } interface EmbeddingsConfig { enabled: boolean; provider?: string; batch_size: number; } interface EnrichMaxTokens { descriptions: number; profiles: number; routing: number; restructure: number; } interface EnrichProfileLimits { max_nodes: number; max_edges: number; } interface EnrichConfig { enabled: boolean; provider?: string; threshold: number; max_communities: number; candidate_threshold: number; description_batch_tokens: number; routing_batch_size: number; concurrency: number; max_retry_depth: number; max_tokens: EnrichMaxTokens; profile: EnrichProfileLimits; restructure_max_pairs: number; } /** Outline config — simplified. File selection comes from top-level patterns. */ interface OutlineConfig { enabled: boolean; } interface ServerConfig { [key: string]: unknown; } /** Search result */ interface SearchResult { id: string; label: string; type: string; source_file?: string; repo?: string; community?: string; rank: number; properties?: Record; } /** Impact analysis result */ interface ImpactResult { target: GraphNode; upstream: ImpactLayer[]; downstream: ImpactLayer[]; cross_repo_summary: Map; } interface ImpactLayer { depth: number; nodes: ImpactNode[]; } interface ImpactNode { id: string; label: string; source_file?: string; repo?: string; edge_type: string; via?: string; } /** Shortest path result */ interface PathResult { found: boolean; from: string; to: string; hops: number; path: PathStep[]; cross_repo?: string; edge_types_used?: Map; } interface PathStep { node_id: string; label: string; source_file?: string; edge_type?: string; } interface ContextCandidate { id: string; label: string; type: string; source_file?: string; repo?: string; community?: string; score: number; signature?: string; docstring?: string; graph_rel_path?: string | null; absolute_path?: string | null; } interface RelationshipEntry { from: string; to: string; edge_type: string; from_label?: string; to_label?: string; } interface CommunitySummaryEntry { community_id: string; label: string; summary: string; } interface SourceSnippet { file: string; start_line: number; end_line: number; content: string; } interface StructuredContext { candidates: ContextCandidate[]; relationships: RelationshipEntry[]; communities: CommunitySummaryEntry[]; source_snippets: SourceSnippet[]; } /** Node detail result */ interface NodeDetail { id: string; label: string; type: string; source_file?: string; repo?: string; community?: string; signature?: string; decorators?: string[]; docstring?: string; start_line?: number; end_line?: number; outgoing_edges: GroupedEdges; incoming_edges: GroupedEdges; centrality: CentralityMetrics; } interface GroupedEdges { [edgeType: string]: EdgeDetail[]; } interface EdgeDetail { node_id: string; label: string; source_file?: string; repo?: string; is_cross_repo?: boolean; is_external?: boolean; } interface CentralityMetrics { in_degree: number; out_degree: number; betweenness: number; } /** Outline structures */ interface FileOutline { file_path: string; line_count: number; imports: ImportEntry[]; functions: FunctionEntry[]; classes: ClassEntry[]; } interface ImportEntry { module: string; names?: string[]; line: number; } interface FunctionEntry { name: string; signature: string; decorators: string[]; docstring?: string; start_line: number; end_line: number; calls: string[]; } interface ClassEntry { name: string; bases: string[]; docstring?: string; start_line: number; end_line: number; methods: FunctionEntry[]; } interface McpServerOptions { graphPath?: string; } declare function startMcpServer(options?: McpServerOptions): Promise; /** * Load and validate configuration from a YAML file. */ declare function loadConfig(configPath?: string): { config: Config; configDir: string; }; type Database = Database$1; interface DbOptions { readonly?: boolean; } /** * Open or create the SQLite database using sql.js (WASM). */ declare function openDatabase(dbPath: string, _options?: DbOptions): Promise; /** * Save the database to disk. */ declare function saveDatabase(db: Database, dbPath: string): void; /** * Initialize the database schema. */ declare function initializeSchema(db: Database): void; /** * Populate the database from graph data. */ declare function populateDatabase(db: Database, graphData: GraphData): void; /** * Get a metadata value. */ declare function getMeta(db: Database, key: string): string | null; /** * Run a query and return all result rows as objects. */ declare function queryAll(db: Database, sql: string, params?: unknown[]): Record[]; /** * Run a query and return the first result row. */ declare function queryOne(db: Database, sql: string, params?: unknown[]): Record | null; /** * Load graph.json from disk and parse it. * Handles multiple graph formats: * - Nodes: { id, label, type, source_file, community, ... } * - Edges: stored as "edges" or "links" with { source, target, type/relation, ... } */ declare function loadGraphData(graphJsonPath: string): GraphData; /** * Build an adjacency map from the graph edges for BFS/Dijkstra traversal. */ declare function buildAdjacencyMap(edges: GraphEdge[], edgeWeights?: Record): AdjacencyMap; /** * Build a node lookup map: id → GraphNode. */ declare function buildNodeMap(nodes: GraphNode[]): Map; interface SearchOptions { top_k?: number; repo?: string; type?: string; } /** * Perform text search on knowledge graph nodes using LIKE matching. * Scores results by number of matching terms (higher = better match). */ declare function searchNodes(db: Database, query: string, options?: SearchOptions): SearchResult[]; /** * Fuzzy match a node by name. Uses OR logic (any term matches) * to handle partial/typo'd names like "get_usr" matching "get_user_by_id". */ declare function fuzzyMatchNode(db: Database, name: string, top_k?: number): SearchResult[]; /** * Build a Set of directories to skip, considering the `exclude_common` config flag. * * @param excludeCommon - When true, returns a Set of COMMON_SKIP_DIRS; when false, returns empty Set */ declare function buildSkipDirs(excludeCommon: boolean): Set; /** Repo mapping — runtime representation with resolved absolute path. */ interface RepoMapping { name: string; /** * Absolute path to the repo root (normalized forward slashes). * Build-time: resolve(configDir, repoConfig.path). * Query-time: reconstructed from metadata — resolve(graphDir, metadata.config_dir, repo.path). * NEVER serialized as absolute — graph.json stores only relative paths. */ absPath: string; } /** Resolved once from config — passed to all build-time functions. */ interface PathContext { mode: "single" | "multi"; repos: RepoMapping[]; /** Root of the workspace (single-repo = repoRoot, multi = tmpDir/workspace) */ workspace: string; /** Output directory (e.g. /abs/path/reponova-out) */ outputDir: string; } /** * Resolve source_file → absolute filesystem path using repo mappings from metadata. * ONLY function for absolute path resolution. Does NOT use graphDir. */ declare function resolveAbsolutePath(repos: RepoMapping[], sourceFile: string, mode: "single" | "multi"): string | null; /** Construct the path to a pre-computed outline file. */ declare function resolveOutlinePath(graphDir: string, sourceFile: string): string; /** * Reconstruct RepoMapping[] from graph.json metadata (query-time). * Returns null if metadata is missing required fields. */ declare function reconstructRepos(graphDir: string, metadataConfigDir?: string, metadataRepos?: Array<{ name: string; path: string; }>): RepoMapping[] | null; interface ResolvedPaths { /** Path relative to the graph output directory (portable across machines if layout is preserved) */ graph_rel_path: string | null; /** Absolute filesystem path (null if file not found or repos unavailable) */ absolute_path: string | null; } /** * Create a bidirectional pattern matcher. * * Tests patterns against multiple forms of the same path: * 1. As given (covers both workspace-relative and repo-relative depending on caller) * 2. Stripped: removes known repo prefix (workspace-relative → repo-relative) * 3. Prefixed: adds repoName (repo-relative → workspace-relative) * * @param patterns - Glob patterns * @param repoNames - Known repo names (enables dual matching) * @returns A function `(relPath, repoName?) => boolean` */ declare function createPatternMatcher(patterns: string[], repoNames?: Set): (relPath: string, repoName?: string) => boolean; interface ImpactOptions { direction?: "upstream" | "downstream" | "both"; max_depth?: number; include_tests?: boolean; } /** * Perform blast-radius impact analysis using BFS on the edge graph. */ declare function analyzeImpact(db: Database, symbolId: string, options?: ImpactOptions): ImpactResult | null; /** * Format impact result as markdown. */ declare function formatImpactMarkdown(result: ImpactResult, resolvePath?: (sourceFile: string) => ResolvedPaths): string; interface ShortestPathOptions { max_depth?: number; edge_types?: string[]; edge_weights?: Record; } /** * Find the shortest path between two nodes using weighted Dijkstra. */ declare function findShortestPath(db: Database, fromName: string, toName: string, options?: ShortestPathOptions): PathResult; /** * Format path result as markdown. */ declare function formatPathMarkdown(result: PathResult, resolvePath?: (sourceFile: string) => ResolvedPaths): string; /** * Get complete detail for a node. */ declare function getNodeDetail(db: Database, symbol: string): NodeDetail | null; /** * Get suggestions for a not-found node. */ declare function getNodeSuggestions(db: Database, symbol: string): string[]; /** * Format node detail as markdown. */ declare function formatNodeDetailMarkdown(detail: NodeDetail): string; /** * Auto-detect the path to reponova-out directory. * * Resolution order: * 1. Explicit --graph flag * 2. Env var REPONOVA_GRAPH_PATH * 3. ./reponova-out/ (current directory) * 4. ../{sibling}/reponova-out/ - sibling probe * 5. null (not found) */ declare function resolveGraphPath(explicitPath?: string): string | null; /** * Resolve the graph.json file path within a reponova-out directory. */ declare function resolveGraphJson(graphDir: string): string | null; /** * Resolve the search database path within a reponova-out directory. */ declare function resolveSearchDb(graphDir: string): string | null; /** * Abstract provider contracts for LLM and embedding providers. * * Both local (node-llama-cpp, ONNX) and remote (OpenAI-compatible) * providers implement these interfaces. */ interface LlmCompletionOptions { systemPrompt: string; userPrompt: string; maxTokens?: number; temperature?: number; } /** * Abstract LLM provider contract. * Both local (node-llama-cpp) and remote (OpenAI-compatible) implement this. * * generate() throws on failure with a descriptive message (HTTP error, timeout, * network failure, etc.). Callers should catch and handle/retry as needed. */ interface LlmProvider { readonly isAvailable: boolean; initialize(): Promise; generate(options: LlmCompletionOptions): Promise; dispose(): Promise; } /** * Abstract embedding provider contract. * Both local (ONNX) and remote (OpenAI-compatible) implement this. */ interface EmbeddingProvider { readonly isAvailable: boolean; initialize(): Promise; embedBatch(items: Array<{ id: string; text: string; }>): Promise; dispose(): Promise; } interface EmbeddingResult { id: string; text: string; vector: Float32Array; } declare class EmbeddingEngine { private session; private tokenizer; private modelName; private cacheDir; private downloadOnFirstUse; private available; constructor(modelName: string, cacheDir: string, downloadOnFirstUse?: boolean); initialize(): Promise; embedBatch(items: Array<{ id: string; text: string; }>): Promise; private embedBatchInternal; private meanPool; private l2Normalize; private downloadModel; dispose(): Promise; get isAvailable(): boolean; } interface NodeEmbeddingInput { id: string; label: string; type: string; signature?: string; docstring?: string; bases?: string[]; source_file?: string; } /** * Compose embedding text for a graph node based on its type. * Enriched: includes community summary + node description when available. * Truncated to 512 chars to fit model's effective window. */ declare function composeNodeText(node: NodeEmbeddingInput, communitySummary?: string, nodeDescription?: string): string; interface VectorRecord { id: string; label: string; type: string; repo: string; source_file: string; community: string; text: string; vector: number[]; } interface SimilarityResult { id: string; label: string; type: string; repo: string; source_file: string; community: string; score: number; } interface VectorQueryOptions { top_k?: number; type_filter?: string; repo_filter?: string; } declare class VectorStore { private db; private table; private fallbackData; private useFallback; private dbPath; constructor(outputDir: string); /** * Initialize vector store. Tries LanceDB first, falls back to in-memory brute force. */ initialize(): Promise; /** * Store embeddings with metadata. Passing an empty array clears the store. */ upsert(records: VectorRecord[]): Promise; /** * Find similar vectors by query vector. */ query(queryVector: number[] | Float32Array, options?: VectorQueryOptions): Promise; /** * Load existing vectors from disk (for server-side queries without rebuild). */ loadExisting(): Promise; loadAllRecords(): Promise; private bruteForceSearch; dispose(): Promise; private persistSidecar; private getBaseDir; private getSidecarPath; } /** * Smart Context Builder — assembles token-budgeted, ranked context for any query. * * Algorithm: * 1. ENTRY POINTS: text search + vector search → merge/dedup * 2. GRAPH EXPANSION: 1-2 hop BFS from candidates * 3. RELEVANCE SCORING: similarity + centrality + proximity * 4. TOKEN BUDGET FITTING: greedy fill sections by score * 5. FORMAT OUTPUT: structured JSON or narrative Markdown */ interface ContextParams { query: string; max_tokens?: number; scope?: string; include_source?: boolean; format?: "structured" | "narrative"; } interface ContextResult { query: string; total_tokens: number; max_tokens: number; sections: ContextSection[]; /** Structured format only */ structured?: StructuredContext; } interface ContextSection { type: "candidates" | "relationships" | "community" | "source" | "metadata"; content: string; tokens: number; } declare class ContextBuilder { private db; private graphDir; private vectorStore; private tfidfEngine; private embeddingProvider; private communitySummaries; private communityLabels; private nodeDescriptions; constructor(db: Database, graphDir: string); /** * Initialize optional components (vector store, community summaries). * Non-blocking: works in degraded mode without them. */ initialize(embeddingsConfig?: EmbeddingsConfig, _cacheDir?: string): Promise; /** * Build context for a query within token budget. */ buildContext(params: ContextParams): Promise; /** * Format context result as a single string (for MCP tool output). */ formatAsText(result: ContextResult): string; private findCandidates; private expandGraph; private getSourceSnippet; private formatCandidates; private formatRelationships; private formatCommunities; private formatSource; /** * Dispose resources. */ dispose(): Promise; } declare class ProviderRegistry { private providers; private modelsConfig; private llmPool; private llmProviders; private embeddingProviders; constructor(providers: Record, modelsConfig: ModelsConfig); acquireLlm(providerName?: string): Promise; acquireEmbedding(providerName?: string): Promise; disposeAll(): Promise; private createLlmProvider; private createEmbeddingProvider; private requireProvider; } /** * Core interfaces for the in-process extraction engine. * * Every language extractor produces FileExtraction objects. The graph builder * consumes them to produce a graphology graph. These types are the contract * between language-specific extraction and language-agnostic graph building. */ /** * Declares the file-level graph node. * The extractor produces this — the graph-builder assembles from it mechanically. * This eliminates classification logic from the assembler entirely. */ interface FileNodeDeclaration { /** The kind of the file node — determines graph node `type` */ kind: FileNodeKind; /** Display label (defaults to filename if omitted) */ label?: string; /** First paragraph or summary of the file */ docstring?: string; /** Tags/decorators attached to the file node (e.g., ["plantuml"], ["svg"]) */ tags?: string[]; } /** * Valid kinds for file-level nodes. * Each maps 1:1 to the graph node `type` attribute. * Convention: "module" | "document" | "diagram" | ... any extractor-defined value */ type FileNodeKind = string; /** * A raw extraction from a single source file. * Every language extractor produces this same shape. */ interface FileExtraction { /** Relative file path (normalized with forward slashes) */ filePath: string; /** Language identifier (e.g., "python", "javascript") */ language: string; /** * Declares the file-level graph node. * The extractor MUST provide this — it tells the graph-builder what kind of * node to create for the file itself (module, document, diagram). * The graph-builder uses this mechanically — zero classification logic. */ fileNode: FileNodeDeclaration; /** Extracted symbol nodes (internal contents only — NOT the file itself) */ symbols: SymbolNode[]; /** Import/export declarations */ imports: ImportDeclaration[]; /** Detected calls/references to other symbols */ references: SymbolReference[]; /** * Explicitly exported symbol names, for languages with export semantics. * If undefined, all symbols are considered exported. * Python: derived from __all__ or public names (no _ prefix) */ exports?: string[]; } /** * A symbol defined in a file (function, class, method, variable). */ interface SymbolNode { /** Simple name: "ClassName" or "method_name" */ name: string; /** Qualified name with module path, used for graph node ID generation */ qualifiedName: string; /** Symbol kind */ kind: SymbolKind; /** Function/method signature (if applicable) */ signature?: string; /** Decorators/annotations */ decorators: string[]; /** First line of docstring (if present) */ docstring?: string; /** Start line (1-indexed) */ startLine: number; /** End line (1-indexed) */ endLine: number; /** Parent symbol name (e.g., class name for methods) */ parent?: string; /** Base classes (for class nodes) */ bases?: string[]; } /** * Convention: "function" | "class" | "method" | "variable" | "constant" * | "interface" | "enum" | "module" | "document" | "diagram" * | "section" | "component" | ... any extractor-defined value */ type SymbolKind = string; /** * An import/export declaration. */ interface ImportDeclaration { /** The module being imported from (e.g., "os.path", "./utils", "lodash") */ module: string; /** Specific names imported (e.g., ["join", "dirname"]) */ names: string[]; /** Whether this is a wildcard import (from x import *) */ isWildcard: boolean; /** Whether this is a re-export */ isExport?: boolean; /** Line number (1-indexed) */ line: number; } /** * A reference to another symbol (function call, type annotation, etc.) */ interface SymbolReference { /** Name of the symbol being referenced */ name: string; /** Context: which symbol contains this reference */ fromSymbol: string; /** Edge type to create in the graph — extractor decides, builder uses as-is */ kind: "calls" | "extends" | "references"; /** Line number (1-indexed) */ line: number; } /** * THE CORE INTERFACE that every language extractor must implement. * * To add a new language: * 1. Create src/extract/languages/.ts * 2. Implement this interface * 3. Register in src/extract/languages/registry.ts * * That's it. Everything else (graph building, import resolution, * community detection) works automatically. */ interface LanguageExtractor { /** Language identifier (must match tree-sitter grammar name) */ readonly languageId: string; /** * WASM grammar filename (e.g., "tree-sitter-python.wasm"). * If provided, the pipeline parses with tree-sitter and passes the AST. * If omitted/empty, the extractor receives a null tree and uses sourceCode directly. */ readonly wasmFile?: string; /** * Extract symbols, imports, and references from a source file. * * @param tree - The parsed tree-sitter syntax tree (null if no wasmFile) * @param sourceCode - The raw source code string * @param filePath - Relative file path (for qualified name generation) * @param pluginConfig - Effective plugin configuration for this call. * The pipeline merges the plugin's declared `LanguagePlugin.configDefaults` * with the user's `config.plugins[pluginId]` (from `reponova.yml`), strips * the loader-reserved fields (`package`, `enabled`, `patterns`, `exclude`), * and passes the result. The parameter is optional so plugins built * against earlier RepoNova versions keep working — when omitted, the * extractor should fall back to its own defaults. * @returns FileExtraction with all discovered symbols and relationships */ extract(tree: SyntaxTree | null, sourceCode: string, filePath: string, pluginConfig?: Readonly>): FileExtraction; /** * Resolve an import module path to candidate relative file paths. * * Given an import like `from config.loader import X`, resolve it to * file paths like `config/loader.py` that can be matched against * other extracted files. * * @param importModule - The module path from the import declaration * @param currentFilePath - Path of the file containing the import * @returns Resolved relative file path candidates, or empty array if external */ resolveImportPath(importModule: string, currentFilePath: string): string[]; } /** * Tree-sitter syntax tree (web-tree-sitter WASM interface). * This is the SAME interface already used by the outline module. */ interface SyntaxTree { rootNode: SyntaxNode; } interface SyntaxNode { type: string; text: string; startPosition: { row: number; column: number; }; endPosition: { row: number; column: number; }; children: SyntaxNode[]; childCount: number; namedChildren: SyntaxNode[]; namedChildCount: number; parent: SyntaxNode | null; childForFieldName(name: string): SyntaxNode | null; childrenForFieldName(name: string): SyntaxNode[]; descendantsOfType(type: string | string[]): SyntaxNode[]; } /** * Unified registry of all available language extractors. * * This registry is the SINGLE SOURCE OF TRUTH for language support. * Both the extraction engine and the outline module use it. * * Built-in: only markdown. All other languages are provided by plugins * (`@reponova/lang-*`) discovered at runtime via `discoverLanguagePlugins()`. * * Extensions are passed EXPLICITLY by the caller (loaded from * `package.json.reponova.extensions[]` for plugins, hard-coded for built-ins). * The `LanguageExtractor` interface no longer exposes `extensions` — concrete * extractor classes are free to keep a private field for their own logic * (e.g. import-path resolution), but the routing table below is built solely * from the explicit `extensions` parameter. */ declare function registerExtractor(extractor: LanguageExtractor, extensions: readonly string[]): void; /** * Contract for a language-specific outline generator. * * Each supported language implements this interface, providing: * - A tree-sitter extractor (for when the WASM grammar is available) * - A regex extractor (fallback, always available) * - The WASM filename to look for in grammars/ */ interface LanguageSupport { /** WASM grammar filename (e.g. "tree-sitter-python.wasm") */ readonly wasmFile: string; /** * Extract outline from a tree-sitter AST root node. * * The optional `pluginConfig` argument carries the same merged plugin * configuration the pipeline forwards to `LanguageExtractor.extract()` — * see the JSDoc of that method for the merge rules. Plugins built * against earlier RepoNova versions ignore the extra parameter. */ treeSitterExtract(rootNode: SyntaxNode, filePath: string, lineCount: number, pluginConfig?: Readonly>): FileOutline; /** * Extract outline from raw source using regex (no external deps). * * The optional `pluginConfig` argument follows the same propagation * contract as {@link treeSitterExtract}. */ regexExtract(filePath: string, source: string, lineCount: number, pluginConfig?: Readonly>): FileOutline; } /** * Language registry — maps file extensions to LanguageSupport modules. * * Built-in: none (markdown doesn't have outline support). * All outline languages are provided by plugins discovered at runtime. * * Extensible: call `registerOutlineLanguage()` to add new languages at runtime. */ /** * Register an outline language support module. * * @param language - Language name (e.g., "python", "javascript") * @param extensions - File extensions without dot (e.g., ["py", "pyw"]) * @param support - The LanguageSupport implementation */ declare function registerOutlineLanguage(language: string, extensions: string[], support: LanguageSupport): void; /** * Language plugin contract. * * External packages (`@reponova/lang-*`) export a single `plugin` object * conforming to this interface. Discovery (`discovery.ts`) dynamically imports * each plugin and registers its extractor, outline support, and grammar path. */ interface LanguagePlugin { /** Unique language identifier (e.g. "python", "plantuml") */ readonly id: string; /** Label for file categorization in detected-files.json (default: plugin id) */ readonly fileType?: string; /** Absolute path to a tree-sitter WASM grammar, if needed */ readonly grammarPath?: string; /** Extraction implementation */ readonly extractor: LanguageExtractor; /** Outline support (optional — not all languages have outlines) */ readonly outline?: LanguageSupport; /** * Default values for plugin-specific config properties. * * Two effects at runtime: * * 1. **`reponova lang add` documentation surface** — `addPluginToConfig` * writes these defaults into `reponova.yml` under the plugin's key * so users discover the available knobs without reading the README. * 2. **Effective config delivered to the plugin** — at build time the * loader merges these defaults with the user's `config.plugins[id]` * (user overrides win), strips the loader-reserved fields * (`package`, `enabled`, `patterns`, `exclude`), and passes the * resulting object as the optional `pluginConfig` argument of * {@link LanguageExtractor.extract}, {@link LanguageSupport.treeSitterExtract}, * and {@link LanguageSupport.regexExtract}. * * Plugins are free to declare any keys they want — RepoNova never * inspects the contents. The convention is to use camelCase keys and * primitive / JSON-friendly values so they round-trip cleanly through * `reponova.yml`. */ readonly configDefaults?: Record; } /** Discovered plugin metadata (for `reponova lang list` / `reponova check`). */ interface DiscoveredPlugin { id: string; fileType: string; extensions: string[]; packageName: string; version: string; hasGrammar: boolean; hasOutline: boolean; } /** * Resolve the package name for a plugin config entry. */ declare function resolvePluginPackage(key: string, config: { package?: string; }): string; /** * Load and register plugins declared in `config.plugins`. * Plugins missing `reponova.type` or `reponova.extensions[]` in their * manifest are skipped with a warning — they cannot be safely routed. */ declare function loadDeclaredPlugins(config: Config): Promise; /** * Legacy alias — calls `loadDeclaredPlugins` with an empty config (no plugins). * Used by `tests/setup.ts` when no config is available. * In test mode, falls back to scanning `@reponova/lang-*` in `node_modules`. */ declare function discoverLanguagePlugins(config?: Config): Promise; /** * Get list of discovered plugins (after `loadDeclaredPlugins()` has run). */ declare function getDiscoveredPlugins(): DiscoveredPlugin[]; /** * Register an absolute path for a grammar WASM file. * Called by plugin discovery when a plugin provides a grammarPath. */ declare function registerGrammarPath(wasmFile: string, absolutePath: string): void; /** * Resolve a grammar WASM file to an absolute path. * Returns the plugin-registered path if available, otherwise falls back to `fallbackDir/wasmFile`. */ declare function resolveGrammarPath(wasmFile: string, fallbackDir: string): string; /** * Plugin config registry. * * Stores the per-plugin merged config object — the result of overlaying * the user's `config.plugins[pluginId]` (from `reponova.yml`) on top of * the plugin-declared `LanguagePlugin.configDefaults` — minus the * reserved loader-internal fields (`package`, `enabled`, `patterns`, * `exclude`) which never reach the plugin's business logic. * * This registry is populated by `loadDeclaredPlugins()` after each * plugin is successfully imported, and consumed at runtime by: * - the extraction pipeline before invoking `extract()` * - the outline pipeline before invoking `treeSitterExtract()` / * `regexExtract()` * * Keys: every plugin's effective config is registered under TWO keys — * the plugin id (used by the outline registry) and the extractor's * language id (used by the extraction registry). They usually coincide * but the dual indexing is cheap, future-proof, and removes any * ambiguity about which lookup string a call site must use. */ /** * Compute the effective plugin config by merging the user-provided * overrides on top of the plugin-declared defaults, stripping the * loader-reserved fields. Returns a frozen, plain object — never the * inputs themselves, so callers cannot mutate registry state. * * Reserved fields (`package`, `enabled`, `patterns`, `exclude`) are * consumed by the plugin loader / file-detection pipeline and never * forwarded to the extractor; including them in the per-plugin payload * would only cause name collisions with future plugin-specific keys. */ declare function mergePluginConfig(defaults: Readonly> | undefined, userConfig: Readonly> | undefined): Readonly>; /** * Register a frozen plugin config under one or more lookup keys. * Typical call: `setPluginConfig([plugin.id, plugin.extractor.languageId], cfg)`. * Duplicate keys overwrite (last write wins). */ declare function setPluginConfig(keys: readonly string[], config: Readonly>): void; /** * Look up the effective plugin config for a given language / plugin id. * Returns `undefined` if the key was never registered, so callers can * trivially `?? EMPTY_CONFIG` if they need a defined value. */ declare function getPluginConfig(key: string): Readonly> | undefined; /** * Drop all registered plugin configs. Primarily a test seam — production * code calls this implicitly when a new build re-runs plugin discovery. */ declare function clearPluginConfigs(): void; /** * Snapshot of the registry, keyed by lookup string. Read-only — useful * for diagnostics (e.g. `reponova lang list --verbose`) and for tests * that need to assert on the propagation logic without poking the * private map. */ declare function getAllPluginConfigs(): ReadonlyMap>>; /** Empty frozen config — returned wherever a default value is preferable to `undefined`. */ declare const EMPTY_PLUGIN_CONFIG: Readonly>; /** * Result returned by every phase. */ interface PhaseResult { /** Number of items processed (for logging) */ processed: number; /** If true, the phase decided not to execute (already up-to-date) */ skipped: boolean; /** Reason for skipping (for logging) */ skipReason?: string; } /** * Generic DAG orchestrator — executes phases level-by-level with maximum parallelism. * * The orchestrator knows NOTHING about specific phases. It: * 1. Takes a registry of phases * 2. Builds and validates a DAG * 3. Computes topological levels * 4. Executes level-by-level (phases within a level run in parallel) * 5. Collects results * * Cache logic (check/seal/invalidate) is entirely owned by each phase. * The orchestrator never touches caching — it only sequences and parallelizes. */ interface BuildResult { /** Absolute output directory */ outputDir: string; /** Per-phase results */ phases: Map; /** Total items processed across all phases */ totalProcessed: number; } interface BuildOptions { force?: boolean; target?: string | string[]; startAfter?: string; } /** * Run the full build pipeline (or a subset via --target). * * Programmatic API entry point. */ declare function build(configPath?: string, options?: BuildOptions): Promise; export { type BuildResult, type Config, ContextBuilder, type ContextParams, type ContextResult, type Database, EMPTY_PLUGIN_CONFIG, EmbeddingEngine, type EmbeddingProvider, type EmbeddingsConfig, type EnrichConfig, type FileExtraction, type FileNodeDeclaration, type FileOutline, type GraphCommunity, type GraphData, type GraphEdge, type GraphNode, type ImpactResult, type ImportDeclaration, type LanguageExtractor, type LanguagePlugin, type LanguageSupport, type LlmCompletionOptions, type LlmProvider, type ModelsConfig, type NodeDetail, type PathContext, type PathResult, type ProviderConfig, ProviderRegistry, type ProviderType, type RepoMapping, type SearchResult, type SymbolNode, type SymbolReference, type SyntaxNode, type SyntaxTree, VectorStore, analyzeImpact, build, buildAdjacencyMap, buildNodeMap, buildSkipDirs, clearPluginConfigs, composeNodeText, createPatternMatcher, discoverLanguagePlugins, findShortestPath, formatImpactMarkdown, formatNodeDetailMarkdown, formatPathMarkdown, fuzzyMatchNode, getAllPluginConfigs, getDiscoveredPlugins, getMeta, getNodeDetail, getNodeSuggestions, getPluginConfig, initializeSchema, loadConfig, loadDeclaredPlugins, loadGraphData, mergePluginConfig, openDatabase, populateDatabase, queryAll, queryOne, reconstructRepos, registerExtractor, registerGrammarPath, registerOutlineLanguage, resolveAbsolutePath, resolveGrammarPath, resolveGraphJson, resolveGraphPath, resolveOutlinePath, resolvePluginPackage, resolveSearchDb, saveDatabase, searchNodes, setPluginConfig, startMcpServer };