/** * Analysis Artifact Generator * * Takes all analysis results and generates structured output files * that will be consumed by the LLM generation phase and optionally by humans. */ import { CfgSpill } from './cfg-spill.js'; import { type StyleFingerprint } from './style-fingerprint.js'; import { type ParseHealthReport } from './parse-health.js'; import { type MemoryDegradation } from './memory-strategy.js'; import type { RepositoryMap } from './repository-mapper.js'; import type { DependencyGraphResult } from './dependency-graph.js'; import type { UIComponent } from './ui-component-extractor.js'; import type { SchemaTable } from './schema-extractor.js'; import type { RouteInventory } from './http-route-parser.js'; import type { MiddlewareEntry } from './middleware-extractor.js'; import type { EnvVar } from './env-extractor.js'; export { isTestFile } from './test-file.js'; /** * Architecture layer information */ export interface ArchitectureLayer { name: string; purpose: string; files: string[]; representativeFile: string | null; } /** * Detected domain (maps to OpenSpec spec) */ export interface DetectedDomain { name: string; suggestedSpecPath: string; files: string[]; entities: string[]; keyFile: string | null; } /** * Entry point information */ export interface EntryPointInfo { file: string; type: 'application-entry' | 'api-entry' | 'test-entry' | 'build-entry'; initializes: string[]; } /** * Data flow information */ export interface DataFlowInfo { sources: string[]; sinks: string[]; transformers: string[]; } /** * Key files by category */ export interface KeyFiles { schemas: string[]; config: string[]; auth: string[]; database: string[]; routes: string[]; services: string[]; } /** * Repository structure (JSON artifact) */ export interface RepoStructure { projectName: string; projectType: string; frameworks: string[]; architecture: { pattern: 'layered' | 'modular' | 'microservices' | 'monolith' | 'unknown'; layers: ArchitectureLayer[]; }; domains: DetectedDomain[]; entryPoints: EntryPointInfo[]; dataFlow: DataFlowInfo; keyFiles: KeyFiles; /** Detected UI components (React, Vue, Svelte, Angular) */ uiComponents: UIComponent[]; /** Detected database schema tables */ schemas: SchemaTable[]; /** Aggregated HTTP route inventory */ routeInventory: RouteInventory; /** Detected middleware entries */ middleware: MiddlewareEntry[]; /** Detected environment variables */ envVars: EnvVar[]; statistics: { totalFiles: number; analyzedFiles: number; skippedFiles: number; avgFileScore: number; nodeCount: number; edgeCount: number; cycleCount: number; clusterCount: number; }; } /** * LLM context phase */ export interface LLMContextPhase { purpose: string; files: Array<{ path: string; content?: string; tokens: number; }>; totalTokens?: number; estimatedTokens?: number; } /** * LLM context preparation */ export interface LLMContext { phase1_survey: LLMContextPhase; phase2_deep: LLMContextPhase; phase3_validation: LLMContextPhase; /** Compact signatures for ALL analyzed files — used by Stage 1 instead of bare file paths */ signatures?: import('./signature-extractor.js').FileSignatureMap[]; /** Static call graph: function→function relationships across all TS/Python files */ callGraph?: import('./call-graph.js').SerializedCallGraph; /** * Per-function CFG + reaching-definitions overlay (spec: * add-intraprocedural-cfg-dataflow-overlay). Transient: written to the SQLite * store but STRIPPED before llm-context.json is persisted, so it never enters * the always-resident graph or the hot cache. */ cfgs?: Array<{ functionId: string; filePath: string; cfg: import('./cfg.js').FunctionCfg; }>; } /** * All generated artifacts */ export interface AnalysisArtifacts { repoStructure: RepoStructure; summaryMarkdown: string; dependencyDiagram: string; llmContext: LLMContext; /** * Descriptive per-language idiom profile (change: add-codebase-style-fingerprint), computed in * the call-graph AST walk and rolled up to repo/region/file. Absent when no supported language * is present (fail-soft). Persisted as its own `style-fingerprint.json` to keep the hot * llm-context.json lean. */ styleFingerprint?: StyleFingerprint; /** * Per-file parse health (change: add-parse-health-boundary-disclosure): the files where * extraction silently under-produced (tree-sitter ERROR/MISSING regions, a swallowed parse * failure, or a lossy encoding decode), rolled up per language. Absent on a clean repo (no * artifact written), so a healthy repo pays zero. Persisted as its own `parse-health.json`. */ parseHealth?: ParseHealthReport; /** * A one-line note about the Pass-1 extraction lane, present ONLY when something degraded * (change: optimize-parallel-extraction-pool) — a worker failed, or the worker pool could * not be used at all. It describes HOW the facts were computed, never WHAT they are. * Returned rather than logged * because `build()` also runs inside `openlore mcp`, whose stdout is the JSON-RPC channel; * only the CLI renders it. */ extractionLaneNote?: string; /** * A one-line note naming how many files reused memoized Pass-1 facts, how many were * re-extracted, and — when nothing was reused — why (change: optimize-hash-keyed-analyze). * Set whenever a memo was consulted; unlike the lane note this is not a degradation report * but the standing disclosure that keeps the reused lane from being silent. * * RETURNED, never logged, for the same stdout reason as {@link extractionLaneNote}: this * code path also runs inside the stdio MCP server. Today only the CLI epilogue renders it, * so an embedded caller that wants the disclosure must read it from here — exactly as with * {@link extractionLaneNote}. */ pass1CacheNote?: string; /** * What the graceful-degradation ladder shed under memory pressure, if anything (change: * make-analyze-scale-to-any-repo). Undefined at full fidelity. Also recorded inside * {@link parseHealth} for persistence; surfaced here too so a caller renders the one-line CLI * disclosure without re-reading the artifact — the same pattern as {@link extractionLaneNote}. */ memoryDegradation?: MemoryDegradation; } /** Pass-1 memo rows to persist, plus the live path set the memo is pruned against. */ interface Pass1MemoWrite { stamp: string; rows: Array<{ filePath: string; contentHash: string; facts: string; }>; analyzedPaths: string[]; } /** * Optional enrichment data produced by new extractors, passed into generate(). */ export interface EnrichmentData { uiComponents?: UIComponent[]; schemas?: SchemaTable[]; routeInventory?: RouteInventory; middleware?: MiddlewareEntry[]; envVars?: EnvVar[]; } /** * Options for artifact generation */ export interface ArtifactGeneratorOptions { /** Root directory of the project */ rootDir: string; /** Output directory for artifacts */ outputDir: string; /** Maximum files to include in LLM deep analysis */ maxDeepAnalysisFiles?: number; /** Maximum files for validation phase */ maxValidationFiles?: number; /** Approximate tokens per character for estimation */ tokensPerChar?: number; /** * Re-extract every file instead of reusing memoized Pass-1 facts, then repopulate the memo * (change: optimize-hash-keyed-analyze). The reference output the reused lane is verified * against. * * Deliberately NOT called `force`. "Force" already means "do not skip this run" to every * caller that has one, and most of those callers — a daemon rebuilding after an edit batch, * a watcher healing a stale store — want exactly the re-analysis and none of the re-parsing. * Conflating the two would have removed the benefit from precisely the incremental workload * this exists for. `analyze --force` on the command line sets both, because a human typing * it is asking to trust nothing. */ reExtract?: boolean; } /** * Convert a serialised RepoStructure (from repo-structure.json on disk) back * to a minimal RepositoryMap-compatible object. Only the fields that * consumers of the cached-analysis path actually use are populated; the * file-level arrays (`allFiles`, `highValueFiles`, etc.) are left empty * because the original per-file data is not persisted to disk. */ export declare function repoStructureToRepoMap(rs: RepoStructure): RepositoryMap; /** * Generates analysis artifacts from repository map and dependency graph */ export declare class AnalysisArtifactGenerator { private options; /** Style fingerprint computed during the last generateLLMContext (call-graph walk). */ private _styleFingerprint?; /** Parse-health report computed during the last generateLLMContext (call-graph walk). */ private _parseHealth?; /** * What the graceful-degradation ladder shed on the last build under memory pressure, if anything * (change: make-analyze-scale-to-any-repo). Undefined at full fidelity. Also folded into * `_parseHealth` for persistence; kept here so callers can render the one-line CLI disclosure * without re-reading the artifact. */ private _memoryDegradation?; /** Pass-1 extraction-lane degradation note from the last generateLLMContext, if any. */ private _extractionLaneNote?; /** * Pass-1 memo rows produced by the last generateLLMContext, plus the paths that were * analyzed (so rows for deleted files can be pruned). Persisted by generateAndSave through * the same store handle that rebuilds the graph (change: optimize-hash-keyed-analyze). */ private _pass1Memo?; /** Off-heap overlay hand-off for this build; drained into `cfg_overlay` after `clearAll()`. */ private _cfgSpill; /** Files reused vs. re-extracted on the last build — surfaced by the analyze summary. */ private _pass1CacheNote?; constructor(options: ArtifactGeneratorOptions); /** * Generate all artifacts */ generate(repoMap: RepositoryMap, depGraph: DependencyGraphResult, enrichment?: EnrichmentData): Promise; /** * Generate and save all artifacts to disk */ generateAndSave(repoMap: RepositoryMap, depGraph: DependencyGraphResult, enrichment?: EnrichmentData): Promise; /** * Generate repo-structure.json */ private generateRepoStructure; /** * Format project type for display */ private formatProjectType; /** * Detect architecture pattern from code structure */ private detectArchitecturePattern; /** * Generate architecture layers */ private generateArchitectureLayers; /** * Generate domains from clusters */ private generateDomains; /** * Normalize domain name for OpenSpec path */ private normalizeDomainName; /** * Extract potential entity names from files */ private extractEntities; /** * Generate entry points information */ private generateEntryPoints; /** * Generate data flow information */ private generateDataFlow; /** * Generate key files by category */ private generateKeyFiles; /** * Generate SUMMARY.md */ private generateSummaryMarkdown; /** * Format project type for human reading */ private formatProjectTypeReadable; /** * Generate dependency diagram in Mermaid format */ private generateDependencyDiagram; /** * Generate LLM context preparation */ private generateLLMContext; /** * Open the Pass-1 fact memo for one build (change: optimize-hash-keyed-analyze), or * `undefined` when there is nothing to memoize against and nothing to gain. * * Every failure mode here degrades to "extract everything", which is exactly today's * behavior — the memo is an optimization and is never allowed to be the reason a build * fails or answers differently. But each mode NAMES itself (`noReuseReason`) so the * epilogue can tell an operator who asked for a full re-extraction apart from one whose * memo is quietly unavailable. Even a bypassed memo still buffers writes, so a forced run * REPOPULATES it rather than leaving the next run to pay full price. */ private openPass1Memo; } /** * Writes the full call graph (nodes, edges, classes, inheritance) to SQLite. * Full rebuild on every analyze — incremental updates handled by the watcher. * Additive alongside llm-context.json; backward compat preserved. */ export declare function writeEdgesToSQLite(callGraph: import('./call-graph.js').SerializedCallGraph, dbPath: string, rootPath?: string, cfgs?: Array<{ functionId: string; filePath: string; cfg: import('./cfg.js').FunctionCfg; }>, pass1Memo?: Pass1MemoWrite, cfgSpill?: CfgSpill): Promise; /** * Generate all artifacts */ export declare function generateArtifacts(repoMap: RepositoryMap, depGraph: DependencyGraphResult, options: ArtifactGeneratorOptions): Promise; /** * Generate and save all artifacts */ export declare function generateAndSaveArtifacts(repoMap: RepositoryMap, depGraph: DependencyGraphResult, options: ArtifactGeneratorOptions): Promise; //# sourceMappingURL=artifact-generator.d.ts.map