/** * Shared utilities for MCP tool handlers. */ import type { LLMContext } from '../../analyzer/artifact-generator.js'; import { EdgeStore } from '../edge-store.js'; /** LLMContext with optional SQLite edge store attached (present when call-graph.db exists). */ export type CachedContext = LLMContext & { edgeStore?: EdgeStore; }; /** * Resolve and validate a user-supplied directory path. * * Ensures the path resolves to an existing directory, which prevents path * traversal attacks where a client supplies `"../../../../etc"` or a plain * file path instead of a project directory. */ export declare function validateDirectory(directory: string, maxDepth?: number): Promise; export declare function validateDirectoryImpl(directory: string, maxDepth?: number): Promise; export declare function validateDirectoryDepth(absDir: string, maxDepth: number): void; /** * Strip common API key and token patterns from an error message before * returning it to MCP clients, to prevent secret leakage via error responses. * * @param err - The error to sanitize * @param format - Output format: "string" (default) or "json" * @returns Sanitized error as string or {message, code} object when format is "json" */ export declare function sanitizeMcpError(err: unknown, format?: 'string' | 'json'): string | { message: string; code: number; }; /** * Resolve a user-supplied relative file path against a validated project root and * ensure the result stays within that root — by BOTH a lexical check (cheap, blocks * `../` traversal) AND a canonical, symlink-resolved check (mcp-security: * Symlink-Aware Path Confinement). The canonical check defeats an in-root symlink * that points outside the root: confinement is enforced on the real path of the * target where it exists, and on the real path of its nearest existing ancestor * where it does not (so a not-yet-created write target is confined too). */ export declare function safeJoin(absDir: string, filePath: string): string; /** * Bound a free-text query/description argument before it drives an embedding call * or BM25 tokenization (mcp-security: Bounded Computation — a hostile caller could * otherwise send a multi-megabyte string and force unbounded work or a huge * provider request). Returns an `{ error }` object to return verbatim when the * input exceeds MAX_QUERY_LENGTH, or null when it is within bounds. */ export declare function queryTooLongError(query: unknown, field?: string): { error: string; } | null; /** * Resolve the project's openspec directory, confined to the validated root. * * `config.openspecPath` is read from `.openlore/config.json` — an untrusted on-disk * artifact (mcp-security threat model). A poisoned value (`../../etc`, an absolute * escape) must not redirect the reads/writes that derive from it (spec/manifest * reads, decision ADR reads, decision sync writes) outside the project root. We * confine via safeJoin; a value that escapes the root falls back to the default * `openspec/` dir — a legitimate in-root path (default or custom) passes through * unchanged, so only an escaping value is neutralized. */ export declare function safeOpenspecDir(absRoot: string, configuredPath: string | undefined): string; /** Test-only: clear in-memory context cache to force cold path. */ export declare function _resetContextCacheForTesting(): void; /** * Watch-mode handoff (Spec 13.1). Push an updated context into the in-memory * read cache so the next tool call is a cache HIT — no 2.1 MB disk re-parse — * even though the watcher only patched a few signatures. Keyed identically to * {@link readCachedContext} (resolved project directory). * * The cached `mtime` is set to the current on-disk `llm-context.json` mtime so * the entry stays valid until the file genuinely changes on disk again: * • watcher patches in memory but defers the disk write → disk mtime is * unchanged → this entry matches → hit returns the patched context; * • watcher writes the file then primes → disk mtime is the just-written one * → this entry matches → hit, no cold re-parse of what we just wrote; * • some other process (e.g. `openlore analyze`) rewrites the file → its mtime * differs from this entry → next read MISSes and re-reads disk → correct. */ export declare function primeContextCache(directory: string, ctx: CachedContext): Promise; export declare function readCachedContext(directory: string, timeout?: number): Promise; /** * Wait for graph rebuild to complete after schema mismatch. * * When a schema version change is detected, EdgeStore resets itself and * McpWatcher spawns a background `openlore analyze --force`. This helper * polls until the rebuild completes (edgeStore is populated) or timeout. * * Used by graph tools (analyze_impact, trace_execution_path) to auto-heal * after version upgrades instead of failing immediately. * * @returns true if rebuild completed (edgeStore now available), false on timeout */ export declare function waitForGraphRebuild(directory: string, timeoutMs?: number): Promise; /** Compute a SHA-256 fingerprint of all source file mtimes+sizes under rootDir. */ export declare function computeProjectFingerprint(rootDir: string): Promise; /** * Returns true if the cached analysis matches the current source files. * Uses content-hash fingerprint when available; falls back to TTL check. */ export declare function isCacheFresh(directory: string): Promise; export interface MappingEntry { requirement: string; service: string; domain: string; specFile: string; functions: Array<{ name: string; file: string; line: number; kind: string; confidence: string; }>; } export interface MappingIndex { /** filePath → list of mapping entries that reference it */ byFile: Map; /** domain → list of mapping entries for that domain */ byDomain: Map; entries: MappingEntry[]; } /** Load and index mapping.json for bidirectional lookup. Returns null if not found. */ export declare function loadMappingIndex(absDir: string, retryCount?: number): Promise; /** Clear the mapping cache. Useful for tests to reset state. */ export declare function clearMappingCache(): void; /** Summarise which specs cover a given file path (for search_code enrichment). */ export declare function specsForFile(index: MappingIndex, filePath: string): Array<{ requirement: string; domain: string; specFile: string; }>; /** Return functions that implement a given domain/specFile (for search_specs enrichment). */ export declare function functionsForDomain(index: MappingIndex, domain: string): Array<{ name: string; file: string; line: number; kind: string; confidence: string; requirement: string; }>; //# sourceMappingURL=utils.d.ts.map