@triedotdev/mcp 1.0.148 → 1.0.151
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +225 -78
- package/dist/{autonomy-config-ZCOSTMPD.js → autonomy-config-3APNC6QF.js} +3 -3
- package/dist/{chat-store-OJLJCJFI.js → chat-store-HMTDL7I2.js} +3 -3
- package/dist/{chunk-LD7ZEFNY.js → chunk-3KZBC3RJ.js} +2 -2
- package/dist/{chunk-SH7H3WRU.js → chunk-4TQQP7JD.js} +3 -3
- package/dist/{chunk-SH7H3WRU.js.map → chunk-4TQQP7JD.js.map} +1 -1
- package/dist/{chunk-N2EDZTKG.js → chunk-4UDBGYI3.js} +13 -13
- package/dist/{chunk-XTTZAQWJ.js → chunk-74R4XSFB.js} +5 -5
- package/dist/{chunk-T6PS3MXJ.js → chunk-7HYOJ4Q7.js} +8 -8
- package/dist/{chunk-T6PS3MXJ.js.map → chunk-7HYOJ4Q7.js.map} +1 -1
- package/dist/{chunk-3MUCUZ46.js → chunk-ABY2R7OK.js} +3 -3
- package/dist/chunk-ED7PLRQA.js +782 -0
- package/dist/chunk-ED7PLRQA.js.map +1 -0
- package/dist/{chunk-4MJ52WBH.js → chunk-EFWVF6TI.js} +4 -2
- package/dist/chunk-EFWVF6TI.js.map +1 -0
- package/dist/{chunk-23RJT5WT.js → chunk-F7BMFOZ6.js} +2 -2
- package/dist/{chunk-4PAAGLKO.js → chunk-G7Q23IGF.js} +36 -9
- package/dist/{chunk-4PAAGLKO.js.map → chunk-G7Q23IGF.js.map} +1 -1
- package/dist/{chunk-LT6VUZG2.js → chunk-GLY76TSI.js} +3 -3
- package/dist/{chunk-WMDFK7LI.js → chunk-IFBEAOHH.js} +5 -5
- package/dist/{chunk-YEIJW6X6.js → chunk-JIS2OCZR.js} +4 -4
- package/dist/{chunk-FG467PDD.js → chunk-LNUMECBJ.js} +2 -2
- package/dist/{chunk-FPEMP54L.js → chunk-OJXFQRUE.js} +2 -2
- package/dist/{chunk-5KJ4UJOY.js → chunk-QQG42HCI.js} +2 -2
- package/dist/{chunk-7OJ6JIPL.js → chunk-REHKDCI6.js} +7 -150
- package/dist/chunk-REHKDCI6.js.map +1 -0
- package/dist/{chunk-J7CEBSEB.js → chunk-SU3WCAC4.js} +102 -163
- package/dist/chunk-SU3WCAC4.js.map +1 -0
- package/dist/{chunk-4C67GV3O.js → chunk-TCNCNWGV.js} +2 -2
- package/dist/{chunk-NKHO34UZ.js → chunk-TU7D5DEW.js} +2 -2
- package/dist/{chunk-62POBLFC.js → chunk-TWPX6PHF.js} +453 -986
- package/dist/chunk-TWPX6PHF.js.map +1 -0
- package/dist/{chunk-FH335WL5.js → chunk-TWQPOVRA.js} +2 -2
- package/dist/{chunk-YOJGSRZK.js → chunk-V3O7C2LY.js} +2 -2
- package/dist/{chunk-V7AY2EJO.js → chunk-WOTLY5NA.js} +2 -2
- package/dist/cli/create-agent.js +8 -40
- package/dist/cli/create-agent.js.map +1 -1
- package/dist/cli/main.js +121 -164
- package/dist/cli/main.js.map +1 -1
- package/dist/cli/yolo-daemon.js +31 -155
- package/dist/cli/yolo-daemon.js.map +1 -1
- package/dist/{client-INNE2GGZ.js → client-5L64D5SQ.js} +3 -3
- package/dist/{codebase-index-FMIULFZQ.js → codebase-index-OOE7OAHP.js} +3 -3
- package/dist/{fast-analyzer-MWKCDRGD.js → fast-analyzer-FMU3X4AZ.js} +5 -5
- package/dist/github-ingester-C66ZRUYC.js +11 -0
- package/dist/{goal-manager-ZBWKWEML.js → goal-manager-VTBFFYN4.js} +8 -7
- package/dist/{goal-validator-DA3JQ6JN.js → goal-validator-EM5XVWVC.js} +7 -6
- package/dist/{graph-J4OGTYCO.js → graph-26JPZ3DF.js} +3 -3
- package/dist/{hypothesis-JCUMZKTG.js → hypothesis-4UPE7KXU.js} +8 -7
- package/dist/{incident-index-BWW2UEY7.js → incident-index-H6APJ4S3.js} +3 -3
- package/dist/index.js +116 -397
- package/dist/index.js.map +1 -1
- package/dist/{insight-store-A5XXMFD6.js → insight-store-QEEUQR5L.js} +4 -4
- package/dist/{issue-store-LZWZIGM7.js → issue-store-C6XYENE5.js} +7 -4
- package/dist/ledger-VNA4DX3Z.js +51 -0
- package/dist/linear-ingester-WIUBWF55.js +11 -0
- package/dist/{tiered-storage-VZL7KK64.js → tiered-storage-P6Z3NV2Q.js} +3 -3
- package/dist/trie-agent-GJJJCL6P.js +27 -0
- package/dist/trie-agent-GJJJCL6P.js.map +1 -0
- package/package.json +8 -6
- package/dist/chunk-4MJ52WBH.js.map +0 -1
- package/dist/chunk-62POBLFC.js.map +0 -1
- package/dist/chunk-7OJ6JIPL.js.map +0 -1
- package/dist/chunk-G76DYVGX.js +0 -136
- package/dist/chunk-G76DYVGX.js.map +0 -1
- package/dist/chunk-J7CEBSEB.js.map +0 -1
- package/dist/comprehension-46F7ZNKL.js +0 -821
- package/dist/comprehension-46F7ZNKL.js.map +0 -1
- package/dist/github-ingester-J2ZFYXVE.js +0 -11
- package/dist/linear-ingester-JRDQAIAA.js +0 -11
- package/dist/trie-agent-6A7YBNTQ.js +0 -26
- package/dist/workers/agent-worker.d.ts +0 -2
- package/dist/workers/agent-worker.js +0 -28
- package/dist/workers/agent-worker.js.map +0 -1
- /package/dist/{autonomy-config-ZCOSTMPD.js.map → autonomy-config-3APNC6QF.js.map} +0 -0
- /package/dist/{chat-store-OJLJCJFI.js.map → chat-store-HMTDL7I2.js.map} +0 -0
- /package/dist/{chunk-LD7ZEFNY.js.map → chunk-3KZBC3RJ.js.map} +0 -0
- /package/dist/{chunk-N2EDZTKG.js.map → chunk-4UDBGYI3.js.map} +0 -0
- /package/dist/{chunk-XTTZAQWJ.js.map → chunk-74R4XSFB.js.map} +0 -0
- /package/dist/{chunk-3MUCUZ46.js.map → chunk-ABY2R7OK.js.map} +0 -0
- /package/dist/{chunk-23RJT5WT.js.map → chunk-F7BMFOZ6.js.map} +0 -0
- /package/dist/{chunk-LT6VUZG2.js.map → chunk-GLY76TSI.js.map} +0 -0
- /package/dist/{chunk-WMDFK7LI.js.map → chunk-IFBEAOHH.js.map} +0 -0
- /package/dist/{chunk-YEIJW6X6.js.map → chunk-JIS2OCZR.js.map} +0 -0
- /package/dist/{chunk-FG467PDD.js.map → chunk-LNUMECBJ.js.map} +0 -0
- /package/dist/{chunk-FPEMP54L.js.map → chunk-OJXFQRUE.js.map} +0 -0
- /package/dist/{chunk-5KJ4UJOY.js.map → chunk-QQG42HCI.js.map} +0 -0
- /package/dist/{chunk-4C67GV3O.js.map → chunk-TCNCNWGV.js.map} +0 -0
- /package/dist/{chunk-NKHO34UZ.js.map → chunk-TU7D5DEW.js.map} +0 -0
- /package/dist/{chunk-FH335WL5.js.map → chunk-TWQPOVRA.js.map} +0 -0
- /package/dist/{chunk-YOJGSRZK.js.map → chunk-V3O7C2LY.js.map} +0 -0
- /package/dist/{chunk-V7AY2EJO.js.map → chunk-WOTLY5NA.js.map} +0 -0
- /package/dist/{client-INNE2GGZ.js.map → client-5L64D5SQ.js.map} +0 -0
- /package/dist/{codebase-index-FMIULFZQ.js.map → codebase-index-OOE7OAHP.js.map} +0 -0
- /package/dist/{fast-analyzer-MWKCDRGD.js.map → fast-analyzer-FMU3X4AZ.js.map} +0 -0
- /package/dist/{github-ingester-J2ZFYXVE.js.map → github-ingester-C66ZRUYC.js.map} +0 -0
- /package/dist/{goal-manager-ZBWKWEML.js.map → goal-manager-VTBFFYN4.js.map} +0 -0
- /package/dist/{goal-validator-DA3JQ6JN.js.map → goal-validator-EM5XVWVC.js.map} +0 -0
- /package/dist/{graph-J4OGTYCO.js.map → graph-26JPZ3DF.js.map} +0 -0
- /package/dist/{hypothesis-JCUMZKTG.js.map → hypothesis-4UPE7KXU.js.map} +0 -0
- /package/dist/{incident-index-BWW2UEY7.js.map → incident-index-H6APJ4S3.js.map} +0 -0
- /package/dist/{insight-store-A5XXMFD6.js.map → insight-store-QEEUQR5L.js.map} +0 -0
- /package/dist/{issue-store-LZWZIGM7.js.map → issue-store-C6XYENE5.js.map} +0 -0
- /package/dist/{linear-ingester-JRDQAIAA.js.map → ledger-VNA4DX3Z.js.map} +0 -0
- /package/dist/{tiered-storage-VZL7KK64.js.map → linear-ingester-WIUBWF55.js.map} +0 -0
- /package/dist/{trie-agent-6A7YBNTQ.js.map → tiered-storage-P6Z3NV2Q.js.map} +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/memory/issue-store.ts","../src/memory/bm25.ts","../src/memory/compactor.ts"],"sourcesContent":["/**\n * Issue Memory Store\n * \n * Working state cache complementary to the immutable ledger.\n * \n * Architecture:\n * - Ledger (.trie/memory/ledger.json) = immutable source of truth\n * - Issue Memory = mutable working state + BM25 search index\n * \n * Issue memory provides:\n * - Fast BM25 semantic search for pattern matching\n * - Mutable state tracking (resolved, open, fixed)\n * - Goal violation metrics and progress tracking\n * - Links back to ledger via ledgerBlockHash\n * \n * Phase 1 Hardening:\n * - SHA256 hashing for proper deduplication\n * - Atomic writes to prevent corruption\n * - Rotational backups for recovery\n * - Zod validation for data integrity\n */\n\nimport { mkdir, readFile, readdir } from 'fs/promises';\nimport { createHash } from 'crypto';\nimport { existsSync } from 'fs';\nimport { join } from 'path';\nimport { getWorkingDirectory, getTrieDirectory } from '../utils/workspace.js';\nimport type { Issue } from '../types/index.js';\nimport { BM25Index } from './bm25.js';\nimport { compactOldIssues, saveCompactedSummary, getHistoricalInsights } from './compactor.js';\nimport { atomicWriteJSON } from '../utils/atomic-write.js';\nimport { BackupManager } from '../utils/backup-manager.js';\nimport { IssueIndexSchema, safeParseAndValidate } from './validation.js';\nimport { appendIssuesToLedger } from './ledger.js';\n\nexport interface StoredIssue {\n id: string;\n hash: string;\n severity: string;\n issue: string;\n fix: string;\n file: string;\n line: number | undefined;\n agent: string;\n category: string | undefined;\n timestamp: string;\n project: string;\n resolved: boolean | undefined;\n resolvedAt: string | undefined;\n ledgerBlockHash?: string; // Link to immutable ledger record\n}\n\nexport interface IssueSearchResult {\n issue: StoredIssue;\n score: number;\n matchType: 'bm25' | 'keyword' | 'fts5';\n}\n\nexport interface IssueMemoryStats {\n totalIssues: number;\n activeIssues: number; // Unresolved issues\n issuesByAgent: Record<string, number>;\n issuesBySeverity: Record<string, number>; // All issues (historical)\n activeIssuesBySeverity: Record<string, number>; // Only unresolved\n oldestIssue: string | undefined;\n newestIssue: string | undefined;\n resolvedCount: number;\n historicalIssues: number;\n improvementTrend: 'improving' | 'stable' | 'declining' | 'unknown';\n capacityInfo: {\n current: number;\n max: number;\n percentFull: number;\n isAtCap: boolean;\n };\n deduplicationStats: {\n duplicatesAvoided: number;\n uniquePatterns: number;\n };\n}\n\n/**\n * Store issues and link them to the ledger\n * \n * Flow:\n * 1. Append to ledger (immutable source of truth)\n * 2. Cache in issue memory with ledger reference\n * \n * Returns number of unique issues added (after deduplication)\n */\nexport async function storeIssues(\n issues: Issue[],\n project: string,\n workDir?: string\n): Promise<{ stored: number; duplicates: number }> {\n const projectDir = workDir || getWorkingDirectory(undefined, true);\n const memoryDir = join(getTrieDirectory(projectDir), 'memory');\n await mkdir(memoryDir, { recursive: true });\n \n const stored: StoredIssue[] = [];\n const now = new Date().toISOString();\n const seenHashes = new Set<string>();\n let duplicates = 0;\n \n for (const issue of issues) {\n const hash = hashIssue(issue);\n \n // Skip duplicates within the same scan\n if (seenHashes.has(hash)) {\n duplicates++;\n continue;\n }\n seenHashes.add(hash);\n \n const storedIssue: StoredIssue = {\n id: issue.id,\n hash,\n severity: issue.severity,\n issue: issue.issue,\n fix: issue.fix,\n file: issue.file,\n line: issue.line,\n agent: issue.agent,\n category: issue.category,\n timestamp: now,\n project,\n resolved: false,\n resolvedAt: undefined,\n ...(issue.ledgerBlockHash && { ledgerBlockHash: issue.ledgerBlockHash }),\n };\n stored.push(storedIssue);\n }\n\n // 1. Write to ledger (immutable source of truth)\n const ledgerBlock = await appendIssuesToLedger(stored, projectDir);\n \n // 2. Link issues to their ledger block\n if (ledgerBlock) {\n for (const issue of stored) {\n issue.ledgerBlockHash = ledgerBlock.blockHash;\n }\n }\n \n // 3. Cache in issue memory (working state + search index)\n const dedupedCount = await updateIssueIndex(stored, projectDir);\n \n return { stored: dedupedCount, duplicates: duplicates + (stored.length - dedupedCount) };\n}\n\n/**\n * Search issues using BM25 ranking (same algorithm as Elasticsearch)\n */\nexport async function searchIssues(\n query: string,\n options: {\n workDir?: string;\n limit?: number;\n project?: string;\n severity?: string[];\n agent?: string;\n includeResolved?: boolean;\n } = {}\n): Promise<IssueSearchResult[]> {\n const projectDir = options.workDir || getWorkingDirectory(undefined, true);\n const limit = options.limit || 10;\n const allIssues = await loadIssueIndex(projectDir);\n \n if (allIssues.length === 0) {\n return [];\n }\n\n // Filter issues first\n const filteredIssues = allIssues.filter(issue => {\n if (options.project && issue.project !== options.project) return false;\n if (options.severity && !options.severity.includes(issue.severity)) return false;\n if (options.agent && issue.agent !== options.agent) return false;\n if (!options.includeResolved && issue.resolved) return false;\n return true;\n });\n\n if (filteredIssues.length === 0) {\n return [];\n }\n\n // Build BM25 index\n const bm25 = new BM25Index();\n const issueMap = new Map<string, StoredIssue>();\n \n for (const issue of filteredIssues) {\n const searchText = `${issue.issue} ${issue.fix} ${issue.file} ${issue.agent} ${issue.category || ''} ${issue.severity}`;\n bm25.addDocument({\n id: issue.id,\n text: searchText,\n });\n issueMap.set(issue.id, issue);\n }\n\n // Search with BM25\n const bm25Results = bm25.search(query, limit);\n \n return bm25Results.map(result => ({\n issue: issueMap.get(result.id)!,\n score: result.score,\n matchType: 'bm25' as const,\n }));\n}\n\n/**\n * Find similar issues using BM25 similarity\n */\nexport async function findSimilarIssues(\n issue: Issue,\n options: {\n workDir?: string;\n limit?: number;\n excludeSameFile?: boolean;\n } = {}\n): Promise<IssueSearchResult[]> {\n // Use the issue description and fix as the query for similarity\n const query = `${issue.issue} ${issue.fix} ${issue.agent}`;\n const searchOptions: Parameters<typeof searchIssues>[1] = {\n limit: (options.limit || 5) + 5, // Get extra to account for filtering\n includeResolved: true,\n };\n if (options.workDir !== undefined) {\n searchOptions.workDir = options.workDir;\n }\n const results = await searchIssues(query, searchOptions);\n\n let filtered = results.filter(r => r.issue.id !== issue.id);\n \n if (options.excludeSameFile) {\n filtered = filtered.filter(r => r.issue.file !== issue.file);\n }\n \n return filtered.slice(0, options.limit || 5);\n}\n\n/**\n * Mark an issue as resolved\n */\nexport async function markIssueResolved(\n issueId: string,\n workDir?: string\n): Promise<boolean> {\n const projectDir = workDir || getWorkingDirectory(undefined, true);\n const index = await loadIssueIndex(projectDir);\n \n const issue = index.find(i => i.id === issueId);\n if (!issue) return false;\n \n issue.resolved = true;\n issue.resolvedAt = new Date().toISOString();\n \n await saveIssueIndex(index, projectDir);\n return true;\n}\n\n/**\n * Auto-resolve issues that were not found in the latest scan\n * \n * After a scan completes, this function compares new issues against stored issues.\n * Issues that were previously found in scanned files but are no longer detected\n * are automatically marked as resolved.\n * \n * @param newIssueHashes - Set of hashes from the current scan\n * @param scannedFiles - List of files that were scanned (to scope resolution)\n * @param workDir - Working directory\n * @returns Number of issues auto-resolved\n */\nexport async function autoResolveIssues(\n newIssueHashes: Set<string>,\n scannedFiles: string[],\n workDir?: string\n): Promise<{ resolved: number; stillActive: number }> {\n const projectDir = workDir || getWorkingDirectory(undefined, true);\n const index = await loadIssueIndex(projectDir);\n \n // Normalize scanned file paths for comparison\n const scannedFileSet = new Set(scannedFiles.map(f => f.replace(/\\\\/g, '/')));\n \n const now = new Date().toISOString();\n let resolvedCount = 0;\n let stillActiveCount = 0;\n \n for (const issue of index) {\n // Skip already resolved issues\n if (issue.resolved) continue;\n \n // Normalize the issue file path\n const normalizedFile = issue.file.replace(/\\\\/g, '/');\n \n // Only auto-resolve issues in files that were scanned\n // This prevents marking issues as resolved if we just did a partial scan\n if (!scannedFileSet.has(normalizedFile)) {\n stillActiveCount++;\n continue;\n }\n \n // If the issue's hash is NOT in the new scan results, it's been fixed\n if (!newIssueHashes.has(issue.hash)) {\n issue.resolved = true;\n issue.resolvedAt = now;\n resolvedCount++;\n } else {\n stillActiveCount++;\n }\n }\n \n // Only save if we resolved something\n if (resolvedCount > 0) {\n await saveIssueIndex(index, projectDir);\n }\n \n return { resolved: resolvedCount, stillActive: stillActiveCount };\n}\n\n/**\n * Resolve goal violation issues for a specific file and goal\n * Called when a goal violation is fixed\n */\nexport async function resolveGoalViolation(\n file: string,\n goalDescription: string,\n workDir?: string\n): Promise<number> {\n const projectDir = workDir || getWorkingDirectory(undefined, true);\n const index = await loadIssueIndex(projectDir);\n \n const now = new Date().toISOString();\n let resolvedCount = 0;\n \n for (const issue of index) {\n if (issue.resolved) continue;\n if (issue.agent !== 'goal-violation') continue;\n \n // Match file and goal description\n const normalizedFile = issue.file.replace(/\\\\/g, '/');\n const normalizedTarget = file.replace(/\\\\/g, '/');\n \n if (normalizedFile === normalizedTarget && \n issue.issue.includes(`Goal \"${goalDescription}\"`)) {\n issue.resolved = true;\n issue.resolvedAt = now;\n resolvedCount++;\n }\n }\n \n if (resolvedCount > 0) {\n await saveIssueIndex(index, projectDir);\n }\n \n return resolvedCount;\n}\n\n/**\n * Get hash for an issue (for external callers)\n */\nexport function getIssueHash(issue: Issue): string {\n return hashIssue(issue);\n}\n\n/**\n * Get memory statistics including historical insights\n */\nexport async function getMemoryStats(workDir?: string): Promise<IssueMemoryStats> {\n const projectDir = workDir || getWorkingDirectory(undefined, true);\n const index = await loadIssueIndex(projectDir);\n const historical = await getHistoricalInsights(projectDir);\n \n const MAX_ISSUES = 10000;\n const uniqueHashes = new Set(index.map(i => i.hash));\n \n const stats: IssueMemoryStats = {\n totalIssues: index.length,\n activeIssues: 0,\n issuesByAgent: {},\n issuesBySeverity: {},\n activeIssuesBySeverity: {},\n oldestIssue: undefined,\n newestIssue: undefined,\n resolvedCount: 0,\n historicalIssues: historical.totalHistoricalIssues,\n improvementTrend: historical.improvementTrend,\n capacityInfo: {\n current: index.length,\n max: MAX_ISSUES,\n percentFull: Math.round((index.length / MAX_ISSUES) * 100),\n isAtCap: index.length >= MAX_ISSUES,\n },\n deduplicationStats: {\n duplicatesAvoided: index.length - uniqueHashes.size,\n uniquePatterns: uniqueHashes.size,\n },\n };\n\n for (const issue of index) {\n stats.issuesByAgent[issue.agent] = (stats.issuesByAgent[issue.agent] || 0) + 1;\n stats.issuesBySeverity[issue.severity] = (stats.issuesBySeverity[issue.severity] || 0) + 1;\n \n if (issue.resolved) {\n stats.resolvedCount++;\n } else {\n stats.activeIssues++;\n stats.activeIssuesBySeverity[issue.severity] = (stats.activeIssuesBySeverity[issue.severity] || 0) + 1;\n }\n }\n\n if (index.length > 0) {\n const sorted = [...index].sort((a, b) => \n new Date(a.timestamp).getTime() - new Date(b.timestamp).getTime()\n );\n const oldest = sorted[0]?.timestamp;\n const newest = sorted[sorted.length - 1]?.timestamp;\n if (oldest !== undefined) {\n stats.oldestIssue = oldest;\n }\n if (newest !== undefined) {\n stats.newestIssue = newest;\n }\n }\n\n return stats;\n}\n\n/**\n * Get recent issues\n * @param options.includeResolved - If false (default), only returns unresolved issues\n */\nexport async function getRecentIssues(\n options: {\n workDir?: string;\n limit?: number;\n daysBack?: number;\n includeResolved?: boolean;\n } = {}\n): Promise<StoredIssue[]> {\n const projectDir = options.workDir || getWorkingDirectory(undefined, true);\n const index = await loadIssueIndex(projectDir);\n const limit = options.limit || 20;\n const daysBack = options.daysBack || 7;\n const includeResolved = options.includeResolved ?? false;\n \n const cutoff = new Date();\n cutoff.setDate(cutoff.getDate() - daysBack);\n \n return index\n .filter(i => {\n if (new Date(i.timestamp) < cutoff) return false;\n if (!includeResolved && i.resolved) return false;\n return true;\n })\n .sort((a, b) => new Date(b.timestamp).getTime() - new Date(a.timestamp).getTime())\n .slice(0, limit);\n}\n\n/**\n * Purge issues from memory\n * Offers different strategies for managing memory capacity\n */\nexport async function purgeIssues(\n strategy: 'smart' | 'resolved' | 'old' | 'all',\n options: {\n workDir?: string;\n daysOld?: number;\n } = {}\n): Promise<{ removed: number; remaining: number; strategy: string }> {\n const projectDir = options.workDir || getWorkingDirectory(undefined, true);\n const index = await loadIssueIndex(projectDir);\n const originalCount = index.length;\n \n let remaining: StoredIssue[] = [];\n \n switch (strategy) {\n case 'smart':\n // Keep: critical/high severity, recent (< 30 days), unresolved\n const thirtyDaysAgo = new Date();\n thirtyDaysAgo.setDate(thirtyDaysAgo.getDate() - 30);\n \n remaining = index.filter(i => {\n const isRecent = new Date(i.timestamp) >= thirtyDaysAgo;\n const isImportant = ['critical', 'high'].includes(i.severity);\n const isUnresolved = !i.resolved;\n \n return isRecent || isImportant || isUnresolved;\n });\n break;\n \n case 'resolved':\n // Remove all resolved issues\n remaining = index.filter(i => !i.resolved);\n break;\n \n case 'old':\n // Remove issues older than specified days (default 90)\n const daysOld = options.daysOld || 90;\n const cutoffDate = new Date();\n cutoffDate.setDate(cutoffDate.getDate() - daysOld);\n \n remaining = index.filter(i => new Date(i.timestamp) >= cutoffDate);\n break;\n \n case 'all':\n // Clear all issues (keeps compacted summaries)\n remaining = [];\n break;\n }\n \n await saveIssueIndex(remaining, projectDir);\n \n return {\n removed: originalCount - remaining.length,\n remaining: remaining.length,\n strategy,\n };\n}\n\n/**\n * Get daily log files (legacy - deprecated)\n * \n * Note: Daily logs are redundant with the ledger.\n * The ledger provides the same information with better guarantees.\n * This function is kept for backward compatibility.\n */\nexport async function getDailyLogs(workDir?: string): Promise<string[]> {\n const projectDir = workDir || getWorkingDirectory(undefined, true);\n const memoryDir = join(getTrieDirectory(projectDir), 'memory');\n \n try {\n if (!existsSync(memoryDir)) return [];\n const files = await readdir(memoryDir);\n return files\n .filter(f => /^\\d{4}-\\d{2}-\\d{2}\\.md$/.test(f))\n .sort()\n .reverse();\n } catch {\n return [];\n }\n}\n\n// Private helpers\n\n/**\n * Get ledger entry for an issue\n * \n * Issue memory stores working state (resolved, etc.)\n * Ledger stores immutable history (what was detected, when)\n * \n * This helper links them together for full context.\n */\nexport async function getIssueLedgerEntry(\n issue: StoredIssue,\n workDir?: string\n): Promise<{ block: any; entry: any } | null> {\n if (!issue.ledgerBlockHash) return null;\n \n const projectDir = workDir || getWorkingDirectory(undefined, true);\n const { getLedgerBlocks } = await import('./ledger.js');\n const blocks = await getLedgerBlocks(projectDir);\n \n // Find the block this issue is linked to\n const block = blocks.find(b => b.blockHash === issue.ledgerBlockHash);\n if (!block) return null;\n \n // Find the specific entry in that block\n const entry = block.entries.find((e: any) => e.hash === issue.hash);\n if (!entry) return null;\n \n return { block, entry };\n}\n\n// Private helpers\n\n/**\n * Load issue index with validation and auto-recovery\n * \n * If the file is corrupted:\n * 1. Attempts to recover from the most recent valid backup\n * 2. Returns empty array if no valid backup exists\n */\nasync function loadIssueIndex(projectDir: string): Promise<StoredIssue[]> {\n const indexPath = join(getTrieDirectory(projectDir), 'memory', 'issues.json');\n \n try {\n if (existsSync(indexPath)) {\n const content = await readFile(indexPath, 'utf-8');\n const result = safeParseAndValidate(content, IssueIndexSchema);\n \n if (result.success) {\n return result.data as StoredIssue[];\n }\n \n // Validation failed - attempt recovery from backup\n console.error(` Issue index corrupted: ${result.error}`);\n const backupManager = new BackupManager(indexPath);\n \n if (await backupManager.recoverFromBackup()) {\n console.error(' ✅ Recovered from backup');\n const recovered = await readFile(indexPath, 'utf-8');\n const recoveredResult = safeParseAndValidate(recovered, IssueIndexSchema);\n if (recoveredResult.success) {\n return recoveredResult.data as StoredIssue[];\n }\n }\n \n console.error(' No valid backup found, starting fresh');\n }\n } catch {\n // Index doesn't exist or recovery failed\n }\n \n return [];\n}\n\nasync function updateIssueIndex(newIssues: StoredIssue[], projectDir: string): Promise<number> {\n const memoryDir = join(getTrieDirectory(projectDir), 'memory');\n await mkdir(memoryDir, { recursive: true });\n \n let existing = await loadIssueIndex(projectDir);\n \n // Intelligent deduplication: only add truly unique issues\n // Issues are unique if they have different hash (content + file + severity + agent)\n const hashSet = new Set(existing.map(i => i.hash));\n const toAdd = newIssues.filter(i => !hashSet.has(i.hash));\n const dedupedCount = toAdd.length;\n \n existing = [...existing, ...toAdd];\n \n // Intelligent compaction: summarize old issues instead of deleting\n if (existing.length > 500) {\n const { summary, remaining } = await compactOldIssues(existing, {\n keepDays: 30,\n minIssuesToCompact: 100,\n });\n \n if (summary) {\n await saveCompactedSummary(summary, projectDir);\n existing = remaining;\n }\n }\n \n // Hard cap: prune to 10,000 if still too large\n // Prioritize: 1) Recent issues, 2) High severity, 3) Unresolved\n if (existing.length > 10000) {\n existing = intelligentPrune(existing, 10000);\n }\n \n await saveIssueIndex(existing, projectDir);\n return dedupedCount;\n}\n\n/**\n * Intelligently prune issues to target count\n * Prioritizes: recent, high severity, unresolved\n */\nfunction intelligentPrune(issues: StoredIssue[], targetCount: number): StoredIssue[] {\n const severityWeight: Record<string, number> = {\n critical: 100,\n high: 50,\n moderate: 20,\n low: 10,\n info: 5,\n };\n \n const scored = issues.map(issue => {\n const ageInDays = (Date.now() - new Date(issue.timestamp).getTime()) / (1000 * 60 * 60 * 24);\n const recencyScore = Math.max(0, 100 - ageInDays * 2); // Newer = higher score\n const severityScore = severityWeight[issue.severity] || 10;\n const resolvedPenalty = issue.resolved ? -50 : 0;\n \n return {\n issue,\n score: recencyScore + severityScore + resolvedPenalty,\n };\n });\n \n return scored\n .sort((a, b) => b.score - a.score)\n .slice(0, targetCount)\n .map(s => s.issue);\n}\n\n/**\n * Save issue index with backup and atomic write\n * \n * 1. Creates a backup of the existing file\n * 2. Writes the new data atomically (temp file + rename)\n * 3. Maintains up to 5 rotational backups\n */\nasync function saveIssueIndex(issues: StoredIssue[], projectDir: string): Promise<void> {\n const memoryDir = join(getTrieDirectory(projectDir), 'memory');\n await mkdir(memoryDir, { recursive: true });\n \n const indexPath = join(memoryDir, 'issues.json');\n \n // Create backup before writing\n const backupManager = new BackupManager(indexPath);\n await backupManager.createBackup();\n \n // Atomic write\n await atomicWriteJSON(indexPath, issues);\n}\n\n/**\n * Hash an issue using SHA256 for proper deduplication\n * \n * Uses cryptographic hashing to eliminate collision risk.\n * The hash is truncated to 16 characters for storage efficiency\n * while still providing effectively zero collision probability.\n */\nfunction hashIssue(issue: Issue): string {\n const content = `${issue.issue}|${issue.file}|${issue.severity}|${issue.agent}`;\n return createHash('sha256').update(content).digest('hex').slice(0, 16);\n}\n\n","/**\n * BM25 Search Implementation\n * \n * BM25 (Best Match 25) is a ranking function used by search engines.\n * It's more sophisticated than TF-IDF and handles term frequency saturation.\n * \n * This is the same algorithm used by Elasticsearch.\n */\n\nexport interface BM25Document {\n id: string;\n text: string;\n metadata?: Record<string, unknown>;\n}\n\nexport interface BM25Result {\n id: string;\n score: number;\n metadata?: Record<string, unknown>;\n}\n\nexport class BM25Index {\n private documents: Map<string, BM25Document> = new Map();\n private termFrequencies: Map<string, Map<string, number>> = new Map();\n private documentFrequencies: Map<string, number> = new Map();\n private documentLengths: Map<string, number> = new Map();\n private avgDocLength: number = 0;\n private k1: number = 1.5;\n private b: number = 0.75;\n\n /**\n * Add a document to the index\n */\n addDocument(doc: BM25Document): void {\n const tokens = this.tokenize(doc.text);\n this.documents.set(doc.id, doc);\n this.documentLengths.set(doc.id, tokens.length);\n\n const termFreq = new Map<string, number>();\n const seenTerms = new Set<string>();\n\n for (const token of tokens) {\n termFreq.set(token, (termFreq.get(token) || 0) + 1);\n \n if (!seenTerms.has(token)) {\n seenTerms.add(token);\n this.documentFrequencies.set(token, (this.documentFrequencies.get(token) || 0) + 1);\n }\n }\n\n this.termFrequencies.set(doc.id, termFreq);\n this.updateAvgDocLength();\n }\n\n /**\n * Add multiple documents\n */\n addDocuments(docs: BM25Document[]): void {\n for (const doc of docs) {\n this.addDocument(doc);\n }\n }\n\n /**\n * Search the index\n */\n search(query: string, limit: number = 10): BM25Result[] {\n const queryTokens = this.tokenize(query);\n const scores: Map<string, number> = new Map();\n const N = this.documents.size;\n\n for (const [docId] of this.documents) {\n let score = 0;\n const docLength = this.documentLengths.get(docId) || 0;\n const termFreqs = this.termFrequencies.get(docId);\n\n if (!termFreqs) continue;\n\n for (const term of queryTokens) {\n const tf = termFreqs.get(term) || 0;\n if (tf === 0) continue;\n\n const df = this.documentFrequencies.get(term) || 0;\n const idf = Math.log((N - df + 0.5) / (df + 0.5) + 1);\n\n const numerator = tf * (this.k1 + 1);\n const denominator = tf + this.k1 * (1 - this.b + this.b * (docLength / this.avgDocLength));\n \n score += idf * (numerator / denominator);\n }\n\n if (score > 0) {\n scores.set(docId, score);\n }\n }\n\n return Array.from(scores.entries())\n .sort((a, b) => b[1] - a[1])\n .slice(0, limit)\n .map(([id, score]) => {\n const metadata = this.documents.get(id)?.metadata;\n const result: BM25Result = { id, score };\n if (metadata !== undefined) {\n result.metadata = metadata;\n }\n return result;\n });\n }\n\n /**\n * Get document count\n */\n get size(): number {\n return this.documents.size;\n }\n\n /**\n * Clear the index\n */\n clear(): void {\n this.documents.clear();\n this.termFrequencies.clear();\n this.documentFrequencies.clear();\n this.documentLengths.clear();\n this.avgDocLength = 0;\n }\n\n /**\n * Serialize the index to JSON\n */\n serialize(): string {\n return JSON.stringify({\n documents: Array.from(this.documents.entries()),\n termFrequencies: Array.from(this.termFrequencies.entries()).map(([k, v]) => [k, Array.from(v.entries())]),\n documentFrequencies: Array.from(this.documentFrequencies.entries()),\n documentLengths: Array.from(this.documentLengths.entries()),\n avgDocLength: this.avgDocLength,\n });\n }\n\n /**\n * Load from serialized JSON\n */\n static deserialize(json: string): BM25Index {\n const data = JSON.parse(json);\n const index = new BM25Index();\n \n index.documents = new Map(data.documents);\n index.termFrequencies = new Map(data.termFrequencies.map(([k, v]: [string, [string, number][]]) => [k, new Map(v)]));\n index.documentFrequencies = new Map(data.documentFrequencies);\n index.documentLengths = new Map(data.documentLengths);\n index.avgDocLength = data.avgDocLength;\n \n return index;\n }\n\n private tokenize(text: string): string[] {\n return text\n .toLowerCase()\n .replace(/[^\\w\\s]/g, ' ')\n .split(/\\s+/)\n .filter(token => token.length > 2 && !this.isStopWord(token));\n }\n\n private isStopWord(word: string): boolean {\n const stopWords = new Set([\n 'the', 'be', 'to', 'of', 'and', 'a', 'in', 'that', 'have', 'i',\n 'it', 'for', 'not', 'on', 'with', 'he', 'as', 'you', 'do', 'at',\n 'this', 'but', 'his', 'by', 'from', 'they', 'we', 'say', 'her', 'she',\n 'or', 'an', 'will', 'my', 'one', 'all', 'would', 'there', 'their', 'what',\n 'so', 'up', 'out', 'if', 'about', 'who', 'get', 'which', 'go', 'me',\n 'when', 'make', 'can', 'like', 'time', 'no', 'just', 'him', 'know', 'take',\n 'into', 'year', 'your', 'some', 'could', 'them', 'see', 'other', 'than', 'then',\n 'now', 'look', 'only', 'come', 'its', 'over', 'also', 'back', 'after', 'use',\n 'two', 'how', 'our', 'first', 'way', 'even', 'new', 'want', 'because', 'any',\n 'these', 'give', 'day', 'most', 'us', 'should', 'been', 'has', 'was', 'are',\n ]);\n return stopWords.has(word);\n }\n\n private updateAvgDocLength(): void {\n if (this.documentLengths.size === 0) {\n this.avgDocLength = 0;\n return;\n }\n const total = Array.from(this.documentLengths.values()).reduce((a, b) => a + b, 0);\n this.avgDocLength = total / this.documentLengths.size;\n }\n}\n","/**\n * Memory Compactor\n * \n * Intelligently compacts old issues into summaries instead of deleting them.\n * Preserves patterns and insights while reducing storage.\n * \n * Phase 1 Hardening:\n * - Atomic writes to prevent corruption\n * - Backup rotation for recovery\n * - Zod validation for data integrity\n */\n\nimport { mkdir, readFile } from 'fs/promises';\nimport { existsSync } from 'fs';\nimport { join } from 'path';\nimport { getTrieDirectory } from '../utils/workspace.js';\nimport type { StoredIssue } from './issue-store.js';\nimport { atomicWriteJSON } from '../utils/atomic-write.js';\nimport { BackupManager } from '../utils/backup-manager.js';\nimport { CompactedSummariesIndexSchema, safeParseAndValidate } from './validation.js';\n\nexport interface CompactedSummary {\n period: string;\n startDate: string;\n endDate: string;\n totalIssues: number;\n resolvedCount: number;\n bySeverity: Record<string, number>;\n byAgent: Record<string, number>;\n topPatterns: PatternSummary[];\n hotFiles: { file: string; count: number }[];\n compactedAt: string;\n}\n\nexport interface PatternSummary {\n pattern: string;\n count: number;\n severity: string;\n agent: string;\n exampleFix: string;\n}\n\n/**\n * Compact old issues into summaries\n * Returns the compacted summary and the remaining (recent) issues\n */\nexport async function compactOldIssues(\n issues: StoredIssue[],\n options: {\n keepDays?: number;\n minIssuesToCompact?: number;\n } = {}\n): Promise<{ summary: CompactedSummary | null; remaining: StoredIssue[] }> {\n const keepDays = options.keepDays ?? 30;\n const minIssues = options.minIssuesToCompact ?? 100;\n \n const cutoffDate = new Date();\n cutoffDate.setDate(cutoffDate.getDate() - keepDays);\n \n const oldIssues = issues.filter(i => new Date(i.timestamp) < cutoffDate);\n const recentIssues = issues.filter(i => new Date(i.timestamp) >= cutoffDate);\n \n // Only compact if we have enough old issues\n if (oldIssues.length < minIssues) {\n return { summary: null, remaining: issues };\n }\n \n // Build summary\n const summary = buildSummary(oldIssues);\n \n return { summary, remaining: recentIssues };\n}\n\n/**\n * Build a summary from a set of issues\n */\nfunction buildSummary(issues: StoredIssue[]): CompactedSummary {\n const sorted = issues.sort((a, b) => \n new Date(a.timestamp).getTime() - new Date(b.timestamp).getTime()\n );\n \n const bySeverity: Record<string, number> = {};\n const byAgent: Record<string, number> = {};\n const patternMap: Map<string, { count: number; issue: StoredIssue }> = new Map();\n const fileCount: Map<string, number> = new Map();\n \n for (const issue of issues) {\n // Count by severity\n bySeverity[issue.severity] = (bySeverity[issue.severity] || 0) + 1;\n \n // Count by agent\n byAgent[issue.agent] = (byAgent[issue.agent] || 0) + 1;\n \n // Track patterns (normalized issue text)\n const patternKey = normalizePattern(issue.issue);\n const existing = patternMap.get(patternKey);\n if (existing) {\n existing.count++;\n } else {\n patternMap.set(patternKey, { count: 1, issue });\n }\n \n // Count files\n const fileName = issue.file.split('/').pop() || issue.file;\n fileCount.set(fileName, (fileCount.get(fileName) || 0) + 1);\n }\n \n // Get top patterns\n const topPatterns = Array.from(patternMap.entries())\n .sort((a, b) => b[1].count - a[1].count)\n .slice(0, 10)\n .map(([pattern, data]) => ({\n pattern: pattern.slice(0, 100),\n count: data.count,\n severity: data.issue.severity,\n agent: data.issue.agent,\n exampleFix: data.issue.fix.slice(0, 200),\n }));\n \n // Get hot files\n const hotFiles = Array.from(fileCount.entries())\n .sort((a, b) => b[1] - a[1])\n .slice(0, 10)\n .map(([file, count]) => ({ file, count }));\n \n return {\n period: `${sorted[0]?.timestamp.split('T')[0]} to ${sorted[sorted.length - 1]?.timestamp.split('T')[0]}`,\n startDate: sorted[0]?.timestamp || '',\n endDate: sorted[sorted.length - 1]?.timestamp || '',\n totalIssues: issues.length,\n resolvedCount: issues.filter(i => i.resolved).length,\n bySeverity,\n byAgent,\n topPatterns,\n hotFiles,\n compactedAt: new Date().toISOString(),\n };\n}\n\n/**\n * Normalize issue text for pattern matching\n */\nfunction normalizePattern(text: string): string {\n return text\n .toLowerCase()\n .replace(/`[^`]+`/g, 'CODE')\n .replace(/\\b\\d+\\b/g, 'N')\n .replace(/[\"']/g, '')\n .replace(/\\s+/g, ' ')\n .trim()\n .slice(0, 150);\n}\n\n/**\n * Save compacted summary to disk with atomic write and backup\n */\nexport async function saveCompactedSummary(\n summary: CompactedSummary,\n projectDir: string\n): Promise<void> {\n const memoryDir = join(getTrieDirectory(projectDir), 'memory');\n await mkdir(memoryDir, { recursive: true });\n \n const summaryPath = join(memoryDir, 'compacted-summaries.json');\n \n let summaries: CompactedSummary[] = [];\n try {\n if (existsSync(summaryPath)) {\n const content = await readFile(summaryPath, 'utf-8');\n const result = safeParseAndValidate(content, CompactedSummariesIndexSchema);\n if (result.success) {\n summaries = result.data as CompactedSummary[];\n }\n }\n } catch {\n summaries = [];\n }\n \n summaries.push(summary);\n \n // Keep only last 12 summaries (1 year of monthly summaries)\n if (summaries.length > 12) {\n summaries = summaries.slice(-12);\n }\n \n // Create backup before writing\n const backupManager = new BackupManager(summaryPath);\n await backupManager.createBackup();\n \n // Atomic write\n await atomicWriteJSON(summaryPath, summaries);\n}\n\n/**\n * Load compacted summaries with validation and auto-recovery\n */\nexport async function loadCompactedSummaries(projectDir: string): Promise<CompactedSummary[]> {\n const summaryPath = join(getTrieDirectory(projectDir), 'memory', 'compacted-summaries.json');\n \n try {\n if (existsSync(summaryPath)) {\n const content = await readFile(summaryPath, 'utf-8');\n const result = safeParseAndValidate(content, CompactedSummariesIndexSchema);\n \n if (result.success) {\n return result.data as CompactedSummary[];\n }\n \n // Validation failed - attempt recovery\n const backupManager = new BackupManager(summaryPath);\n if (await backupManager.recoverFromBackup()) {\n const recovered = await readFile(summaryPath, 'utf-8');\n const recoveredResult = safeParseAndValidate(recovered, CompactedSummariesIndexSchema);\n if (recoveredResult.success) {\n return recoveredResult.data as CompactedSummary[];\n }\n }\n }\n } catch {\n // File doesn't exist or recovery failed\n }\n \n return [];\n}\n\n/**\n * Generate a markdown summary of compacted history\n */\nexport function formatCompactedSummary(summary: CompactedSummary): string {\n const lines: string[] = [\n `## Compacted Summary: ${summary.period}`,\n '',\n `**Total Issues:** ${summary.totalIssues} (${summary.resolvedCount} resolved)`,\n '',\n '### By Severity',\n ...Object.entries(summary.bySeverity).map(([s, c]) => `- ${s}: ${c}`),\n '',\n '### Top Patterns',\n ...summary.topPatterns.slice(0, 5).map(p => \n `- **${p.pattern.slice(0, 50)}...** (${p.count}x, ${p.severity})`\n ),\n '',\n '### Hot Files',\n ...summary.hotFiles.slice(0, 5).map(f => `- ${f.file}: ${f.count} issues`),\n ];\n \n return lines.join('\\n');\n}\n\n/**\n * Get insights from compacted history\n */\nexport async function getHistoricalInsights(projectDir: string): Promise<{\n totalHistoricalIssues: number;\n recurringPatterns: PatternSummary[];\n improvementTrend: 'improving' | 'stable' | 'declining' | 'unknown';\n}> {\n const summaries = await loadCompactedSummaries(projectDir);\n \n if (summaries.length === 0) {\n return {\n totalHistoricalIssues: 0,\n recurringPatterns: [],\n improvementTrend: 'unknown',\n };\n }\n \n const totalHistoricalIssues = summaries.reduce((sum, s) => sum + s.totalIssues, 0);\n \n // Find patterns that appear across multiple summaries\n const patternCounts: Map<string, PatternSummary & { appearances: number }> = new Map();\n \n for (const summary of summaries) {\n for (const pattern of summary.topPatterns) {\n const key = pattern.pattern;\n const existing = patternCounts.get(key);\n if (existing) {\n existing.count += pattern.count;\n existing.appearances++;\n } else {\n patternCounts.set(key, { ...pattern, appearances: 1 });\n }\n }\n }\n \n const recurringPatterns = Array.from(patternCounts.values())\n .filter(p => p.appearances >= 2)\n .sort((a, b) => b.count - a.count)\n .slice(0, 5);\n \n // Calculate improvement trend\n let improvementTrend: 'improving' | 'stable' | 'declining' | 'unknown' = 'unknown';\n \n if (summaries.length >= 2) {\n const recent = summaries.slice(-2);\n const olderCount = recent[0]?.totalIssues || 0;\n const newerCount = recent[1]?.totalIssues || 0;\n \n if (newerCount < olderCount * 0.8) {\n improvementTrend = 'improving';\n } else if (newerCount > olderCount * 1.2) {\n improvementTrend = 'declining';\n } else {\n improvementTrend = 'stable';\n }\n }\n \n return {\n totalHistoricalIssues,\n recurringPatterns,\n improvementTrend,\n };\n}\n"],"mappings":"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","names":["mkdir","readFile","existsSync","join","join","mkdir","existsSync","readFile"]}
|
|
@@ -184,7 +184,9 @@ var StoredIssueSchema = z.object({
|
|
|
184
184
|
timestamp: z.string(),
|
|
185
185
|
project: z.string(),
|
|
186
186
|
resolved: z.boolean().optional(),
|
|
187
|
-
resolvedAt: z.string().optional()
|
|
187
|
+
resolvedAt: z.string().optional(),
|
|
188
|
+
ledgerBlockHash: z.string().optional()
|
|
189
|
+
// Link to immutable ledger record
|
|
188
190
|
});
|
|
189
191
|
var IssueIndexSchema = z.array(StoredIssueSchema);
|
|
190
192
|
var FixAppliedSchema = z.object({
|
|
@@ -262,4 +264,4 @@ export {
|
|
|
262
264
|
CompactedSummariesIndexSchema,
|
|
263
265
|
safeParseAndValidate
|
|
264
266
|
};
|
|
265
|
-
//# sourceMappingURL=chunk-
|
|
267
|
+
//# sourceMappingURL=chunk-EFWVF6TI.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/utils/backup-manager.ts","../src/memory/validation.ts"],"sourcesContent":["/**\n * Backup Manager\n * \n * Maintains rotational backups of critical data files.\n * Provides automatic recovery from corrupted files.\n * \n * Features:\n * - N rotational backups (default: 5)\n * - Automatic pruning of old backups\n * - Validation before recovery\n * - Timestamp-based backup naming\n */\n\nimport { copyFile, readdir, unlink, readFile, stat } from 'fs/promises';\nimport { existsSync } from 'fs';\nimport { dirname, basename, join } from 'path';\n\nexport interface BackupOptions {\n /**\n * Maximum number of backups to keep\n * @default 5\n */\n maxBackups?: number;\n \n /**\n * Custom validator function for backup validation\n * Returns true if the backup is valid\n */\n validator?: (content: string) => boolean;\n}\n\nexport interface BackupInfo {\n path: string;\n timestamp: number;\n size: number;\n}\n\n/**\n * Manages rotational backups for a file\n * \n * @example\n * ```typescript\n * const manager = new BackupManager('/path/to/issues.json');\n * \n * // Create backup before modifying\n * await manager.createBackup();\n * \n * // If corruption detected, recover\n * if (await manager.recoverFromBackup()) {\n * console.log('Recovered from backup');\n * }\n * ```\n */\nexport class BackupManager {\n private readonly filePath: string;\n private readonly maxBackups: number;\n private readonly validator: ((content: string) => boolean) | undefined;\n private readonly backupDir: string;\n private readonly baseFileName: string;\n\n constructor(filePath: string, options: BackupOptions = {}) {\n this.filePath = filePath;\n this.maxBackups = options.maxBackups ?? 5;\n this.validator = options.validator;\n this.backupDir = dirname(filePath);\n this.baseFileName = basename(filePath);\n }\n\n /**\n * Create a backup of the current file\n * \n * @returns The backup file path, or null if source doesn't exist\n */\n async createBackup(): Promise<string | null> {\n if (!existsSync(this.filePath)) {\n return null;\n }\n\n const timestamp = Date.now();\n const backupPath = this.getBackupPath(timestamp);\n\n await copyFile(this.filePath, backupPath);\n await this.pruneOldBackups();\n\n return backupPath;\n }\n\n /**\n * List all backups sorted by timestamp (newest first)\n */\n async listBackups(): Promise<BackupInfo[]> {\n if (!existsSync(this.backupDir)) {\n return [];\n }\n\n const files = await readdir(this.backupDir);\n const backupPattern = new RegExp(\n `^${this.escapeRegex(this.baseFileName)}\\\\.backup\\\\.(\\\\d+)$`\n );\n\n const backups: BackupInfo[] = [];\n\n for (const file of files) {\n const match = file.match(backupPattern);\n if (match) {\n const timestamp = parseInt(match[1]!, 10);\n const backupPath = join(this.backupDir, file);\n \n try {\n const stats = await stat(backupPath);\n backups.push({\n path: backupPath,\n timestamp,\n size: stats.size,\n });\n } catch {\n // Skip files we can't stat\n }\n }\n }\n\n // Sort by timestamp, newest first\n return backups.sort((a, b) => b.timestamp - a.timestamp);\n }\n\n /**\n * Find the first valid backup\n * \n * Iterates through backups from newest to oldest,\n * returning the first one that passes validation.\n * \n * @returns Path to valid backup, or null if none found\n */\n async findValidBackup(): Promise<string | null> {\n const backups = await this.listBackups();\n\n for (const backup of backups) {\n if (await this.validateBackup(backup.path)) {\n return backup.path;\n }\n }\n\n return null;\n }\n\n /**\n * Validate a backup file\n * \n * If a custom validator was provided, uses that.\n * Otherwise, attempts JSON parse for .json files.\n * \n * @returns true if backup is valid\n */\n async validateBackup(backupPath: string): Promise<boolean> {\n try {\n const content = await readFile(backupPath, 'utf-8');\n\n // Use custom validator if provided\n if (this.validator) {\n return this.validator(content);\n }\n\n // Default validation: check if it's valid JSON for .json files\n if (this.filePath.endsWith('.json')) {\n JSON.parse(content);\n return true;\n }\n\n // For non-JSON files, just check it's readable\n return content.length > 0;\n } catch {\n return false;\n }\n }\n\n /**\n * Recover from the most recent valid backup\n * \n * @returns true if recovery was successful\n */\n async recoverFromBackup(): Promise<boolean> {\n const validBackup = await this.findValidBackup();\n \n if (!validBackup) {\n return false;\n }\n\n await copyFile(validBackup, this.filePath);\n return true;\n }\n\n /**\n * Remove old backups beyond the max limit\n */\n private async pruneOldBackups(): Promise<void> {\n const backups = await this.listBackups();\n\n // Remove backups beyond the limit\n const toRemove = backups.slice(this.maxBackups);\n \n for (const backup of toRemove) {\n try {\n await unlink(backup.path);\n } catch {\n // Ignore errors - backup might already be removed\n }\n }\n }\n\n /**\n * Get the backup path for a given timestamp\n */\n private getBackupPath(timestamp: number): string {\n return join(this.backupDir, `${this.baseFileName}.backup.${timestamp}`);\n }\n\n /**\n * Escape special regex characters in a string\n */\n private escapeRegex(str: string): string {\n return str.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&');\n }\n\n /**\n * Get the number of existing backups\n */\n async getBackupCount(): Promise<number> {\n const backups = await this.listBackups();\n return backups.length;\n }\n\n /**\n * Get the most recent backup timestamp\n */\n async getLatestBackupTime(): Promise<number | null> {\n const backups = await this.listBackups();\n return backups[0]?.timestamp ?? null;\n }\n\n /**\n * Delete all backups\n */\n async clearBackups(): Promise<number> {\n const backups = await this.listBackups();\n let deleted = 0;\n\n for (const backup of backups) {\n try {\n await unlink(backup.path);\n deleted++;\n } catch {\n // Ignore errors\n }\n }\n\n return deleted;\n }\n}\n\n/**\n * Default JSON validator\n * \n * Validates that content is valid JSON and optionally\n * checks for required fields.\n */\nexport function createJSONValidator(requiredFields?: string[]): (content: string) => boolean {\n return (content: string): boolean => {\n try {\n const parsed = JSON.parse(content);\n \n if (requiredFields && Array.isArray(parsed)) {\n // For arrays, check first element has required fields\n if (parsed.length > 0) {\n const first = parsed[0];\n return requiredFields.every(field => field in first);\n }\n // Empty array is valid\n return true;\n }\n \n if (requiredFields && typeof parsed === 'object' && parsed !== null) {\n return requiredFields.every(field => field in parsed);\n }\n \n return true;\n } catch {\n return false;\n }\n };\n}\n","/**\n * Memory Validation\n * \n * Schema validation for memory storage files using Zod.\n * Catches corruption early and provides clear error messages.\n * \n * Used by:\n * - issue-store.ts for issues.json\n * - global-memory.ts for global-patterns.json\n * - compactor.ts for compacted-summaries.json\n */\n\nimport { z } from 'zod';\n\n// ============================================================================\n// Issue Schemas\n// ============================================================================\n\n/**\n * Schema for a stored issue in issues.json\n */\nexport const StoredIssueSchema = z.object({\n id: z.string(),\n hash: z.string(),\n severity: z.string(),\n issue: z.string(),\n fix: z.string(),\n file: z.string(),\n line: z.number().optional(),\n agent: z.string(),\n category: z.string().optional(),\n timestamp: z.string(),\n project: z.string(),\n resolved: z.boolean().optional(),\n resolvedAt: z.string().optional(),\n ledgerBlockHash: z.string().optional(), // Link to immutable ledger record\n});\n\nexport type ValidatedStoredIssue = z.infer<typeof StoredIssueSchema>;\n\n/**\n * Schema for the issues.json file (array of issues)\n */\nexport const IssueIndexSchema = z.array(StoredIssueSchema);\n\n// ============================================================================\n// Global Pattern Schemas\n// ============================================================================\n\n/**\n * Schema for a fix applied record\n */\nexport const FixAppliedSchema = z.object({\n project: z.string(),\n timestamp: z.string(),\n fix: z.string(),\n});\n\n/**\n * Schema for a global pattern in global-patterns.json\n */\nexport const GlobalPatternSchema = z.object({\n id: z.string(),\n pattern: z.string(),\n description: z.string(),\n severity: z.string(),\n agent: z.string(),\n occurrences: z.number(),\n projects: z.array(z.string()),\n firstSeen: z.string(),\n lastSeen: z.string(),\n fixApplied: FixAppliedSchema.optional(),\n});\n\nexport type ValidatedGlobalPattern = z.infer<typeof GlobalPatternSchema>;\n\n/**\n * Schema for global-patterns.json (array of patterns)\n */\nexport const GlobalPatternsIndexSchema = z.array(GlobalPatternSchema);\n\n// ============================================================================\n// Project Summary Schemas\n// ============================================================================\n\n/**\n * Schema for a project summary\n */\nexport const ProjectSummarySchema = z.object({\n name: z.string(),\n path: z.string(),\n lastScan: z.string(),\n totalIssues: z.number(),\n patterns: z.array(z.string()),\n}).passthrough(); // Allow legacy healthScore in old project files\n\nexport type ValidatedProjectSummary = z.infer<typeof ProjectSummarySchema>;\n\n// ============================================================================\n// Compacted Summary Schemas\n// ============================================================================\n\n/**\n * Schema for a pattern summary in compacted data\n */\nexport const PatternSummarySchema = z.object({\n pattern: z.string(),\n count: z.number(),\n severity: z.string(),\n agent: z.string(),\n exampleFix: z.string(),\n});\n\n/**\n * Schema for a hot file entry\n */\nexport const HotFileSchema = z.object({\n file: z.string(),\n count: z.number(),\n});\n\n/**\n * Schema for a compacted summary\n */\nexport const CompactedSummarySchema = z.object({\n period: z.string(),\n startDate: z.string(),\n endDate: z.string(),\n totalIssues: z.number(),\n resolvedCount: z.number(),\n bySeverity: z.record(z.string(), z.number()),\n byAgent: z.record(z.string(), z.number()),\n topPatterns: z.array(PatternSummarySchema),\n hotFiles: z.array(HotFileSchema),\n compactedAt: z.string(),\n});\n\nexport type ValidatedCompactedSummary = z.infer<typeof CompactedSummarySchema>;\n\n/**\n * Schema for compacted-summaries.json (array of summaries)\n */\nexport const CompactedSummariesIndexSchema = z.array(CompactedSummarySchema);\n\n// ============================================================================\n// Validation Error\n// ============================================================================\n\n/**\n * Custom error for validation failures\n */\nexport class ValidationError extends Error {\n constructor(\n message: string,\n public readonly zodError: z.ZodError,\n public readonly filePath?: string\n ) {\n super(message);\n this.name = 'ValidationError';\n }\n\n /**\n * Get a human-readable summary of validation errors\n */\n getSummary(): string {\n const issues = this.zodError.issues.slice(0, 5);\n const lines = issues.map(issue => {\n const path = issue.path.join('.');\n return ` - ${path}: ${issue.message}`;\n });\n \n if (this.zodError.issues.length > 5) {\n lines.push(` ... and ${this.zodError.issues.length - 5} more errors`);\n }\n \n return lines.join('\\n');\n }\n}\n\n// ============================================================================\n// Validation Functions\n// ============================================================================\n\n/**\n * Validate issue index data\n * \n * @throws ValidationError if data is invalid\n * @returns Validated issue array\n */\nexport function validateIssueIndex(\n data: unknown,\n filePath?: string\n): ValidatedStoredIssue[] {\n const result = IssueIndexSchema.safeParse(data);\n \n if (!result.success) {\n throw new ValidationError(\n `Issue index validation failed${filePath ? ` (${filePath})` : ''}`,\n result.error,\n filePath\n );\n }\n \n return result.data;\n}\n\n/**\n * Validate global patterns data\n * \n * @throws ValidationError if data is invalid\n * @returns Validated patterns array\n */\nexport function validateGlobalPatterns(\n data: unknown,\n filePath?: string\n): ValidatedGlobalPattern[] {\n const result = GlobalPatternsIndexSchema.safeParse(data);\n \n if (!result.success) {\n throw new ValidationError(\n `Global patterns validation failed${filePath ? ` (${filePath})` : ''}`,\n result.error,\n filePath\n );\n }\n \n return result.data;\n}\n\n/**\n * Validate project summary data\n * \n * @throws ValidationError if data is invalid\n * @returns Validated project summary\n */\nexport function validateProjectSummary(\n data: unknown,\n filePath?: string\n): ValidatedProjectSummary {\n const result = ProjectSummarySchema.safeParse(data);\n \n if (!result.success) {\n throw new ValidationError(\n `Project summary validation failed${filePath ? ` (${filePath})` : ''}`,\n result.error,\n filePath\n );\n }\n \n return result.data;\n}\n\n/**\n * Validate compacted summaries data\n * \n * @throws ValidationError if data is invalid\n * @returns Validated summaries array\n */\nexport function validateCompactedSummaries(\n data: unknown,\n filePath?: string\n): ValidatedCompactedSummary[] {\n const result = CompactedSummariesIndexSchema.safeParse(data);\n \n if (!result.success) {\n throw new ValidationError(\n `Compacted summaries validation failed${filePath ? ` (${filePath})` : ''}`,\n result.error,\n filePath\n );\n }\n \n return result.data;\n}\n\n/**\n * Safely parse and validate JSON\n * \n * Combines JSON parsing with schema validation.\n * Returns null on any error instead of throwing.\n */\nexport function safeParseAndValidate<T>(\n content: string,\n schema: z.ZodSchema<T>\n): { success: true; data: T } | { success: false; error: string } {\n try {\n const parsed = JSON.parse(content);\n const result = schema.safeParse(parsed);\n \n if (result.success) {\n return { success: true, data: result.data };\n }\n \n return {\n success: false,\n error: `Validation failed: ${result.error.issues[0]?.message || 'Unknown error'}`,\n };\n } catch (error) {\n return {\n success: false,\n error: `JSON parse failed: ${error instanceof Error ? error.message : 'Unknown error'}`,\n };\n }\n}\n\n/**\n * Check if data matches a schema (non-throwing)\n */\nexport function isValidSchema<T>(data: unknown, schema: z.ZodSchema<T>): boolean {\n return schema.safeParse(data).success;\n}\n"],"mappings":"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","names":[]}
|
|
@@ -3,7 +3,7 @@ import {
|
|
|
3
3
|
} from "./chunk-43X6JBEM.js";
|
|
4
4
|
import {
|
|
5
5
|
getTrieDirectory
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-4TQQP7JD.js";
|
|
7
7
|
|
|
8
8
|
// src/cli/dashboard/chat-store.ts
|
|
9
9
|
import { mkdir, readFile, unlink } from "fs/promises";
|
|
@@ -345,4 +345,4 @@ export {
|
|
|
345
345
|
getChatStore,
|
|
346
346
|
clearChatStores
|
|
347
347
|
};
|
|
348
|
-
//# sourceMappingURL=chunk-
|
|
348
|
+
//# sourceMappingURL=chunk-F7BMFOZ6.js.map
|
|
@@ -1,30 +1,32 @@
|
|
|
1
1
|
import {
|
|
2
2
|
IncidentIndex
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-TCNCNWGV.js";
|
|
4
4
|
import {
|
|
5
5
|
TriePatternDiscovery
|
|
6
6
|
} from "./chunk-ZV2K6M7T.js";
|
|
7
7
|
import {
|
|
8
8
|
ContextGraph
|
|
9
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-TWQPOVRA.js";
|
|
10
10
|
import {
|
|
11
11
|
scanForVulnerabilities
|
|
12
12
|
} from "./chunk-F4NJ4CBP.js";
|
|
13
13
|
import {
|
|
14
14
|
scanForVibeCodeIssues
|
|
15
15
|
} from "./chunk-IXO4G4D3.js";
|
|
16
|
+
import {
|
|
17
|
+
storeIssues
|
|
18
|
+
} from "./chunk-ED7PLRQA.js";
|
|
16
19
|
import {
|
|
17
20
|
getDiff,
|
|
18
21
|
getRecentCommits,
|
|
19
22
|
getStagedChanges,
|
|
20
23
|
getUncommittedChanges,
|
|
21
|
-
getWorkingTreeDiff
|
|
22
|
-
|
|
23
|
-
} from "./chunk-62POBLFC.js";
|
|
24
|
+
getWorkingTreeDiff
|
|
25
|
+
} from "./chunk-TWPX6PHF.js";
|
|
24
26
|
import {
|
|
25
27
|
getTrieDirectory,
|
|
26
28
|
getWorkingDirectory
|
|
27
|
-
} from "./chunk-
|
|
29
|
+
} from "./chunk-4TQQP7JD.js";
|
|
28
30
|
import {
|
|
29
31
|
isInteractiveMode
|
|
30
32
|
} from "./chunk-APMV77PU.js";
|
|
@@ -1437,8 +1439,33 @@ async function reasonAboutChanges(projectPath, files, options = {}) {
|
|
|
1437
1439
|
}
|
|
1438
1440
|
async function reasonAboutChangesHumanReadable(projectPath, files, options = {}) {
|
|
1439
1441
|
const reasoning = await reasonAboutChanges(projectPath, files, options);
|
|
1440
|
-
const
|
|
1441
|
-
|
|
1442
|
+
const topFile = [...reasoning.files].sort((a, b) => b.score - a.score)[0];
|
|
1443
|
+
const topReasons = topFile?.reasons?.slice(0, 3) ?? [];
|
|
1444
|
+
const summaryParts = [];
|
|
1445
|
+
summaryParts.push(`Risk ${reasoning.riskLevel.toUpperCase()} (${reasoning.shouldBlock ? "block" : "allow"})`);
|
|
1446
|
+
if (topFile) summaryParts.push(`Most sensitive file: ${topFile.file}`);
|
|
1447
|
+
if (topReasons.length) summaryParts.push(`Top reasons: ${topReasons.join("; ")}`);
|
|
1448
|
+
const patterns = reasoning.matchedPatterns;
|
|
1449
|
+
const incidents = reasoning.relevantIncidents;
|
|
1450
|
+
const firstPattern = patterns[0];
|
|
1451
|
+
const patternLine = firstPattern ? `Pattern match: ${firstPattern.data.description}` : "No known risky patterns matched.";
|
|
1452
|
+
const incidentLine = incidents[0] ? `Relevant prior incident: ${incidents[0].data.description} (${incidents[0].data.severity})` : "No prior incidents linked to these files.";
|
|
1453
|
+
const howBad = [
|
|
1454
|
+
`Overall risk is ${reasoning.riskLevel}.`,
|
|
1455
|
+
reasoning.shouldBlock ? "This is likely to break things or violate policy if pushed without review." : "This is unlikely to cause major issues if you follow normal review/testing.",
|
|
1456
|
+
incidentLine
|
|
1457
|
+
].join(" ");
|
|
1458
|
+
const whatToDo = [
|
|
1459
|
+
reasoning.recommendation,
|
|
1460
|
+
patterns.length ? `Double-check the pattern(s): ${patterns.slice(0, 2).map((p) => p.data.description).join(" | ")}.` : ""
|
|
1461
|
+
].filter(Boolean).join(" ");
|
|
1462
|
+
return {
|
|
1463
|
+
summary: summaryParts.join(" \u2014 "),
|
|
1464
|
+
whatIFound: [patternLine, `Incidents considered: ${incidents.length}. Patterns considered: ${patterns.length}.`].join(" "),
|
|
1465
|
+
howBad,
|
|
1466
|
+
whatToDo,
|
|
1467
|
+
original: reasoning
|
|
1468
|
+
};
|
|
1442
1469
|
}
|
|
1443
1470
|
|
|
1444
1471
|
export {
|
|
@@ -1454,4 +1481,4 @@ export {
|
|
|
1454
1481
|
handleCheckpointCommand,
|
|
1455
1482
|
isTrieInitialized
|
|
1456
1483
|
};
|
|
1457
|
-
//# sourceMappingURL=chunk-
|
|
1484
|
+
//# sourceMappingURL=chunk-G7Q23IGF.js.map
|