UNPKG

arela

Version:

AI-powered CTO with multi-agent orchestration, code summarization, visual testing (web + mobile) for blazing fast development.

146 lines 4.86 kB
import fs from "fs-extra"; import path from "path"; import { ASTExtractor } from "./extractor/ast-extractor.js"; import { LLMSynthesizer } from "./synthesizer/llm-synthesizer.js"; import { SemanticCache } from "./cache/semantic-cache.js"; /** * Main orchestrator for code summarization pipeline. * * Pipeline: * 1. Extract semantic contract (AST) * 2. Check cache * 3. Synthesize summary (LLM) if cache miss * 4. Store in cache */ export class CodeSummarizer { extractor; synthesizer; cache; projectPath; silent; constructor(projectPath, options = {}) { this.projectPath = projectPath; this.silent = options.silent ?? false; this.extractor = new ASTExtractor(); this.synthesizer = new LLMSynthesizer(); this.cache = new SemanticCache(projectPath, { cacheDir: options.cacheDir, logger: this.silent ? () => { } : undefined, }); } /** * Summarize a code file (with caching) */ async summarize(filePath, options = {}) { const absolutePath = path.isAbsolute(filePath) ? filePath : path.join(this.projectPath, filePath); if (!(await fs.pathExists(absolutePath))) { throw new Error(`File not found: ${filePath}`); } const code = await fs.readFile(absolutePath, "utf-8"); // Stage 1: Extract semantic contract (AST) if (!this.silent) { console.log(`📄 Extracting semantic contract from ${filePath}...`); } const contract = await this.extractor.extract(code, filePath); // Check cache first (unless noCache option) if (!options.noCache) { const cached = await this.cache.get(contract); if (cached) { if (!this.silent) { console.log(`✅ Cache hit! Using cached summary.`); } return cached; } } // Stage 2: Synthesize summary (LLM) if (!this.silent) { console.log(`🤖 Synthesizing summary with LLM...`); } const summary = await this.synthesizer.synthesize(contract); // Store in cache if (!options.noCache) { await this.cache.set(contract, summary); } return summary; } /** * Summarize multiple files in parallel */ async summarizeBatch(filePaths, options = {}) { if (!this.silent) { console.log(`📚 Summarizing ${filePaths.length} files...`); } const results = await Promise.all(filePaths.map(async (filePath, index) => { if (!this.silent) { console.log(` [${index + 1}/${filePaths.length}] ${filePath}`); } try { const summary = await this.summarize(filePath, options); return [filePath, summary]; } catch (error) { if (!this.silent) { console.error(` ❌ Failed to summarize ${filePath}:`, error); } throw error; } })); return new Map(results); } /** * Get cache statistics */ getCacheStats() { return this.cache.getStats(); } /** * Clean up expired cache entries */ async cleanupCache() { return await this.cache.cleanup(); } } /** * Convert TechnicalSummary to Markdown format */ export function summaryToMarkdown(summary) { const lines = []; lines.push(`# ${summary.filePath}`); lines.push(""); lines.push(`## Main Responsibility`); lines.push(summary.mainResponsibility); lines.push(""); if (summary.publicAPI.length > 0) { lines.push(`## Public API`); summary.publicAPI.forEach((api) => { lines.push(`- \`${api}\``); }); lines.push(""); } if (summary.ioContracts.length > 0) { lines.push(`## I/O Contracts`); summary.ioContracts.forEach((contract) => { lines.push(`- **${contract.name}:** \`${contract.definition}\``); }); lines.push(""); } lines.push(`## Dependencies`); lines.push(summary.dependencies); lines.push(""); lines.push(`## Side Effects`); lines.push(summary.sideEffects); lines.push(""); if (summary.keyAlgorithms) { lines.push(`## Key Algorithms`); lines.push(summary.keyAlgorithms); lines.push(""); } lines.push(`## Metadata`); lines.push(`- **Token Count:** ${summary.metadata.tokenCount}`); lines.push(`- **Compression Ratio:** ${summary.metadata.compressionRatio.toFixed(2)}x`); lines.push(`- **Synthesized At:** ${new Date(summary.metadata.synthesizedAt).toLocaleString()}`); return lines.join("\n"); } //# sourceMappingURL=code-summarizer.js.map