| |
| |
| |
| |
| |
|
|
|
|
| interface FileSummary {
|
| path: string
|
| summary: string
|
| functions: string[]
|
| imports: string[]
|
| exports: string[]
|
| lastAnalyzed: number
|
| hash: string
|
| }
|
|
|
| |
| |
|
|
| function hashContent(content: string): string {
|
| let hash = 0
|
| for (let i = 0; i < content.length; i++) {
|
| hash = ((hash << 5) - hash + content.charCodeAt(i)) | 0
|
| }
|
| return Math.abs(hash).toString(36)
|
| }
|
|
|
| |
| |
|
|
| function extractSummary(
|
| content: string,
|
| language: string = 'typescript'
|
| ): string {
|
| const lines = content.split('\n')
|
| const summary: string[] = []
|
|
|
|
|
| const functionRegex =
|
| /(?:function|const|let|var)\s+(\w+)\s*(?:=\s*(?:async\s*)?\(|\()/
|
| const functions: string[] = []
|
|
|
|
|
| const importRegex = /import\s+.*from\s+['"]([^'"]+)['"]/
|
| const imports: string[] = []
|
|
|
|
|
| const exportRegex = /export\s+(?:const|function|class|interface)\s+(\w+)/
|
| const exports: string[] = []
|
|
|
| for (const line of lines) {
|
| const funcMatch = line.match(functionRegex)
|
| if (funcMatch) functions.push(funcMatch[1])
|
|
|
| const importMatch = line.match(importRegex)
|
| if (importMatch) imports.push(importMatch[1])
|
|
|
| const exportMatch = line.match(exportRegex)
|
| if (exportMatch) exports.push(exportMatch[1])
|
| }
|
|
|
|
|
| if (functions.length > 0) {
|
| summary.push(`Functions: ${functions.slice(0, 5).join(', ')}`)
|
| }
|
| if (imports.length > 0) {
|
| summary.push(`Imports: ${imports.slice(0, 5).join(', ')}`)
|
| }
|
| if (exports.length > 0) {
|
| summary.push(`Exports: ${exports.join(', ')}`)
|
| }
|
|
|
|
|
| if (content.includes('useState') || content.includes('useEffect')) {
|
| summary.push('React hooks detected')
|
| }
|
| if (content.includes('async') || content.includes('await')) {
|
| summary.push('Async operations')
|
| }
|
| if (content.includes('fetch') || content.includes('axios')) {
|
| summary.push('HTTP requests')
|
| }
|
|
|
| return summary.join(' | ')
|
| }
|
|
|
| |
| |
|
|
| class FileCache {
|
| private cache: Map<string, FileSummary> = new Map()
|
| private readonly TTL_MS = 7 * 24 * 60 * 60 * 1000
|
| private readonly MAX_CACHE_SIZE = 1000
|
|
|
| |
| |
|
|
| get(path: string, content: string): FileSummary | null {
|
| const cached = this.cache.get(path)
|
| if (!cached) return null
|
|
|
|
|
| if (Date.now() - cached.lastAnalyzed > this.TTL_MS) {
|
| this.cache.delete(path)
|
| return null
|
| }
|
|
|
|
|
| const currentHash = hashContent(content)
|
| if (cached.hash !== currentHash) {
|
| this.cache.delete(path)
|
| return null
|
| }
|
|
|
| return cached
|
| }
|
|
|
| |
| |
|
|
| set(
|
| path: string,
|
| content: string,
|
| language: string = 'typescript'
|
| ): FileSummary {
|
| const summary = extractSummary(content, language)
|
| const lines = content.split('\n')
|
|
|
| const functions: string[] = []
|
| const imports: string[] = []
|
| const exports: string[] = []
|
|
|
| for (const line of lines) {
|
| const funcMatch = line.match(
|
| /(?:function|const|let|var)\s+(\w+)\s*(?:=\s*(?:async\s*)?\(|\()/
|
| )
|
| if (funcMatch) functions.push(funcMatch[1])
|
|
|
| const importMatch = line.match(/import\s+.*from\s+['"]([^'"]+)['"]/)
|
| if (importMatch) imports.push(importMatch[1])
|
|
|
| const exportMatch = line.match(
|
| /export\s+(?:const|function|class|interface)\s+(\w+)/
|
| )
|
| if (exportMatch) exports.push(exportMatch[1])
|
| }
|
|
|
| const fileSummary: FileSummary = {
|
| path,
|
| summary,
|
| functions,
|
| imports,
|
| exports,
|
| lastAnalyzed: Date.now(),
|
| hash: hashContent(content)
|
| }
|
|
|
| this.cache.set(path, fileSummary)
|
|
|
|
|
| if (this.cache.size > this.MAX_CACHE_SIZE) {
|
| this.evictOld()
|
| }
|
|
|
| return fileSummary
|
| }
|
|
|
| |
| |
|
|
| private evictOld(): void {
|
| const entries = Array.from(this.cache.entries())
|
| entries.sort((a, b) => a[1].lastAnalyzed - b[1].lastAnalyzed)
|
|
|
|
|
| const toRemove = Math.floor(entries.length * 0.1)
|
| for (let i = 0; i < toRemove; i++) {
|
| this.cache.delete(entries[i][0])
|
| }
|
| }
|
|
|
| |
| |
|
|
| clear(): void {
|
| this.cache.clear()
|
| }
|
|
|
| |
| |
|
|
| getStats(): {size: number; hitRate?: number} {
|
| return {size: this.cache.size}
|
| }
|
|
|
| |
| |
|
|
| has(path: string): boolean {
|
| return this.cache.has(path)
|
| }
|
| }
|
|
|
|
|
| const fileCache = new FileCache()
|
|
|
| |
| |
|
|
| export function getFileSummary(
|
| path: string,
|
| content: string,
|
| language: string = 'typescript'
|
| ): FileSummary | null {
|
| const cached = fileCache.get(path, content)
|
| if (cached) {
|
| return cached
|
| }
|
|
|
|
|
| return fileCache.set(path, content, language)
|
| }
|
|
|
| |
| |
|
|
| export function getFileSummaryString(
|
| path: string,
|
| content: string,
|
| language: string = 'typescript'
|
| ): string {
|
| const summary = getFileSummary(path, content, language)
|
| if (!summary) {
|
| return extractSummary(content, language)
|
| }
|
| return summary.summary
|
| }
|
|
|
| |
| |
|
|
| export function clearFileCache(): void {
|
| fileCache.clear()
|
| }
|
|
|
| |
| |
|
|
| export function getFileCacheStats(): {size: number} {
|
| return fileCache.getStats()
|
| }
|
|
|
| |
| |
|
|
| export function isFileCached(path: string): boolean {
|
| return fileCache.has(path)
|
| }
|
|
|