UNPKG

socraticode

Version:

SocratiCode — MCP server for local codebase indexing, semantic search, and code dependency graphs. All private, all local via Docker.

234 lines • 11.7 kB
import { Lang } from "@ast-grep/napi"; import type { CodeGraph, SymbolEdge, SymbolNode } from "../types.js"; import type { GitRefreshTarget } from "./git-state.js"; import { type GraphChangeSummary, type GraphInputRecord, type GraphInputRecorder, type GraphRebuildDecision } from "./graph-inputs.js"; import { type GodotSymbolResolutionContext } from "./graph-symbol-resolution.js"; import { type RustUseBinding } from "./graph-symbols.js"; export { describeGraphBuilder, describeUnresolvedSymbolEdges, findCircularDependencies, generateMermaidDiagram, getFileDependencies, getGraphStats, isGraphBuilderStale, isImportResolutionLow } from "./graph-analysis.js"; /** Progress details for an in-flight graph build operation */ export interface GraphBuildProgress { startedAt: number; filesTotal: number; filesProcessed: number; /** * Files counted in `filesProcessed` that got no node of their own; an importer * may still have created a placeholder for them. Absent until the first skip. */ filesSkipped?: number; phase: string; error?: string; } /** Summary of a completed graph build operation */ export interface GraphBuildCompleted { completedAt: number; durationMs: number; filesProcessed: number; /** * Files that got no node of their own during this build; an importer may still * have created a placeholder for them. Absent when none were skipped. */ filesSkipped?: number; nodesCreated: number; edgesCreated: number; error?: string; /** * Set when the file-import graph was built and saved but the symbol graph * could not be persisted. That half-failure used to be logged and otherwise * dropped, so the build reported success while `codebase_impact` silently had * nothing to answer with; recording it here is what lets status say so. */ symbolGraphError?: string; } /** Check if a graph build is currently in progress for a project */ export declare function isGraphBuildInProgress(projectPath: string): boolean; /** Get progress details for a graph build currently in progress */ export declare function getGraphBuildProgress(projectPath: string): GraphBuildProgress | null; /** Get the last completed graph build for a project */ export declare function getLastGraphBuildCompleted(projectPath: string): GraphBuildCompleted | null; /** Get all projects currently building a graph */ export declare function getGraphBuildInProgressProjects(): string[]; /** Invalidate graph cache for a project (called by watcher on file changes) */ export declare function invalidateGraphCache(projectPath: string): void; /** A pinned identity can be cached under more than one checkout path; drop them all. */ export declare function invalidateGraphCacheForIdentity(projectId: string): void; /** Get a cached graph, or load from Qdrant, or build one */ export declare function getOrBuildGraph(projectPath: string, extraExtensions?: Set<string>): Promise<CodeGraph>; /** Get a cached or persisted graph without creating one when it is absent. */ export declare function getExistingGraph(projectPath: string): Promise<CodeGraph | null>; /** Options for `rebuildGraph` controlling which layers are rebuilt. */ export interface RebuildGraphOptions { /** Storage identity captured by the enclosing index operation. */ projectId?: string; /** Git refreshes require both graph layers to finish for the observed state. */ target?: GitRefreshTarget; /** Extra file extensions to treat as graph nodes. */ extraExtensions?: Set<string>; /** * When `true`, skip the symbol-graph extraction + persistence step entirely. * The file-import graph is still rebuilt and persisted. The caller is then * expected to update the symbol graph incrementally via * `updateChangedFilesSymbolGraph` from `symbol-graph-incremental.ts`. * Default: `false`. */ skipSymbolGraph?: boolean; } /** * Whether an incremental update has to rebuild the code graph, and why. * * The answer comes from the record the last build left behind — the files it * read and the settings it read them under — never from a predicate describing * which files a graph is built from. There is no such predicate to keep in * step, which is the whole reason the build reports its own inputs. * * Conservative wherever the record cannot settle the question: a project with * no usable record rebuilds once and writes one, and so does any addition, any * settings change, and any recorded input that moved. Only a change proven to * touch nothing the build read is skipped. */ export declare function shouldRebuildGraph(projectPath: string, change: GraphChangeSummary, extraExtensions?: Set<string>, projectId?: string): Promise<GraphRebuildDecision>; /** * The parsers this process actually has, as one hash. * * Read after {@link ensureDynamicLanguages} has run, which every path into the * build and the decision does first, so it describes the process answering * rather than some other one. */ export declare function currentGraphCapabilities(): Promise<string>; /** Force-rebuild, cache, and persist a graph. * If a build is already in progress for this project, returns the existing * in-flight promise (deduplication — same as indexer concurrency guard). * * Backward-compatible: accepts either `extraExtensions` (legacy positional * Set) or a `RebuildGraphOptions` object. */ export declare function rebuildGraph(projectPath: string, optsOrExtras?: Set<string> | RebuildGraphOptions): Promise<CodeGraph>; /** * Wait for any in-flight graph build to finish for a project. * Resolves immediately if no build is in progress. * Swallows errors — the caller typically wants to proceed regardless. */ export declare function awaitGraphBuild(projectPath: string): Promise<void>; /** Remove a persisted code graph from Qdrant and clear cache */ export declare function removeGraph(projectPath: string): Promise<void>; /** Check if a graph exists (in cache or persisted) */ export declare function hasGraph(projectPath: string): Promise<boolean>; /** Get graph metadata for status display */ export declare function getGraphStatus(projectPath: string): Promise<{ lastBuiltAt: string; nodeCount: number; edgeCount: number; /** Import specifiers captured across all files, resolved or not. Absent on * graphs persisted before this field was recorded. */ importCount?: number; /** SocratiCode version that built this graph, which is not necessarily the * one serving it. Absent on graphs persisted before this field was * recorded. */ builtByVersion?: string; cached: boolean; symbol?: { fileCount: number; symbolCount: number; edgeCount: number; unresolvedEdgePct: number; builtAt: number; }; } | null>; /** Snapshot of dynamic-language registration state, for diagnostics. */ export interface DynamicLanguageStatus { loaded: string[]; failed: Array<{ name: string; error: string; }>; } /** Returns which dynamic ast-grep grammars registered successfully and which failed. */ export declare function getDynamicLanguageStatus(): DynamicLanguageStatus; /** * Whether the GDScript tree-sitter parser was successfully registered. * tree-sitter-gdscript ships platform-specific prebuilds; on platforms * without a compatible artifact (e.g. linux-arm64) this stays false so * callers can skip AST processing and use syntax-aware fallback extraction. */ export { gdscriptParserAvailable } from "./parser-availability.js"; export declare function ensureDynamicLanguages(): void; /** * Map a file extension to its ast-grep grammar (or null when none). An * EXTENSION_LANGUAGE_MAP override is resolved through the target language's * canonical extension, so a mapped extension (e.g. `.inc` → php) gets the same * grammar a native file of that language would, keeping symbol extraction and * AST chunking consistent with the language label. */ export declare function getAstGrepLang(ext: string, override?: Map<string, string>): Lang | string | null; /** * Get all source files in a project for graph analysis, with the detected * extension of every extensionless file admitted by content detection. * * Includes files with known AST grammars, mixed Elixir templates handled by their * dedicated parsers, and any extra extensions. Extensionless files are head-read * here to decide admission, and the extension that decision * used is returned in `detectedExts` so the build pass can reuse it instead of * reading the head again. * * `files` is sorted lexicographically. Node documents no readdir ordering, and a * depth-first walk additionally interleaves a directory's contents with the * sibling entries that sort after it — `a/x.ts` is yielded before `a.ts`. * Processing order determines node insertion order, and `buildJvmSuffixMap`'s * first-match-wins tie-break for duplicate class paths reads the set in that * order directly (`buildCsNamespaceMap` and `buildGoModuleInfo` sort their own * filtered views instead). Normalize here rather than leaving it to the traversal. */ export declare function getGraphableFiles(projectPath: string, extraExts?: Set<string>, recorder?: GraphInputRecorder): Promise<{ files: string[]; detectedExts: Map<string, string>; }>; /** * Build a code graph for a project using ast-grep for polyglot support. * Files with extra extensions (no AST grammar) are included as leaf nodes * that can be targets of import edges from other files. * * Also extracts symbols and call sites in the same pass — returned via * `symbolsByFile` / `outgoingCallsByFile` and persisted by `doRebuildGraph`. */ export declare function buildCodeGraph(projectPath: string, extraExtensions?: Set<string>, progress?: GraphBuildProgress): Promise<CodeGraph & { symbolsByFile: Map<string, SymbolNode[]>; outgoingCallsByFile: Map<string, SymbolEdge[]>; autoloadTable?: Map<string, string>; resToRepoPathMap: Map<string, string>; godotContext?: GodotSymbolResolutionContext; inferredTypesByFile: Map<string, Map<string, Array<{ type: string; startLine: number; endLine: number; }>>>; memberAssignmentsByFile: Map<string, Array<{ receiver: string; memberName: string; valueType: string; }>>; /** Rust `use` bindings per file — resolution input only, never persisted. */ rustBindingsByFile: Map<string, RustUseBinding[]>; /** Rust file → its crate's directory prefix. Resolution input only. */ rustCrateRootByFile: Map<string, string>; /** Rust file → every target root its parsed Cargo manifest declares. */ rustCrateRootsByFile: Map<string, string[]>; /** * The call edges whose qualifier is rooted in an inline `mod` — a scope with * no file to name. Resolution leaves them unresolved. Held by identity, so * nothing about it reaches an edge or the store. */ rustInlineScopedCalls: Set<SymbolEdge>; /** * The ids of the Rust symbols declared inside an inline `mod`. A path * anchored at a file's own module cannot reach them, so resolution drops * them from that path's candidates. Resolution input only, never persisted. */ rustInlineDeclaredSymbols: Map<string, string>; /** * Every input this build consumed, for the next incremental update to check * a change against. Reported from the build rather than described beside it, * so no second answer to "does this file contribute to the graph?" exists to * drift from this one. */ graphInputs: GraphInputRecord; }>; //# sourceMappingURL=code-graph.d.ts.map