@triedotdev/mcp 1.0.136 → 1.0.138

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.
Files changed (105) hide show
  1. package/README.md +6 -6
  2. package/dist/{autonomy-config-QA6ATWLJ.js → autonomy-config-TZ6HF4FA.js} +3 -3
  3. package/dist/{chat-store-HFOOWZYN.js → chat-store-OJLJCJFI.js} +3 -3
  4. package/dist/{chunk-DFPVUMVE.js → chunk-23RJT5WT.js} +5 -4
  5. package/dist/chunk-23RJT5WT.js.map +1 -0
  6. package/dist/{chunk-4YJ6KLGI.js → chunk-3MUCUZ46.js} +8 -8
  7. package/dist/chunk-3MUCUZ46.js.map +1 -0
  8. package/dist/{chunk-6VIMBFUZ.js → chunk-3RRXWX3V.js} +21 -17
  9. package/dist/chunk-3RRXWX3V.js.map +1 -0
  10. package/dist/{chunk-WHIQAGB7.js → chunk-4C67GV3O.js} +2 -2
  11. package/dist/{chunk-WS6OA7H6.js → chunk-4MJ52WBH.js} +2 -3
  12. package/dist/chunk-4MJ52WBH.js.map +1 -0
  13. package/dist/{chunk-AJ34GCMD.js → chunk-67GSG2ST.js} +41 -38
  14. package/dist/chunk-67GSG2ST.js.map +1 -0
  15. package/dist/{chunk-UHX4462X.js → chunk-6LLH3TBZ.js} +24 -25
  16. package/dist/chunk-6LLH3TBZ.js.map +1 -0
  17. package/dist/{chunk-DFHMB44X.js → chunk-D3AS5LY7.js} +6 -10
  18. package/dist/chunk-D3AS5LY7.js.map +1 -0
  19. package/dist/{chunk-6OUWNVLX.js → chunk-EDDT4ZIH.js} +8 -8
  20. package/dist/chunk-EDDT4ZIH.js.map +1 -0
  21. package/dist/{chunk-Z4DN527J.js → chunk-FG467PDD.js} +156 -39
  22. package/dist/chunk-FG467PDD.js.map +1 -0
  23. package/dist/{chunk-T4THB2OR.js → chunk-FOCXXIXY.js} +49 -28
  24. package/dist/chunk-FOCXXIXY.js.map +1 -0
  25. package/dist/{goal-validator-PDKYZSNP.js → chunk-GFFUDJMK.js} +97 -40
  26. package/dist/chunk-GFFUDJMK.js.map +1 -0
  27. package/dist/{chunk-ZEXMMTIQ.js → chunk-J5EMP4XW.js} +2 -2
  28. package/dist/{chunk-UHMMANC2.js → chunk-LT6VUZG2.js} +21 -18
  29. package/dist/chunk-LT6VUZG2.js.map +1 -0
  30. package/dist/{chunk-55CBWOEZ.js → chunk-QSWUPSLK.js} +2 -2
  31. package/dist/{chunk-45Y5TLQZ.js → chunk-SH7H3WRU.js} +3 -6
  32. package/dist/chunk-SH7H3WRU.js.map +1 -0
  33. package/dist/{chunk-VRLMTOB6.js → chunk-TIMIKBY2.js} +1 -1
  34. package/dist/chunk-TIMIKBY2.js.map +1 -0
  35. package/dist/{chunk-POHBQUG7.js → chunk-X3F5QDER.js} +1224 -448
  36. package/dist/chunk-X3F5QDER.js.map +1 -0
  37. package/dist/{chunk-O6OTJI3W.js → chunk-Y32FM3MR.js} +2 -2
  38. package/dist/{chunk-G5PRBQIQ.js → chunk-YOKQ25IW.js} +102 -82
  39. package/dist/chunk-YOKQ25IW.js.map +1 -0
  40. package/dist/{chunk-JAKMZI5S.js → chunk-Z2P4WST6.js} +291 -180
  41. package/dist/chunk-Z2P4WST6.js.map +1 -0
  42. package/dist/cli/create-agent.js +1 -1
  43. package/dist/cli/main.js +113 -86
  44. package/dist/cli/main.js.map +1 -1
  45. package/dist/cli/yolo-daemon.js +19 -19
  46. package/dist/cli/yolo-daemon.js.map +1 -1
  47. package/dist/{client-BZHI675W.js → client-JTU5TRLB.js} +3 -3
  48. package/dist/{codebase-index-CR6Q2HEI.js → codebase-index-FNJ4GCBE.js} +3 -3
  49. package/dist/{goal-manager-FAK7H4RR.js → goal-manager-6BJQ36AH.js} +7 -8
  50. package/dist/goal-validator-GISXYANK.js +22 -0
  51. package/dist/{graph-PAUZ5EMP.js → graph-X2FMRQLG.js} +3 -3
  52. package/dist/{hypothesis-L5446W36.js → hypothesis-K3KQJOXJ.js} +7 -8
  53. package/dist/{incident-index-ZCDSJ42L.js → incident-index-BWW2UEY7.js} +3 -3
  54. package/dist/index.js +343 -288
  55. package/dist/index.js.map +1 -1
  56. package/dist/{insight-store-F5KDBY5Y.js → insight-store-A5XXMFD6.js} +6 -6
  57. package/dist/issue-store-BO5OWLJW.js +32 -0
  58. package/dist/{output-manager-BOTMXSND.js → output-manager-DZO5LGSG.js} +2 -2
  59. package/dist/{tiered-storage-QW2G7GSG.js → tiered-storage-VZL7KK64.js} +3 -3
  60. package/dist/trie-agent-XMSGMD7E.js +26 -0
  61. package/dist/trie-agent-XMSGMD7E.js.map +1 -0
  62. package/dist/ui/chat.html +260 -67
  63. package/dist/ui/goals.html +246 -3
  64. package/dist/ui/hypotheses.html +248 -5
  65. package/dist/ui/ledger.html +252 -9
  66. package/dist/ui/nudges.html +244 -1
  67. package/package.json +1 -1
  68. package/dist/chunk-45Y5TLQZ.js.map +0 -1
  69. package/dist/chunk-4YJ6KLGI.js.map +0 -1
  70. package/dist/chunk-6OUWNVLX.js.map +0 -1
  71. package/dist/chunk-6VIMBFUZ.js.map +0 -1
  72. package/dist/chunk-AJ34GCMD.js.map +0 -1
  73. package/dist/chunk-DFHMB44X.js.map +0 -1
  74. package/dist/chunk-DFPVUMVE.js.map +0 -1
  75. package/dist/chunk-G5PRBQIQ.js.map +0 -1
  76. package/dist/chunk-JAKMZI5S.js.map +0 -1
  77. package/dist/chunk-PEJEYWVR.js +0 -135
  78. package/dist/chunk-PEJEYWVR.js.map +0 -1
  79. package/dist/chunk-POHBQUG7.js.map +0 -1
  80. package/dist/chunk-T4THB2OR.js.map +0 -1
  81. package/dist/chunk-UHMMANC2.js.map +0 -1
  82. package/dist/chunk-UHX4462X.js.map +0 -1
  83. package/dist/chunk-VRLMTOB6.js.map +0 -1
  84. package/dist/chunk-WS6OA7H6.js.map +0 -1
  85. package/dist/chunk-Z4DN527J.js.map +0 -1
  86. package/dist/goal-validator-PDKYZSNP.js.map +0 -1
  87. package/dist/guardian-agent-4RHGIXUD.js +0 -27
  88. package/dist/ledger-WKVJWHBX.js +0 -17
  89. /package/dist/{autonomy-config-QA6ATWLJ.js.map → autonomy-config-TZ6HF4FA.js.map} +0 -0
  90. /package/dist/{chat-store-HFOOWZYN.js.map → chat-store-OJLJCJFI.js.map} +0 -0
  91. /package/dist/{chunk-WHIQAGB7.js.map → chunk-4C67GV3O.js.map} +0 -0
  92. /package/dist/{chunk-ZEXMMTIQ.js.map → chunk-J5EMP4XW.js.map} +0 -0
  93. /package/dist/{chunk-55CBWOEZ.js.map → chunk-QSWUPSLK.js.map} +0 -0
  94. /package/dist/{chunk-O6OTJI3W.js.map → chunk-Y32FM3MR.js.map} +0 -0
  95. /package/dist/{client-BZHI675W.js.map → client-JTU5TRLB.js.map} +0 -0
  96. /package/dist/{codebase-index-CR6Q2HEI.js.map → codebase-index-FNJ4GCBE.js.map} +0 -0
  97. /package/dist/{goal-manager-FAK7H4RR.js.map → goal-manager-6BJQ36AH.js.map} +0 -0
  98. /package/dist/{graph-PAUZ5EMP.js.map → goal-validator-GISXYANK.js.map} +0 -0
  99. /package/dist/{guardian-agent-4RHGIXUD.js.map → graph-X2FMRQLG.js.map} +0 -0
  100. /package/dist/{hypothesis-L5446W36.js.map → hypothesis-K3KQJOXJ.js.map} +0 -0
  101. /package/dist/{incident-index-ZCDSJ42L.js.map → incident-index-BWW2UEY7.js.map} +0 -0
  102. /package/dist/{insight-store-F5KDBY5Y.js.map → insight-store-A5XXMFD6.js.map} +0 -0
  103. /package/dist/{ledger-WKVJWHBX.js.map → issue-store-BO5OWLJW.js.map} +0 -0
  104. /package/dist/{output-manager-BOTMXSND.js.map → output-manager-DZO5LGSG.js.map} +0 -0
  105. /package/dist/{tiered-storage-QW2G7GSG.js.map → tiered-storage-VZL7KK64.js.map} +0 -0
package/README.md CHANGED
@@ -18,7 +18,7 @@ The agent learns from every incident, decision, and fix. When you ship, Trie rem
18
18
 
19
19
  ## Goal
20
20
 
21
- **Trie is your personal AI agent that maintains multiple codebases without losing context.**
21
+ **Trie is your personal AI agent that carries context across tools.**
22
22
 
23
23
  Every bug you fix teaches the agent a pattern that protects all your projects. Every incident you report becomes institutional knowledge that travels with your code. Every decision you make—and the tradeoffs you considered—gets extracted and stored automatically by the agent.
24
24
 
@@ -108,12 +108,12 @@ Trie remembers your decisions across tools, tracks the tradeoffs you've made, an
108
108
 
109
109
  ## How It Works
110
110
 
111
- ### The Guardian System
111
+ ### The Trie Agent
112
112
 
113
- Trie uses a "Guardian Agent" architecture:
113
+ Trie uses a proactive agent architecture:
114
114
 
115
115
  - Analyzes your code for potential issues based on your incident history.
116
- - Guardian decides what matters based on your history.
116
+ - The agent decides what matters based on your history.
117
117
  - Memory Tree stores incident patterns using a trie data structure for fast lookups.
118
118
  - Learning Loop improves predictions based on your feedback.
119
119
 
@@ -312,7 +312,7 @@ trie watch
312
312
  # Reattach with: tmux attach -t trie-watch
313
313
  ```
314
314
 
315
- This allows you to keep the guardian running while using other terminals. The interactive dashboard will be available when you reattach.
315
+ This allows you to keep the agent running while using other terminals. The interactive dashboard will be available when you reattach.
316
316
 
317
317
  ### JIT Defect Prediction ("Gotchas")
318
318
 
@@ -635,7 +635,7 @@ What happens at the 10,000 issue cap: Trie will deduplicate new repeats, compact
635
635
 
636
636
  **Hooks not working**: Reinstall with `trie init`. Make sure you have write permissions to .git/hooks/.
637
637
 
638
- **Goals showing but not detecting violations**: This happens when goals appear in the UI but aren't active in guardian state. The Goals view will show a yellow warning box. To fix:
638
+ **Goals showing but not detecting violations**: This happens when goals appear in the UI but aren't active in project state. The Goals view will show a yellow warning box. To fix:
639
639
  ```bash
640
640
  # Quick fix - activate a specific goal
641
641
  node fix-goals.js "NO EMOJIS"
@@ -10,8 +10,8 @@ import {
10
10
  shouldAutoFix,
11
11
  shouldBlockPush,
12
12
  trackIssueOccurrence
13
- } from "./chunk-ZEXMMTIQ.js";
14
- import "./chunk-45Y5TLQZ.js";
13
+ } from "./chunk-J5EMP4XW.js";
14
+ import "./chunk-SH7H3WRU.js";
15
15
  import "./chunk-APMV77PU.js";
16
16
  import "./chunk-DGUM43GV.js";
17
17
  export {
@@ -27,4 +27,4 @@ export {
27
27
  shouldBlockPush,
28
28
  trackIssueOccurrence
29
29
  };
30
- //# sourceMappingURL=autonomy-config-QA6ATWLJ.js.map
30
+ //# sourceMappingURL=autonomy-config-TZ6HF4FA.js.map
@@ -2,9 +2,9 @@ import {
2
2
  ChatStore,
3
3
  clearChatStores,
4
4
  getChatStore
5
- } from "./chunk-DFPVUMVE.js";
5
+ } from "./chunk-23RJT5WT.js";
6
6
  import "./chunk-43X6JBEM.js";
7
- import "./chunk-45Y5TLQZ.js";
7
+ import "./chunk-SH7H3WRU.js";
8
8
  import "./chunk-APMV77PU.js";
9
9
  import "./chunk-DGUM43GV.js";
10
10
  export {
@@ -12,4 +12,4 @@ export {
12
12
  clearChatStores,
13
13
  getChatStore
14
14
  };
15
- //# sourceMappingURL=chat-store-HFOOWZYN.js.map
15
+ //# sourceMappingURL=chat-store-OJLJCJFI.js.map
@@ -3,7 +3,7 @@ import {
3
3
  } from "./chunk-43X6JBEM.js";
4
4
  import {
5
5
  getTrieDirectory
6
- } from "./chunk-45Y5TLQZ.js";
6
+ } from "./chunk-SH7H3WRU.js";
7
7
 
8
8
  // src/cli/dashboard/chat-store.ts
9
9
  import { mkdir, readFile, unlink } from "fs/promises";
@@ -20,11 +20,12 @@ var ChatMessageSchema = z.object({
20
20
  input: z.record(z.any())
21
21
  })).optional(),
22
22
  pendingFix: z.object({
23
- id: z.string(),
23
+ id: z.string().optional(),
24
24
  file: z.string(),
25
25
  goal: z.string(),
26
26
  violation: z.string(),
27
- suggestedFix: z.string().optional()
27
+ suggestedFix: z.string().optional(),
28
+ directory: z.string().optional()
28
29
  }).optional()
29
30
  });
30
31
  var ChatSessionSchema = z.object({
@@ -344,4 +345,4 @@ export {
344
345
  getChatStore,
345
346
  clearChatStores
346
347
  };
347
- //# sourceMappingURL=chunk-DFPVUMVE.js.map
348
+ //# sourceMappingURL=chunk-23RJT5WT.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/cli/dashboard/chat-store.ts"],"sourcesContent":["/**\n * Chat Store - Persistent storage for chat history\n * \n * Features:\n * - Save and load chat sessions\n * - List all chat sessions\n * - Archive management (rename, delete, export)\n * - Atomic writes with backup rotation\n * - Zod validation for data integrity\n * \n * Persists to: .trie/memory/chats/\n */\n\nimport { mkdir, readFile, unlink } from 'fs/promises';\nimport { existsSync } from 'fs';\nimport { join } from 'path';\nimport { getTrieDirectory } from '../../utils/workspace.js';\nimport { z } from 'zod';\nimport { atomicWriteJSON } from '../../utils/atomic-write.js';\nimport type { ChatMessage } from './types.js';\n\n// ============================================================================\n// Schemas\n// ============================================================================\n\nconst ChatMessageSchema = z.object({\n role: z.enum(['user', 'assistant']),\n content: z.string(),\n timestamp: z.number(),\n toolCalls: z.array(z.object({\n id: z.string(),\n name: z.string(),\n input: z.record(z.any()),\n })).optional(),\n pendingFix: z.object({\n id: z.string().optional(),\n file: z.string(),\n goal: z.string(),\n violation: z.string(),\n suggestedFix: z.string().optional(),\n directory: z.string().optional(),\n }).optional(),\n});\n\nconst ChatSessionSchema = z.object({\n id: z.string(),\n title: z.string(),\n messages: z.array(ChatMessageSchema),\n createdAt: z.number(),\n updatedAt: z.number(),\n archived: z.boolean(),\n});\n\nexport type ChatSession = z.infer<typeof ChatSessionSchema>;\n\nconst ChatStoreIndexSchema = z.object({\n version: z.literal(1),\n sessions: z.array(z.object({\n id: z.string(),\n title: z.string(),\n createdAt: z.number(),\n updatedAt: z.number(),\n archived: z.boolean(),\n messageCount: z.number(),\n })),\n lastUpdated: z.string(),\n});\n\ntype ChatStoreIndex = z.infer<typeof ChatStoreIndexSchema>;\n\n// ============================================================================\n// ChatStore Class\n// ============================================================================\n\nexport class ChatStore {\n private projectPath: string;\n private index: ChatStoreIndex | null = null;\n \n constructor(projectPath: string) {\n this.projectPath = projectPath;\n }\n \n /**\n * Get the chats directory path\n */\n private getChatsDir(): string {\n return join(getTrieDirectory(this.projectPath), 'memory', 'chats');\n }\n \n /**\n * Get the index file path\n */\n private getIndexPath(): string {\n return join(this.getChatsDir(), 'index.json');\n }\n \n /**\n * Get the path for a specific session file\n */\n private getSessionPath(sessionId: string): string {\n return join(this.getChatsDir(), `${sessionId}.json`);\n }\n \n /**\n * Load the index file\n */\n private async loadIndex(): Promise<ChatStoreIndex> {\n if (this.index) {\n return this.index;\n }\n \n const indexPath = this.getIndexPath();\n \n if (existsSync(indexPath)) {\n try {\n const content = await readFile(indexPath, 'utf-8');\n const parsed = JSON.parse(content);\n const result = ChatStoreIndexSchema.safeParse(parsed);\n \n if (result.success) {\n this.index = result.data;\n return this.index;\n }\n } catch (error) {\n console.error('Failed to load chat index:', error);\n }\n }\n \n // Create empty index\n this.index = {\n version: 1,\n sessions: [],\n lastUpdated: new Date().toISOString(),\n };\n \n return this.index;\n }\n \n /**\n * Save the index file\n */\n private async saveIndex(): Promise<void> {\n if (!this.index) return;\n \n const indexPath = this.getIndexPath();\n const chatsDir = this.getChatsDir();\n \n // Ensure directory exists\n await mkdir(chatsDir, { recursive: true });\n \n // Update timestamp\n this.index.lastUpdated = new Date().toISOString();\n \n // Atomic write\n await atomicWriteJSON(indexPath, this.index);\n }\n \n /**\n * Generate a unique session ID\n */\n private generateSessionId(): string {\n return `chat-${Date.now()}-${Math.random().toString(36).slice(2, 9)}`;\n }\n \n /**\n * Generate a default title from the first user message\n */\n private generateTitle(messages: ChatMessage[]): string {\n const firstUser = messages.find(m => m.role === 'user');\n if (firstUser) {\n const text = firstUser.content.trim();\n const maxLen = 50;\n if (text.length > maxLen) {\n return text.slice(0, maxLen) + '...';\n }\n return text;\n }\n return 'New Chat';\n }\n \n /**\n * Save a chat session\n */\n async saveSession(messages: ChatMessage[], sessionId?: string, title?: string): Promise<string> {\n await this.loadIndex();\n \n const id = sessionId || this.generateSessionId();\n const now = Date.now();\n \n // Load existing session if updating\n let createdAt = now;\n if (sessionId) {\n const existing = this.index!.sessions.find(s => s.id === sessionId);\n if (existing) {\n createdAt = existing.createdAt;\n }\n }\n \n const session: ChatSession = {\n id,\n title: title || this.generateTitle(messages),\n messages,\n createdAt,\n updatedAt: now,\n archived: false,\n };\n \n // Validate\n const result = ChatSessionSchema.safeParse(session);\n if (!result.success) {\n throw new Error(`Invalid chat session: ${result.error.message}`);\n }\n \n // Save session file\n const sessionPath = this.getSessionPath(id);\n await mkdir(this.getChatsDir(), { recursive: true });\n await atomicWriteJSON(sessionPath, session);\n \n // Update index\n const existingIndex = this.index!.sessions.findIndex(s => s.id === id);\n const indexEntry = {\n id,\n title: session.title,\n createdAt: session.createdAt,\n updatedAt: session.updatedAt,\n archived: session.archived,\n messageCount: messages.length,\n };\n \n if (existingIndex >= 0) {\n this.index!.sessions[existingIndex] = indexEntry;\n } else {\n this.index!.sessions.unshift(indexEntry);\n }\n \n await this.saveIndex();\n \n return id;\n }\n \n /**\n * Load a chat session\n */\n async loadSession(sessionId: string): Promise<ChatSession | null> {\n const sessionPath = this.getSessionPath(sessionId);\n \n if (!existsSync(sessionPath)) {\n return null;\n }\n \n try {\n const content = await readFile(sessionPath, 'utf-8');\n const parsed = JSON.parse(content);\n const result = ChatSessionSchema.safeParse(parsed);\n \n if (result.success) {\n return result.data;\n }\n \n console.error('Invalid chat session:', result.error.message);\n return null;\n } catch (error) {\n console.error('Failed to load chat session:', error);\n return null;\n }\n }\n \n /**\n * List all chat sessions\n */\n async listSessions(includeArchived: boolean = false): Promise<ChatStoreIndex['sessions']> {\n await this.loadIndex();\n \n let sessions = this.index!.sessions;\n \n if (!includeArchived) {\n sessions = sessions.filter(s => !s.archived);\n }\n \n // Sort by most recent first\n return sessions.sort((a, b) => b.updatedAt - a.updatedAt);\n }\n \n /**\n * Delete a chat session\n */\n async deleteSession(sessionId: string): Promise<boolean> {\n await this.loadIndex();\n \n const sessionPath = this.getSessionPath(sessionId);\n \n if (!existsSync(sessionPath)) {\n return false;\n }\n \n try {\n await unlink(sessionPath);\n \n // Remove from index\n this.index!.sessions = this.index!.sessions.filter(s => s.id !== sessionId);\n await this.saveIndex();\n \n return true;\n } catch (error) {\n console.error('Failed to delete chat session:', error);\n return false;\n }\n }\n \n /**\n * Rename a chat session\n */\n async renameSession(sessionId: string, newTitle: string): Promise<boolean> {\n await this.loadIndex();\n \n const session = await this.loadSession(sessionId);\n if (!session) {\n return false;\n }\n \n session.title = newTitle;\n session.updatedAt = Date.now();\n \n // Save updated session\n const sessionPath = this.getSessionPath(sessionId);\n await atomicWriteJSON(sessionPath, session);\n \n // Update index\n const indexEntry = this.index!.sessions.find(s => s.id === sessionId);\n if (indexEntry) {\n indexEntry.title = newTitle;\n indexEntry.updatedAt = session.updatedAt;\n await this.saveIndex();\n }\n \n return true;\n }\n \n /**\n * Archive a chat session\n */\n async archiveSession(sessionId: string): Promise<boolean> {\n await this.loadIndex();\n \n const session = await this.loadSession(sessionId);\n if (!session) {\n return false;\n }\n \n session.archived = true;\n session.updatedAt = Date.now();\n \n // Save updated session\n const sessionPath = this.getSessionPath(sessionId);\n await atomicWriteJSON(sessionPath, session);\n \n // Update index\n const indexEntry = this.index!.sessions.find(s => s.id === sessionId);\n if (indexEntry) {\n indexEntry.archived = true;\n indexEntry.updatedAt = session.updatedAt;\n await this.saveIndex();\n }\n \n return true;\n }\n \n /**\n * Unarchive a chat session\n */\n async unarchiveSession(sessionId: string): Promise<boolean> {\n await this.loadIndex();\n \n const session = await this.loadSession(sessionId);\n if (!session) {\n return false;\n }\n \n session.archived = false;\n session.updatedAt = Date.now();\n \n // Save updated session\n const sessionPath = this.getSessionPath(sessionId);\n await atomicWriteJSON(sessionPath, session);\n \n // Update index\n const indexEntry = this.index!.sessions.find(s => s.id === sessionId);\n if (indexEntry) {\n indexEntry.archived = false;\n indexEntry.updatedAt = session.updatedAt;\n await this.saveIndex();\n }\n \n return true;\n }\n \n /**\n * Export a chat session as JSON\n */\n async exportSession(sessionId: string): Promise<string | null> {\n const session = await this.loadSession(sessionId);\n if (!session) {\n return null;\n }\n \n return JSON.stringify(session, null, 2);\n }\n \n /**\n * Get statistics about chat sessions\n */\n async getStats(): Promise<{\n total: number;\n active: number;\n archived: number;\n totalMessages: number;\n }> {\n await this.loadIndex();\n \n const total = this.index!.sessions.length;\n const archived = this.index!.sessions.filter(s => s.archived).length;\n const active = total - archived;\n const totalMessages = this.index!.sessions.reduce((sum, s) => sum + s.messageCount, 0);\n \n return { total, active, archived, totalMessages };\n }\n \n /**\n * Prune old sessions (older than N days)\n */\n async pruneOldSessions(daysToKeep: number = 90): Promise<number> {\n await this.loadIndex();\n \n const cutoff = Date.now() - (daysToKeep * 24 * 60 * 60 * 1000);\n const toDelete = this.index!.sessions.filter(s => s.updatedAt < cutoff);\n \n for (const session of toDelete) {\n await this.deleteSession(session.id);\n }\n \n return toDelete.length;\n }\n}\n\n// ============================================================================\n// Singleton Management\n// ============================================================================\n\nconst chatStores: Map<string, ChatStore> = new Map();\n\n/**\n * Get the ChatStore for a project (singleton per project)\n */\nexport function getChatStore(projectPath: string): ChatStore {\n let store = chatStores.get(projectPath);\n if (!store) {\n store = new ChatStore(projectPath);\n chatStores.set(projectPath, store);\n }\n return store;\n}\n\n/**\n * Clear all ChatStore instances (for testing)\n */\nexport function clearChatStores(): void {\n chatStores.clear();\n}\n"],"mappings":"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","names":[]}
@@ -1,15 +1,15 @@
1
1
  import {
2
2
  BackupManager,
3
3
  safeParseAndValidate
4
- } from "./chunk-WS6OA7H6.js";
4
+ } from "./chunk-4MJ52WBH.js";
5
5
  import {
6
6
  atomicWriteJSON
7
7
  } from "./chunk-43X6JBEM.js";
8
8
  import {
9
9
  getTrieDirectory
10
- } from "./chunk-45Y5TLQZ.js";
10
+ } from "./chunk-SH7H3WRU.js";
11
11
 
12
- // src/guardian/insight-store.ts
12
+ // src/agent/insight-store.ts
13
13
  import { mkdir, readFile } from "fs/promises";
14
14
  import { existsSync } from "fs";
15
15
  import { join } from "path";
@@ -37,7 +37,7 @@ var InsightDetailsSchema = z.object({
37
37
  patternsCount: z.number().optional(),
38
38
  topPattern: z.string().optional()
39
39
  });
40
- var GuardianInsightSchema = z.object({
40
+ var InsightSchema = z.object({
41
41
  id: z.string(),
42
42
  type: z.enum(["observation", "warning", "suggestion", "celebration", "question"]),
43
43
  message: z.string(),
@@ -53,7 +53,7 @@ var GuardianInsightSchema = z.object({
53
53
  });
54
54
  var InsightStoreDataSchema = z.object({
55
55
  version: z.literal(1),
56
- insights: z.array(GuardianInsightSchema),
56
+ insights: z.array(InsightSchema),
57
57
  cooldowns: z.record(z.string(), z.number()),
58
58
  // insightKey -> timestamp
59
59
  dismissedIds: z.array(z.string()),
@@ -96,7 +96,7 @@ var InsightStore = class _InsightStore {
96
96
  * Get the storage file path
97
97
  */
98
98
  getStorePath() {
99
- return join(getTrieDirectory(this.projectPath), "memory", "guardian-insights.json");
99
+ return join(getTrieDirectory(this.projectPath), "memory", "insights.json");
100
100
  }
101
101
  /**
102
102
  * Create empty data structure
@@ -431,10 +431,10 @@ function clearInsightStores() {
431
431
 
432
432
  export {
433
433
  InsightDetailsSchema,
434
- GuardianInsightSchema,
434
+ InsightSchema,
435
435
  InsightStoreDataSchema,
436
436
  InsightStore,
437
437
  getInsightStore,
438
438
  clearInsightStores
439
439
  };
440
- //# sourceMappingURL=chunk-4YJ6KLGI.js.map
440
+ //# sourceMappingURL=chunk-3MUCUZ46.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/agent/insight-store.ts"],"sourcesContent":["/**\n * Insight Store - Persistent storage for insights\n * \n * Persists to: .trie/memory/insights.json\n * \n * Features:\n * - Insight persistence across restarts\n * - Cooldown state persistence (prevents duplicate insights)\n * - Dismissed insight tracking\n * - Atomic writes with backup rotation\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 { z } from 'zod';\nimport { atomicWriteJSON } from '../utils/atomic-write.js';\nimport { BackupManager } from '../utils/backup-manager.js';\nimport { safeParseAndValidate } from '../memory/validation.js';\n\n// ============================================================================\n// Schemas\n// ============================================================================\n\n/**\n * Schema for insight details\n */\nexport const InsightDetailsSchema = z.object({\n affectedFiles: z.array(z.string()).optional(),\n issueBreakdown: z.record(z.string(), z.number()).optional(),\n examples: z.array(z.string()).optional(),\n trend: z.enum(['improving', 'stable', 'worsening']).optional(),\n comparison: z.string().optional(),\n resolvedCount: z.number().optional(),\n resolvedIssues: z.array(z.object({\n file: z.string(),\n line: z.number().optional(),\n issue: z.string(),\n agent: z.string(),\n resolvedAt: z.string().optional(),\n })).optional(),\n summary: z.string().optional(),\n // Hypothesis-related fields\n hypothesis: z.string().optional(),\n testCriteria: z.string().optional(),\n confidence: z.number().optional(),\n // Pattern-related fields\n patternsCount: z.number().optional(),\n topPattern: z.string().optional(),\n});\n\n/**\n * Schema for an insight\n */\nexport const InsightSchema = z.object({\n id: z.string(),\n type: z.enum(['observation', 'warning', 'suggestion', 'celebration', 'question']),\n message: z.string(),\n context: z.string().optional(),\n suggestedAction: z.string().optional(),\n actionCommand: z.string().optional(),\n relatedIssues: z.array(z.string()),\n priority: z.number().min(1).max(10),\n timestamp: z.number(),\n dismissed: z.boolean(),\n category: z.enum(['security', 'quality', 'performance', 'pattern', 'progress', 'general']),\n details: InsightDetailsSchema.optional(),\n});\n\nexport type Insight = z.infer<typeof InsightSchema>;\n\n/**\n * Schema for the entire insight store file\n */\nexport const InsightStoreDataSchema = z.object({\n version: z.literal(1),\n insights: z.array(InsightSchema),\n cooldowns: z.record(z.string(), z.number()), // insightKey -> timestamp\n dismissedIds: z.array(z.string()), // Track dismissed insight IDs permanently\n lastUpdated: z.string(),\n});\n\nexport type InsightStoreData = z.infer<typeof InsightStoreDataSchema>;\n\n// ============================================================================\n// InsightStore Class\n// ============================================================================\n\n/**\n * Persistent store for insights\n * \n * Usage:\n * ```typescript\n * const store = new InsightStore(projectPath);\n * await store.load();\n * \n * await store.addInsight(insight);\n * const active = store.getActiveInsights();\n * await store.dismissInsight(insightId);\n * ```\n */\nexport class InsightStore {\n private projectPath: string;\n private data: InsightStoreData;\n private loaded: boolean = false;\n private dirty: boolean = false;\n \n // Default cooldown periods (in ms)\n static readonly COOLDOWNS: Record<string, number> = {\n 'pre-push-warning': 60000, // 1 min between pre-push warnings\n 'security-warning': 30000, // 30s between security warnings\n 'new-issues': 30000, // 30s between new issue observations\n 'celebration': 60000, // 1 min between celebrations\n 'pattern-suggestion': 120000, // 2 min between pattern suggestions\n 'accessibility-visual-qa': 300000, // 5 min between visual QA suggestions\n 'goal-suggestion': 300000, // 5 min between goal suggestions\n 'risk-prediction': 180000, // 3 min between risk predictions\n 'hypothesis-update': 600000, // 10 min between hypothesis updates\n 'auto-escalation': 300000, // 5 min between auto-escalations\n };\n \n constructor(projectPath: string) {\n this.projectPath = projectPath;\n this.data = this.createEmptyData();\n }\n \n /**\n * Get the storage file path\n */\n private getStorePath(): string {\n return join(getTrieDirectory(this.projectPath), 'memory', 'insights.json');\n }\n \n /**\n * Create empty data structure\n */\n private createEmptyData(): InsightStoreData {\n return {\n version: 1,\n insights: [],\n cooldowns: {},\n dismissedIds: [],\n lastUpdated: new Date().toISOString(),\n };\n }\n \n /**\n * Load insights from disk\n * \n * If the file is corrupted, attempts recovery from backup.\n * Returns empty data if no valid file/backup exists.\n */\n async load(): Promise<InsightStoreData> {\n if (this.loaded) {\n return this.data;\n }\n \n const storePath = this.getStorePath();\n \n try {\n if (existsSync(storePath)) {\n const content = await readFile(storePath, 'utf-8');\n const result = safeParseAndValidate(content, InsightStoreDataSchema);\n \n if (result.success) {\n this.data = result.data;\n this.loaded = true;\n // Deduplicate any existing duplicate insights\n this.deduplicateInsights();\n return this.data;\n }\n \n // Validation failed - attempt recovery from backup\n console.error(` Insight store corrupted: ${result.error}`);\n const backupManager = new BackupManager(storePath);\n \n if (await backupManager.recoverFromBackup()) {\n console.error(' Recovered from backup');\n const recovered = await readFile(storePath, 'utf-8');\n const recoveredResult = safeParseAndValidate(recovered, InsightStoreDataSchema);\n if (recoveredResult.success) {\n this.data = recoveredResult.data;\n this.loaded = true;\n // Deduplicate any existing duplicate insights\n this.deduplicateInsights();\n return this.data;\n }\n }\n \n console.error(' No valid backup found, starting fresh');\n }\n } catch (error) {\n // File doesn't exist or recovery failed - start fresh\n console.error(` Could not load insight store: ${error}`);\n }\n \n this.data = this.createEmptyData();\n this.loaded = true;\n return this.data;\n }\n \n /**\n * Deduplicate existing insights on load\n * Keeps the most recent instance of each unique insight\n */\n private deduplicateInsights(): void {\n const seen = new Map<string, number>(); // contentKey -> index of first occurrence\n const toRemove: number[] = [];\n \n // Process from newest to oldest (insights are sorted by recency)\n for (let i = 0; i < this.data.insights.length; i++) {\n const insight = this.data.insights[i];\n if (!insight) continue;\n \n const contentKey = this.getContentKey(insight);\n \n if (seen.has(contentKey)) {\n // This is a duplicate - mark for removal\n toRemove.push(i);\n } else {\n seen.set(contentKey, i);\n }\n }\n \n // Remove duplicates (reverse order to preserve indices)\n if (toRemove.length > 0) {\n for (let i = toRemove.length - 1; i >= 0; i--) {\n const idx = toRemove[i];\n if (idx !== undefined) {\n this.data.insights.splice(idx, 1);\n }\n }\n this.dirty = true;\n }\n }\n \n /**\n * Save insights to disk\n * \n * Creates backup before writing, uses atomic write.\n */\n async save(): Promise<void> {\n if (!this.dirty && this.loaded) {\n return; // No changes to save\n }\n \n const storePath = this.getStorePath();\n const memoryDir = join(getTrieDirectory(this.projectPath), 'memory');\n \n // Ensure directory exists\n await mkdir(memoryDir, { recursive: true });\n \n // Create backup before writing\n const backupManager = new BackupManager(storePath);\n await backupManager.createBackup();\n \n // Update timestamp\n this.data.lastUpdated = new Date().toISOString();\n \n // Atomic write\n await atomicWriteJSON(storePath, this.data);\n \n this.dirty = false;\n }\n \n /**\n * Generate a content-based key for deduplication\n * Insights with the same content key are considered duplicates\n */\n private getContentKey(insight: Insight): string {\n // Normalize the message by removing specific numbers/counts that might vary slightly\n // e.g., \"Found 30 security issues\" and \"Found 31 security issues\" are the same insight\n const normalizedMessage = insight.message\n .replace(/\\d+/g, 'N') // Replace all numbers with N\n .toLowerCase()\n .trim();\n \n return `${insight.type}:${insight.category}:${normalizedMessage}`;\n }\n \n /**\n * Add an insight to the store\n * \n * Checks for duplicates using both insight ID and content similarity.\n * If a similar insight already exists (same type, category, and normalized message),\n * updates its timestamp instead of creating a duplicate.\n * Respects cooldowns to prevent insight spam.\n */\n async addInsight(insight: Insight): Promise<boolean> {\n await this.load();\n \n // Check if already exists by ID\n if (this.data.insights.some(i => i.id === insight.id)) {\n return false;\n }\n \n // Check if ID was previously dismissed (don't re-add)\n if (this.data.dismissedIds.includes(insight.id)) {\n return false;\n }\n \n // Check for content-based duplicates (same type, category, similar message)\n const contentKey = this.getContentKey(insight);\n const existingIndex = this.data.insights.findIndex(i => \n !i.dismissed && this.getContentKey(i) === contentKey\n );\n \n if (existingIndex >= 0) {\n // Update existing insight instead of adding duplicate\n const existing = this.data.insights[existingIndex];\n if (existing) {\n // Update with new data but keep the original ID\n existing.timestamp = insight.timestamp;\n existing.message = insight.message; // Update with current counts\n existing.details = insight.details; // Update details\n existing.relatedIssues = insight.relatedIssues;\n existing.suggestedAction = insight.suggestedAction;\n \n // Move to front of list\n this.data.insights.splice(existingIndex, 1);\n this.data.insights.unshift(existing);\n \n this.dirty = true;\n await this.save();\n }\n return false; // Return false to indicate no new insight was created\n }\n \n // Add to front of list\n this.data.insights.unshift(insight);\n \n // Keep only last 100 insights\n if (this.data.insights.length > 100) {\n this.data.insights = this.data.insights.slice(0, 100);\n }\n \n this.dirty = true;\n await this.save();\n \n return true;\n }\n \n /**\n * Check if a cooldown has expired for an insight type\n */\n canCreateInsight(insightKey: string): boolean {\n const lastTime = this.data.cooldowns[insightKey];\n const cooldown = InsightStore.COOLDOWNS[insightKey] || 30000;\n \n if (!lastTime) return true;\n return Date.now() - lastTime > cooldown;\n }\n \n /**\n * Mark that an insight type was created (set cooldown)\n */\n async markInsightCreated(insightKey: string): Promise<void> {\n await this.load();\n this.data.cooldowns[insightKey] = Date.now();\n this.dirty = true;\n await this.save();\n }\n \n /**\n * Get active (non-dismissed) insights\n * \n * Returns insights sorted by priority (highest first),\n * limited to the specified count.\n */\n getActiveInsights(limit: number = 5): Insight[] {\n return this.data.insights\n .filter(i => !i.dismissed)\n .sort((a, b) => b.priority - a.priority)\n .slice(0, limit);\n }\n \n /**\n * Get all insights (including dismissed)\n */\n getAllInsights(): Insight[] {\n return [...this.data.insights];\n }\n \n /**\n * Dismiss an insight by ID\n */\n async dismissInsight(insightId: string): Promise<boolean> {\n await this.load();\n \n const insight = this.data.insights.find(i => i.id === insightId);\n if (!insight) {\n return false;\n }\n \n insight.dismissed = true;\n \n // Track permanently dismissed IDs\n if (!this.data.dismissedIds.includes(insightId)) {\n this.data.dismissedIds.push(insightId);\n \n // Keep only last 500 dismissed IDs\n if (this.data.dismissedIds.length > 500) {\n this.data.dismissedIds = this.data.dismissedIds.slice(-500);\n }\n }\n \n this.dirty = true;\n await this.save();\n \n return true;\n }\n \n /**\n * Remove an insight entirely\n */\n async removeInsight(insightId: string): Promise<boolean> {\n await this.load();\n \n const index = this.data.insights.findIndex(i => i.id === insightId);\n if (index === -1) {\n return false;\n }\n \n this.data.insights.splice(index, 1);\n this.dirty = true;\n await this.save();\n \n return true;\n }\n \n /**\n * Clear all cooldowns\n */\n async clearCooldowns(): Promise<void> {\n await this.load();\n this.data.cooldowns = {};\n this.dirty = true;\n await this.save();\n }\n \n /**\n * Get insight by ID\n */\n getInsight(insightId: string): Insight | undefined {\n return this.data.insights.find(i => i.id === insightId);\n }\n \n /**\n * Update an existing insight\n */\n async updateInsight(insightId: string, updates: Partial<Insight>): Promise<boolean> {\n await this.load();\n \n const insight = this.data.insights.find(i => i.id === insightId);\n if (!insight) {\n return false;\n }\n \n Object.assign(insight, updates);\n this.dirty = true;\n await this.save();\n \n return true;\n }\n \n /**\n * Get insights by category\n */\n getInsightsByCategory(category: Insight['category']): Insight[] {\n return this.data.insights.filter(i => i.category === category && !i.dismissed);\n }\n \n /**\n * Get insights by type\n */\n getInsightsByType(type: Insight['type']): Insight[] {\n return this.data.insights.filter(i => i.type === type && !i.dismissed);\n }\n \n /**\n * Get insights from the last N hours\n */\n getRecentInsights(hours: number = 24): Insight[] {\n const cutoff = Date.now() - (hours * 60 * 60 * 1000);\n return this.data.insights.filter(i => i.timestamp >= cutoff);\n }\n \n /**\n * Get statistics about insights\n */\n getStats(): {\n total: number;\n active: number;\n dismissed: number;\n byCategory: Record<string, number>;\n byType: Record<string, number>;\n } {\n const stats = {\n total: this.data.insights.length,\n active: 0,\n dismissed: 0,\n byCategory: {} as Record<string, number>,\n byType: {} as Record<string, number>,\n };\n \n for (const insight of this.data.insights) {\n if (insight.dismissed) {\n stats.dismissed++;\n } else {\n stats.active++;\n }\n \n stats.byCategory[insight.category] = (stats.byCategory[insight.category] || 0) + 1;\n stats.byType[insight.type] = (stats.byType[insight.type] || 0) + 1;\n }\n \n return stats;\n }\n \n /**\n * Prune old insights (older than N days)\n */\n async pruneOldInsights(daysToKeep: number = 30): Promise<number> {\n await this.load();\n \n const cutoff = Date.now() - (daysToKeep * 24 * 60 * 60 * 1000);\n const originalCount = this.data.insights.length;\n \n this.data.insights = this.data.insights.filter(i => i.timestamp >= cutoff);\n \n const pruned = originalCount - this.data.insights.length;\n if (pruned > 0) {\n this.dirty = true;\n await this.save();\n }\n \n return pruned;\n }\n \n /**\n * Check if the store has been loaded\n */\n isLoaded(): boolean {\n return this.loaded;\n }\n \n /**\n * Force reload from disk\n */\n async reload(): Promise<InsightStoreData> {\n this.loaded = false;\n this.dirty = false;\n return this.load();\n }\n}\n\n// ============================================================================\n// Singleton Management\n// ============================================================================\n\nconst insightStores: Map<string, InsightStore> = new Map();\n\n/**\n * Get the InsightStore for a project (singleton per project)\n */\nexport function getInsightStore(projectPath: string): InsightStore {\n let store = insightStores.get(projectPath);\n if (!store) {\n store = new InsightStore(projectPath);\n insightStores.set(projectPath, store);\n }\n return store;\n}\n\n/**\n * Clear all InsightStore instances (for testing)\n */\nexport function clearInsightStores(): void {\n insightStores.clear();\n}\n"],"mappings":"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","names":[]}
@@ -6,20 +6,18 @@ import {
6
6
  } from "./chunk-43X6JBEM.js";
7
7
  import {
8
8
  getTrieDirectory
9
- } from "./chunk-45Y5TLQZ.js";
10
- import {
11
- __require
12
- } from "./chunk-DGUM43GV.js";
9
+ } from "./chunk-SH7H3WRU.js";
13
10
 
14
11
  // src/context/codebase-index.ts
15
12
  import { readFile, stat } from "fs/promises";
16
13
  import { createHash } from "crypto";
17
14
  import { join } from "path";
18
- import { existsSync } from "fs";
15
+ import { existsSync, readFileSync } from "fs";
19
16
  var CodebaseIndex = class {
20
17
  trie = new Trie();
21
18
  projectPath;
22
19
  indexPath;
20
+ lastUpdated = null;
23
21
  constructor(projectPath) {
24
22
  this.projectPath = projectPath;
25
23
  this.indexPath = join(getTrieDirectory(projectPath), "codebase-index.json");
@@ -31,10 +29,13 @@ var CodebaseIndex = class {
31
29
  load() {
32
30
  if (!existsSync(this.indexPath)) return;
33
31
  try {
34
- const raw = JSON.parse(__require("fs").readFileSync(this.indexPath, "utf-8"));
32
+ const raw = JSON.parse(readFileSync(this.indexPath, "utf-8"));
35
33
  if (raw.trie) {
36
34
  this.trie = Trie.fromJSON(raw.trie);
37
35
  }
36
+ if (raw.lastUpdated) {
37
+ this.lastUpdated = raw.lastUpdated;
38
+ }
38
39
  } catch (error) {
39
40
  console.error("Failed to load codebase index:", error);
40
41
  }
@@ -43,9 +44,10 @@ var CodebaseIndex = class {
43
44
  * Save index to disk
44
45
  */
45
46
  async save() {
47
+ this.lastUpdated = (/* @__PURE__ */ new Date()).toISOString();
46
48
  const data = {
47
49
  version: 1,
48
- lastUpdated: (/* @__PURE__ */ new Date()).toISOString(),
50
+ lastUpdated: this.lastUpdated,
49
51
  fileCount: this.trie.getWithPrefix("").length,
50
52
  trie: this.trie.toJSON()
51
53
  };
@@ -76,8 +78,8 @@ var CodebaseIndex = class {
76
78
  const existing = this.trie.search(filePath);
77
79
  if (existing.found && existing.value) {
78
80
  if (existing.value.hash === hash) {
79
- metadata.violations = existing.value.violations;
80
- metadata.lastScanned = existing.value.lastScanned;
81
+ if (existing.value.violations) metadata.violations = existing.value.violations;
82
+ if (existing.value.lastScanned != null) metadata.lastScanned = existing.value.lastScanned;
81
83
  }
82
84
  }
83
85
  this.trie.insert(filePath, metadata);
@@ -133,7 +135,7 @@ var CodebaseIndex = class {
133
135
  */
134
136
  getFilesByType(type) {
135
137
  const allFiles = this.trie.getWithPrefix("");
136
- return allFiles.map((m) => m.value).filter((v) => v !== null && v.type === type);
138
+ return allFiles.map((m) => m.value).filter((v) => v != null && v.type === type);
137
139
  }
138
140
  /**
139
141
  * Record goal violation scan result for a file
@@ -144,14 +146,15 @@ var CodebaseIndex = class {
144
146
  metadata.lastScanned = Date.now();
145
147
  if (!metadata.violations) metadata.violations = [];
146
148
  metadata.violations = metadata.violations.filter((v) => v.goalId !== goalId);
147
- metadata.violations.push({
149
+ const entry = {
148
150
  goalId,
149
151
  goalDescription,
150
152
  found,
151
- details,
152
- confidence,
153
153
  timestamp: Date.now()
154
- });
154
+ };
155
+ if (details !== void 0) entry.details = details;
156
+ if (confidence !== void 0) entry.confidence = confidence;
157
+ metadata.violations.push(entry);
155
158
  this.trie.insert(filePath, metadata);
156
159
  }
157
160
  /**
@@ -181,7 +184,8 @@ var CodebaseIndex = class {
181
184
  totalSize: allFiles.reduce((sum, f) => sum + f.size, 0),
182
185
  filesByType: {},
183
186
  scannedFiles: allFiles.filter((f) => f.lastScanned).length,
184
- filesWithViolations: allFiles.filter((f) => f.violations && f.violations.some((v) => v.found)).length
187
+ filesWithViolations: allFiles.filter((f) => f.violations && f.violations.some((v) => v.found)).length,
188
+ lastUpdated: this.lastUpdated
185
189
  };
186
190
  for (const file of allFiles) {
187
191
  stats.filesByType[file.type] = (stats.filesByType[file.type] || 0) + 1;
@@ -201,7 +205,7 @@ var CodebaseIndex = class {
201
205
  file.violations = file.violations.filter((v) => v.timestamp > cutoff);
202
206
  cleared += before - file.violations.length;
203
207
  if (file.violations.length === 0) {
204
- file.lastScanned = void 0;
208
+ delete file.lastScanned;
205
209
  }
206
210
  this.trie.insert(file.path, file);
207
211
  }
@@ -213,4 +217,4 @@ var CodebaseIndex = class {
213
217
  export {
214
218
  CodebaseIndex
215
219
  };
216
- //# sourceMappingURL=chunk-6VIMBFUZ.js.map
220
+ //# sourceMappingURL=chunk-3RRXWX3V.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/context/codebase-index.ts"],"sourcesContent":["/**\n * Codebase Index - Fast file indexing for goal violation detection\n * \n * Uses Trie structure for efficient file lookup and caching to avoid\n * re-scanning unchanged files.\n * \n * For large codebases, this dramatically improves goal check performance:\n * - Indexes file hashes to detect changes\n * - Caches AI analysis results\n * - Uses Trie for O(m) lookups where m = path length\n * - Stores file metadata (size, type, last modified, etc.)\n */\n\nimport { readFile, stat } from 'fs/promises';\nimport { createHash } from 'crypto';\nimport { join } from 'path';\nimport { existsSync, readFileSync } from 'fs';\nimport { Trie } from '../trie/trie.js';\nimport { getTrieDirectory } from '../utils/workspace.js';\nimport { atomicWriteJSON } from '../utils/atomic-write.js';\n\nexport interface FileMetadata {\n path: string;\n hash: string; // SHA-256 of content\n size: number;\n lastModified: number;\n type: string; // Extension\n lastScanned?: number; // Timestamp of last AI scan\n violations?: {\n goalId: string;\n goalDescription: string;\n found: boolean;\n details?: string;\n confidence?: number;\n timestamp: number;\n }[];\n}\n\nexport interface CodebaseIndexData {\n version: number;\n lastUpdated: string;\n fileCount: number;\n trie: any; // Serialized Trie\n}\n\nexport class CodebaseIndex {\n private trie: Trie<FileMetadata> = new Trie();\n private projectPath: string;\n private indexPath: string;\n private lastUpdated: string | null = null;\n\n constructor(projectPath: string) {\n this.projectPath = projectPath;\n this.indexPath = join(getTrieDirectory(projectPath), 'codebase-index.json');\n this.load();\n }\n\n /**\n * Load index from disk if it exists\n */\n private load(): void {\n if (!existsSync(this.indexPath)) return;\n\n try {\n const raw = JSON.parse(readFileSync(this.indexPath, 'utf-8'));\n if (raw.trie) {\n this.trie = Trie.fromJSON<FileMetadata>(raw.trie);\n }\n if (raw.lastUpdated) {\n this.lastUpdated = raw.lastUpdated;\n }\n } catch (error) {\n console.error('Failed to load codebase index:', error);\n }\n }\n\n /**\n * Save index to disk\n */\n async save(): Promise<void> {\n this.lastUpdated = new Date().toISOString();\n const data: CodebaseIndexData = {\n version: 1,\n lastUpdated: this.lastUpdated,\n fileCount: this.trie.getWithPrefix('').length,\n trie: this.trie.toJSON(),\n };\n\n await atomicWriteJSON(this.indexPath, data);\n }\n\n /**\n * Add or update a file in the index\n * Returns null if the file doesn't exist or can't be read\n */\n async indexFile(filePath: string): Promise<FileMetadata | null> {\n // Safety check: if filePath is already absolute and doesn't start with projectPath,\n // this is likely a bug (stale cache entry from different project)\n if (filePath.startsWith('/')) {\n // Already absolute - check if it's within our project\n if (!filePath.toLowerCase().startsWith(this.projectPath.toLowerCase())) {\n return null; // Wrong project, skip silently\n }\n // It's absolute but within our project - use as-is\n }\n \n // Build absolute path: if filePath is relative, join with projectPath\n // If filePath is absolute (and within project), use it directly\n const absolutePath = filePath.startsWith('/') ? filePath : join(this.projectPath, filePath);\n \n try {\n const content = await readFile(absolutePath, 'utf-8');\n const stats = await stat(absolutePath);\n const hash = createHash('sha256').update(content).digest('hex');\n\n const metadata: FileMetadata = {\n path: filePath,\n hash,\n size: stats.size,\n lastModified: stats.mtimeMs,\n type: filePath.split('.').pop() || '',\n };\n\n // Check if file exists in index\n const existing = this.trie.search(filePath);\n if (existing.found && existing.value) {\n // File exists - preserve violations if hash matches\n if (existing.value.hash === hash) {\n if (existing.value.violations) metadata.violations = existing.value.violations;\n if (existing.value.lastScanned != null) metadata.lastScanned = existing.value.lastScanned;\n }\n }\n\n this.trie.insert(filePath, metadata);\n return metadata;\n } catch (error) {\n // File doesn't exist, was deleted, or can't be read - return null\n return null;\n }\n }\n\n /**\n * Get file metadata from index\n */\n getFile(filePath: string): FileMetadata | null {\n const result = this.trie.search(filePath);\n return result.found && result.value ? result.value : null;\n }\n\n /**\n * Check if file has changed since last index\n */\n async hasChanged(filePath: string): Promise<boolean> {\n // Safety check for absolute paths from wrong project\n if (filePath.startsWith('/') && !filePath.toLowerCase().startsWith(this.projectPath.toLowerCase())) {\n return true; // Wrong project, treat as changed (will be skipped later)\n }\n \n const metadata = this.getFile(filePath);\n if (!metadata) return true; // Not indexed = changed\n\n // Build absolute path correctly\n const absolutePath = filePath.startsWith('/') ? filePath : join(this.projectPath, filePath);\n try {\n const stats = await stat(absolutePath);\n if (stats.mtimeMs !== metadata.lastModified) return true;\n\n // Double-check with hash for certainty\n const content = await readFile(absolutePath, 'utf-8');\n const hash = createHash('sha256').update(content).digest('hex');\n return hash !== metadata.hash;\n } catch {\n return true; // Error = assume changed\n }\n }\n\n /**\n * Get files in a directory\n */\n getDirectoryFiles(directoryPath: string): FileMetadata[] {\n const prefix = directoryPath.endsWith('/') ? directoryPath : `${directoryPath}/`;\n const matches = this.trie.getWithPrefix(prefix);\n return matches.map(m => m.value).filter((v): v is FileMetadata => v !== null);\n }\n\n /**\n * Get files that match a pattern (uses Trie prefix matching)\n */\n getFilesWithPrefix(prefix: string): FileMetadata[] {\n const matches = this.trie.getWithPrefix(prefix);\n return matches.map(m => m.value).filter((v): v is FileMetadata => v !== null);\n }\n\n /**\n * Get files by type (extension)\n */\n getFilesByType(type: string): FileMetadata[] {\n const allFiles = this.trie.getWithPrefix('');\n return allFiles\n .map(m => m.value)\n .filter((v): v is FileMetadata => v != null && v.type === type);\n }\n\n /**\n * Record goal violation scan result for a file\n */\n recordViolation(\n filePath: string,\n goalId: string,\n goalDescription: string,\n found: boolean,\n details?: string,\n confidence?: number\n ): void {\n const metadata = this.getFile(filePath);\n if (!metadata) return;\n\n metadata.lastScanned = Date.now();\n if (!metadata.violations) metadata.violations = [];\n\n // Remove existing violation for this goal\n metadata.violations = metadata.violations.filter(v => v.goalId !== goalId);\n\n // Add new violation result\n const entry: { goalId: string; goalDescription: string; found: boolean; timestamp: number; details?: string; confidence?: number } = {\n goalId,\n goalDescription,\n found,\n timestamp: Date.now(),\n };\n if (details !== undefined) entry.details = details;\n if (confidence !== undefined) entry.confidence = confidence;\n metadata.violations.push(entry);\n\n this.trie.insert(filePath, metadata);\n }\n\n /**\n * Get cached violation results for a file\n */\n getCachedViolations(filePath: string, goalId?: string): FileMetadata['violations'] {\n const metadata = this.getFile(filePath);\n if (!metadata || !metadata.violations) return undefined;\n\n if (goalId) {\n return metadata.violations.filter(v => v.goalId === goalId);\n }\n\n return metadata.violations;\n }\n\n /**\n * Check if the index is empty (needs initial indexing)\n */\n isEmpty(): boolean {\n return this.trie.getWithPrefix('').length === 0;\n }\n\n /**\n * Get statistics about the index\n */\n getStats(): {\n totalFiles: number;\n totalSize: number;\n filesByType: Record<string, number>;\n scannedFiles: number;\n filesWithViolations: number;\n lastUpdated: string | null;\n } {\n const allFiles = this.trie.getWithPrefix('').map(m => m.value).filter((v): v is FileMetadata => v !== null);\n\n const stats = {\n totalFiles: allFiles.length,\n totalSize: allFiles.reduce((sum, f) => sum + f.size, 0),\n filesByType: {} as Record<string, number>,\n scannedFiles: allFiles.filter(f => f.lastScanned).length,\n filesWithViolations: allFiles.filter(f => f.violations && f.violations.some(v => v.found)).length,\n lastUpdated: this.lastUpdated,\n };\n\n for (const file of allFiles) {\n stats.filesByType[file.type] = (stats.filesByType[file.type] || 0) + 1;\n }\n\n return stats;\n }\n\n /**\n * Clear stale cached results (older than maxAgeMs)\n */\n clearStaleCache(maxAgeMs: number = 7 * 24 * 60 * 60 * 1000): number {\n const allFiles = this.trie.getWithPrefix('').map(m => m.value).filter((v): v is FileMetadata => v !== null);\n const cutoff = Date.now() - maxAgeMs;\n let cleared = 0;\n\n for (const file of allFiles) {\n if (file.violations) {\n const before = file.violations.length;\n file.violations = file.violations.filter(v => v.timestamp > cutoff);\n cleared += before - file.violations.length;\n if (file.violations.length === 0) {\n delete file.lastScanned;\n }\n this.trie.insert(file.path, file);\n }\n }\n\n return cleared;\n }\n}\n"],"mappings":"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","names":[]}
@@ -3,7 +3,7 @@ import {
3
3
  } from "./chunk-6NLHFIYA.js";
4
4
  import {
5
5
  getTrieDirectory
6
- } from "./chunk-45Y5TLQZ.js";
6
+ } from "./chunk-SH7H3WRU.js";
7
7
 
8
8
  // src/context/incident-index.ts
9
9
  import path2 from "path";
@@ -170,4 +170,4 @@ var IncidentIndex = class _IncidentIndex {
170
170
  export {
171
171
  IncidentIndex
172
172
  };
173
- //# sourceMappingURL=chunk-WHIQAGB7.js.map
173
+ //# sourceMappingURL=chunk-4C67GV3O.js.map
@@ -209,10 +209,9 @@ var ProjectSummarySchema = z.object({
209
209
  name: z.string(),
210
210
  path: z.string(),
211
211
  lastScan: z.string(),
212
- healthScore: z.number(),
213
212
  totalIssues: z.number(),
214
213
  patterns: z.array(z.string())
215
- });
214
+ }).passthrough();
216
215
  var PatternSummarySchema = z.object({
217
216
  pattern: z.string(),
218
217
  count: z.number(),
@@ -263,4 +262,4 @@ export {
263
262
  CompactedSummariesIndexSchema,
264
263
  safeParseAndValidate
265
264
  };
266
- //# sourceMappingURL=chunk-WS6OA7H6.js.map
265
+ //# sourceMappingURL=chunk-4MJ52WBH.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});\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":[]}