@rangerchaz/aimem 0.1.5 → 0.2.1

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 (202) hide show
  1. package/README.md +588 -432
  2. package/dist/cli/commands/git.d.ts +0 -0
  3. package/dist/cli/commands/git.d.ts.map +0 -0
  4. package/dist/cli/commands/git.js +0 -0
  5. package/dist/cli/commands/git.js.map +0 -0
  6. package/dist/cli/commands/guardrails.d.ts +3 -0
  7. package/dist/cli/commands/guardrails.d.ts.map +1 -0
  8. package/dist/cli/commands/guardrails.js +429 -0
  9. package/dist/cli/commands/guardrails.js.map +1 -0
  10. package/dist/cli/commands/import.d.ts +0 -0
  11. package/dist/cli/commands/import.d.ts.map +0 -0
  12. package/dist/cli/commands/import.js +0 -0
  13. package/dist/cli/commands/import.js.map +0 -0
  14. package/dist/cli/commands/init.d.ts +0 -0
  15. package/dist/cli/commands/init.d.ts.map +0 -0
  16. package/dist/cli/commands/init.js +0 -0
  17. package/dist/cli/commands/init.js.map +0 -0
  18. package/dist/cli/commands/mcp-serve.d.ts +0 -0
  19. package/dist/cli/commands/mcp-serve.d.ts.map +0 -0
  20. package/dist/cli/commands/mcp-serve.js +0 -0
  21. package/dist/cli/commands/mcp-serve.js.map +0 -0
  22. package/dist/cli/commands/query.d.ts +0 -0
  23. package/dist/cli/commands/query.d.ts.map +0 -0
  24. package/dist/cli/commands/query.js +0 -0
  25. package/dist/cli/commands/query.js.map +0 -0
  26. package/dist/cli/commands/reindex.d.ts +7 -0
  27. package/dist/cli/commands/reindex.d.ts.map +1 -0
  28. package/dist/cli/commands/reindex.js +117 -0
  29. package/dist/cli/commands/reindex.js.map +1 -0
  30. package/dist/cli/commands/setup.d.ts +0 -0
  31. package/dist/cli/commands/setup.d.ts.map +0 -0
  32. package/dist/cli/commands/setup.js +0 -0
  33. package/dist/cli/commands/setup.js.map +0 -0
  34. package/dist/cli/commands/start.d.ts +0 -0
  35. package/dist/cli/commands/start.d.ts.map +0 -0
  36. package/dist/cli/commands/start.js +0 -0
  37. package/dist/cli/commands/start.js.map +0 -0
  38. package/dist/cli/commands/status.d.ts +0 -0
  39. package/dist/cli/commands/status.d.ts.map +0 -0
  40. package/dist/cli/commands/status.js +0 -0
  41. package/dist/cli/commands/status.js.map +0 -0
  42. package/dist/cli/commands/stop.d.ts +0 -0
  43. package/dist/cli/commands/stop.d.ts.map +0 -0
  44. package/dist/cli/commands/stop.js +0 -0
  45. package/dist/cli/commands/stop.js.map +0 -0
  46. package/dist/cli/commands/visualize.d.ts +0 -0
  47. package/dist/cli/commands/visualize.d.ts.map +0 -0
  48. package/dist/cli/commands/visualize.js +0 -0
  49. package/dist/cli/commands/visualize.js.map +0 -0
  50. package/dist/cli/index.d.ts +0 -0
  51. package/dist/cli/index.d.ts.map +0 -0
  52. package/dist/cli/index.js +15 -0
  53. package/dist/cli/index.js.map +1 -1
  54. package/dist/db/index.d.ts +45 -1
  55. package/dist/db/index.d.ts.map +1 -1
  56. package/dist/db/index.js +489 -169
  57. package/dist/db/index.js.map +1 -1
  58. package/dist/db/schema.d.ts +1 -0
  59. package/dist/db/schema.d.ts.map +1 -1
  60. package/dist/db/schema.js +251 -188
  61. package/dist/db/schema.js.map +1 -1
  62. package/dist/extractor/index.d.ts +0 -0
  63. package/dist/extractor/index.d.ts.map +0 -0
  64. package/dist/extractor/index.js +0 -0
  65. package/dist/extractor/index.js.map +0 -0
  66. package/dist/git/extractor.d.ts +0 -0
  67. package/dist/git/extractor.d.ts.map +0 -0
  68. package/dist/git/extractor.js +0 -0
  69. package/dist/git/extractor.js.map +0 -0
  70. package/dist/git/hooks.d.ts +0 -0
  71. package/dist/git/hooks.d.ts.map +0 -0
  72. package/dist/git/hooks.js +0 -0
  73. package/dist/git/hooks.js.map +0 -0
  74. package/dist/git/index.d.ts +0 -0
  75. package/dist/git/index.d.ts.map +0 -0
  76. package/dist/git/index.js +0 -0
  77. package/dist/git/index.js.map +0 -0
  78. package/dist/guardrails/analyzer.d.ts +20 -0
  79. package/dist/guardrails/analyzer.d.ts.map +1 -0
  80. package/dist/guardrails/analyzer.js +329 -0
  81. package/dist/guardrails/analyzer.js.map +1 -0
  82. package/dist/guardrails/calculator.d.ts +46 -0
  83. package/dist/guardrails/calculator.d.ts.map +1 -0
  84. package/dist/guardrails/calculator.js +97 -0
  85. package/dist/guardrails/calculator.js.map +1 -0
  86. package/dist/guardrails/enforcer.d.ts +29 -0
  87. package/dist/guardrails/enforcer.d.ts.map +1 -0
  88. package/dist/guardrails/enforcer.js +180 -0
  89. package/dist/guardrails/enforcer.js.map +1 -0
  90. package/dist/guardrails/index.d.ts +14 -0
  91. package/dist/guardrails/index.d.ts.map +1 -0
  92. package/dist/guardrails/index.js +21 -0
  93. package/dist/guardrails/index.js.map +1 -0
  94. package/dist/guardrails/linter-import.d.ts +31 -0
  95. package/dist/guardrails/linter-import.d.ts.map +1 -0
  96. package/dist/guardrails/linter-import.js +547 -0
  97. package/dist/guardrails/linter-import.js.map +1 -0
  98. package/dist/guardrails/responder.d.ts +28 -0
  99. package/dist/guardrails/responder.d.ts.map +1 -0
  100. package/dist/guardrails/responder.js +98 -0
  101. package/dist/guardrails/responder.js.map +1 -0
  102. package/dist/guardrails/vindication-checker.d.ts +24 -0
  103. package/dist/guardrails/vindication-checker.d.ts.map +1 -0
  104. package/dist/guardrails/vindication-checker.js +209 -0
  105. package/dist/guardrails/vindication-checker.js.map +1 -0
  106. package/dist/guardrails/vindication-queue.d.ts +63 -0
  107. package/dist/guardrails/vindication-queue.d.ts.map +1 -0
  108. package/dist/guardrails/vindication-queue.js +98 -0
  109. package/dist/guardrails/vindication-queue.js.map +1 -0
  110. package/dist/indexer/index.d.ts +0 -0
  111. package/dist/indexer/index.d.ts.map +0 -0
  112. package/dist/indexer/index.js +0 -0
  113. package/dist/indexer/index.js.map +0 -0
  114. package/dist/indexer/parsers/base.d.ts +0 -0
  115. package/dist/indexer/parsers/base.d.ts.map +0 -0
  116. package/dist/indexer/parsers/base.js +0 -0
  117. package/dist/indexer/parsers/base.js.map +0 -0
  118. package/dist/indexer/parsers/cpp.d.ts +0 -0
  119. package/dist/indexer/parsers/cpp.d.ts.map +0 -0
  120. package/dist/indexer/parsers/cpp.js +0 -0
  121. package/dist/indexer/parsers/cpp.js.map +0 -0
  122. package/dist/indexer/parsers/go.d.ts +0 -0
  123. package/dist/indexer/parsers/go.d.ts.map +0 -0
  124. package/dist/indexer/parsers/go.js +0 -0
  125. package/dist/indexer/parsers/go.js.map +0 -0
  126. package/dist/indexer/parsers/java.d.ts +0 -0
  127. package/dist/indexer/parsers/java.d.ts.map +0 -0
  128. package/dist/indexer/parsers/java.js +0 -0
  129. package/dist/indexer/parsers/java.js.map +0 -0
  130. package/dist/indexer/parsers/javascript.d.ts +0 -0
  131. package/dist/indexer/parsers/javascript.d.ts.map +0 -0
  132. package/dist/indexer/parsers/javascript.js +0 -0
  133. package/dist/indexer/parsers/javascript.js.map +0 -0
  134. package/dist/indexer/parsers/kotlin.d.ts +0 -0
  135. package/dist/indexer/parsers/kotlin.d.ts.map +0 -0
  136. package/dist/indexer/parsers/kotlin.js +0 -0
  137. package/dist/indexer/parsers/kotlin.js.map +0 -0
  138. package/dist/indexer/parsers/php.d.ts +0 -0
  139. package/dist/indexer/parsers/php.d.ts.map +0 -0
  140. package/dist/indexer/parsers/php.js +0 -0
  141. package/dist/indexer/parsers/php.js.map +0 -0
  142. package/dist/indexer/parsers/python.d.ts +0 -0
  143. package/dist/indexer/parsers/python.d.ts.map +0 -0
  144. package/dist/indexer/parsers/python.js +0 -0
  145. package/dist/indexer/parsers/python.js.map +0 -0
  146. package/dist/indexer/parsers/ruby.d.ts +0 -0
  147. package/dist/indexer/parsers/ruby.d.ts.map +0 -0
  148. package/dist/indexer/parsers/ruby.js +0 -0
  149. package/dist/indexer/parsers/ruby.js.map +0 -0
  150. package/dist/indexer/parsers/rust.d.ts +0 -0
  151. package/dist/indexer/parsers/rust.d.ts.map +0 -0
  152. package/dist/indexer/parsers/rust.js +0 -0
  153. package/dist/indexer/parsers/rust.js.map +0 -0
  154. package/dist/indexer/watcher-daemon.d.ts +0 -0
  155. package/dist/indexer/watcher-daemon.d.ts.map +0 -0
  156. package/dist/indexer/watcher-daemon.js +10 -0
  157. package/dist/indexer/watcher-daemon.js.map +1 -1
  158. package/dist/indexer/watcher.d.ts +2 -0
  159. package/dist/indexer/watcher.d.ts.map +1 -1
  160. package/dist/indexer/watcher.js +8 -0
  161. package/dist/indexer/watcher.js.map +1 -1
  162. package/dist/mcp/server.d.ts +0 -0
  163. package/dist/mcp/server.d.ts.map +1 -1
  164. package/dist/mcp/server.js +343 -45
  165. package/dist/mcp/server.js.map +1 -1
  166. package/dist/proxy/interceptor-mockttp.d.ts +0 -0
  167. package/dist/proxy/interceptor-mockttp.d.ts.map +0 -0
  168. package/dist/proxy/interceptor-mockttp.js +0 -0
  169. package/dist/proxy/interceptor-mockttp.js.map +0 -0
  170. package/dist/proxy/proxy-daemon.d.ts +0 -0
  171. package/dist/proxy/proxy-daemon.d.ts.map +0 -0
  172. package/dist/proxy/proxy-daemon.js +0 -0
  173. package/dist/proxy/proxy-daemon.js.map +0 -0
  174. package/dist/query/index.d.ts +0 -0
  175. package/dist/query/index.d.ts.map +0 -0
  176. package/dist/query/index.js +0 -0
  177. package/dist/query/index.js.map +0 -0
  178. package/dist/types/index.d.ts +96 -0
  179. package/dist/types/index.d.ts.map +1 -1
  180. package/dist/types/index.js +0 -0
  181. package/dist/types/index.js.map +0 -0
  182. package/dist/visualize/index.d.ts +0 -0
  183. package/dist/visualize/index.d.ts.map +0 -0
  184. package/dist/visualize/index.js +0 -0
  185. package/dist/visualize/index.js.map +0 -0
  186. package/dist/visualize/server.d.ts +0 -0
  187. package/dist/visualize/server.d.ts.map +0 -0
  188. package/dist/visualize/server.js +0 -0
  189. package/dist/visualize/server.js.map +0 -0
  190. package/dist/visualize/template.d.ts +0 -0
  191. package/dist/visualize/template.d.ts.map +0 -0
  192. package/dist/visualize/template.js +0 -0
  193. package/dist/visualize/template.js.map +0 -0
  194. package/package.json +67 -56
  195. package/dist/cli/commands/hook-session-end.d.ts +0 -7
  196. package/dist/cli/commands/hook-session-end.d.ts.map +0 -1
  197. package/dist/cli/commands/hook-session-end.js +0 -109
  198. package/dist/cli/commands/hook-session-end.js.map +0 -1
  199. package/dist/cli/commands/hook-session-start.d.ts +0 -7
  200. package/dist/cli/commands/hook-session-start.d.ts.map +0 -1
  201. package/dist/cli/commands/hook-session-start.js +0 -116
  202. package/dist/cli/commands/hook-session-start.js.map +0 -1
package/dist/db/index.js CHANGED
@@ -2,7 +2,7 @@ import Database from 'better-sqlite3';
2
2
  import { homedir } from 'os';
3
3
  import { join } from 'path';
4
4
  import { mkdirSync, existsSync } from 'fs';
5
- import { SCHEMA, MIGRATIONS, COMMIT_LINKS_SCHEMA } from './schema.js';
5
+ import { SCHEMA, MIGRATIONS, COMMIT_LINKS_SCHEMA, GUARDRAILS_SCHEMA } from './schema.js';
6
6
  function resolveDataDir() {
7
7
  return process.env.AIMEM_DATA_DIR || join(homedir(), '.aimem');
8
8
  }
@@ -28,6 +28,8 @@ function applyMigrations(database) {
28
28
  }
29
29
  // Apply commit_links schema
30
30
  database.exec(COMMIT_LINKS_SCHEMA);
31
+ // Apply guardrails schema
32
+ database.exec(GUARDRAILS_SCHEMA);
31
33
  }
32
34
  export function getDb() {
33
35
  if (!db) {
@@ -69,13 +71,13 @@ export function getAllProjects() {
69
71
  // File operations
70
72
  export function upsertFile(projectId, path, hash) {
71
73
  const db = getDb();
72
- const stmt = db.prepare(`
73
- INSERT INTO files (project_id, path, hash, last_indexed)
74
- VALUES (?, ?, ?, datetime('now'))
75
- ON CONFLICT(project_id, path) DO UPDATE SET
76
- hash = excluded.hash,
77
- last_indexed = datetime('now')
78
- RETURNING *
74
+ const stmt = db.prepare(`
75
+ INSERT INTO files (project_id, path, hash, last_indexed)
76
+ VALUES (?, ?, ?, datetime('now'))
77
+ ON CONFLICT(project_id, path) DO UPDATE SET
78
+ hash = excluded.hash,
79
+ last_indexed = datetime('now')
80
+ RETURNING *
79
81
  `);
80
82
  return stmt.get(projectId, path, hash);
81
83
  }
@@ -98,10 +100,10 @@ export function getProjectFiles(projectId) {
98
100
  // Structure operations
99
101
  export function insertStructure(fileId, type, name, lineStart, lineEnd, signature, rawContent, metadata = {}) {
100
102
  const db = getDb();
101
- const stmt = db.prepare(`
102
- INSERT INTO structures (file_id, type, name, line_start, line_end, signature, raw_content, metadata)
103
- VALUES (?, ?, ?, ?, ?, ?, ?, ?)
104
- RETURNING *
103
+ const stmt = db.prepare(`
104
+ INSERT INTO structures (file_id, type, name, line_start, line_end, signature, raw_content, metadata)
105
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?)
106
+ RETURNING *
105
107
  `);
106
108
  return stmt.get(fileId, type, name, lineStart, lineEnd, signature, rawContent, JSON.stringify(metadata));
107
109
  }
@@ -112,30 +114,30 @@ export function deleteFileStructures(fileId) {
112
114
  export function searchStructures(query, limit = 20, projectId) {
113
115
  const db = getDb();
114
116
  if (projectId) {
115
- return db.prepare(`
116
- SELECT s.* FROM structures s
117
- JOIN structures_fts fts ON s.id = fts.rowid
118
- JOIN files f ON s.file_id = f.id
119
- WHERE structures_fts MATCH ? AND f.project_id = ?
120
- ORDER BY rank
121
- LIMIT ?
117
+ return db.prepare(`
118
+ SELECT s.* FROM structures s
119
+ JOIN structures_fts fts ON s.id = fts.rowid
120
+ JOIN files f ON s.file_id = f.id
121
+ WHERE structures_fts MATCH ? AND f.project_id = ?
122
+ ORDER BY rank
123
+ LIMIT ?
122
124
  `).all(query, projectId, limit);
123
125
  }
124
- return db.prepare(`
125
- SELECT s.* FROM structures s
126
- JOIN structures_fts fts ON s.id = fts.rowid
127
- WHERE structures_fts MATCH ?
128
- ORDER BY rank
129
- LIMIT ?
126
+ return db.prepare(`
127
+ SELECT s.* FROM structures s
128
+ JOIN structures_fts fts ON s.id = fts.rowid
129
+ WHERE structures_fts MATCH ?
130
+ ORDER BY rank
131
+ LIMIT ?
130
132
  `).all(query, limit);
131
133
  }
132
134
  export function getStructuresByName(name, projectId) {
133
135
  const db = getDb();
134
136
  if (projectId) {
135
- return db.prepare(`
136
- SELECT s.* FROM structures s
137
- JOIN files f ON s.file_id = f.id
138
- WHERE s.name = ? AND f.project_id = ?
137
+ return db.prepare(`
138
+ SELECT s.* FROM structures s
139
+ JOIN files f ON s.file_id = f.id
140
+ WHERE s.name = ? AND f.project_id = ?
139
141
  `).all(name, projectId);
140
142
  }
141
143
  return db.prepare('SELECT * FROM structures WHERE name = ?').all(name);
@@ -155,39 +157,39 @@ export function findProjectForPath(targetPath) {
155
157
  // Conversation operations
156
158
  export function insertConversation(rawContent, projectId = null, model = null, tool = null, summary = null) {
157
159
  const db = getDb();
158
- const stmt = db.prepare(`
159
- INSERT INTO conversations (project_id, model, tool, summary, raw_content)
160
- VALUES (?, ?, ?, ?, ?)
161
- RETURNING *
160
+ const stmt = db.prepare(`
161
+ INSERT INTO conversations (project_id, model, tool, summary, raw_content)
162
+ VALUES (?, ?, ?, ?, ?)
163
+ RETURNING *
162
164
  `);
163
165
  return stmt.get(projectId, model, tool, summary, rawContent);
164
166
  }
165
167
  export function searchConversations(query, limit = 20, projectId) {
166
168
  const db = getDb();
167
169
  if (projectId) {
168
- return db.prepare(`
169
- SELECT c.* FROM conversations c
170
- JOIN conversations_fts fts ON c.id = fts.rowid
171
- WHERE conversations_fts MATCH ? AND c.project_id = ?
172
- ORDER BY rank
173
- LIMIT ?
170
+ return db.prepare(`
171
+ SELECT c.* FROM conversations c
172
+ JOIN conversations_fts fts ON c.id = fts.rowid
173
+ WHERE conversations_fts MATCH ? AND c.project_id = ?
174
+ ORDER BY rank
175
+ LIMIT ?
174
176
  `).all(query, projectId, limit);
175
177
  }
176
- return db.prepare(`
177
- SELECT c.* FROM conversations c
178
- JOIN conversations_fts fts ON c.id = fts.rowid
179
- WHERE conversations_fts MATCH ?
180
- ORDER BY rank
181
- LIMIT ?
178
+ return db.prepare(`
179
+ SELECT c.* FROM conversations c
180
+ JOIN conversations_fts fts ON c.id = fts.rowid
181
+ WHERE conversations_fts MATCH ?
182
+ ORDER BY rank
183
+ LIMIT ?
182
184
  `).all(query, limit);
183
185
  }
184
186
  // Link operations
185
187
  export function createLink(sourceType, sourceId, targetType, targetId, linkType) {
186
188
  const db = getDb();
187
- const stmt = db.prepare(`
188
- INSERT OR IGNORE INTO links (source_type, source_id, target_type, target_id, link_type)
189
- VALUES (?, ?, ?, ?, ?)
190
- RETURNING *
189
+ const stmt = db.prepare(`
190
+ INSERT OR IGNORE INTO links (source_type, source_id, target_type, target_id, link_type)
191
+ VALUES (?, ?, ?, ?, ?)
192
+ RETURNING *
191
193
  `);
192
194
  return stmt.get(sourceType, sourceId, targetType, targetId, linkType);
193
195
  }
@@ -202,10 +204,10 @@ export function getLinksTo(targetType, targetId) {
202
204
  // Extraction operations
203
205
  export function insertExtraction(conversationId, type, content, metadata = {}) {
204
206
  const db = getDb();
205
- const stmt = db.prepare(`
206
- INSERT INTO extractions (conversation_id, type, content, metadata)
207
- VALUES (?, ?, ?, ?)
208
- RETURNING *
207
+ const stmt = db.prepare(`
208
+ INSERT INTO extractions (conversation_id, type, content, metadata)
209
+ VALUES (?, ?, ?, ?)
210
+ RETURNING *
209
211
  `);
210
212
  return stmt.get(conversationId, type, content, JSON.stringify(metadata));
211
213
  }
@@ -221,30 +223,30 @@ export function searchExtractions(query, limit = 20, projectId) {
221
223
  const db = getDb();
222
224
  const searchTerm = `%${query}%`;
223
225
  if (projectId) {
224
- return db.prepare(`
225
- SELECT e.* FROM extractions e
226
- JOIN conversations c ON e.conversation_id = c.id
227
- WHERE e.content LIKE ? AND c.project_id = ?
228
- ORDER BY e.id DESC
229
- LIMIT ?
226
+ return db.prepare(`
227
+ SELECT e.* FROM extractions e
228
+ JOIN conversations c ON e.conversation_id = c.id
229
+ WHERE e.content LIKE ? AND c.project_id = ?
230
+ ORDER BY e.id DESC
231
+ LIMIT ?
230
232
  `).all(searchTerm, projectId, limit);
231
233
  }
232
- return db.prepare(`
233
- SELECT * FROM extractions
234
- WHERE content LIKE ?
235
- ORDER BY id DESC
236
- LIMIT ?
234
+ return db.prepare(`
235
+ SELECT * FROM extractions
236
+ WHERE content LIKE ?
237
+ ORDER BY id DESC
238
+ LIMIT ?
237
239
  `).all(searchTerm, limit);
238
240
  }
239
241
  export function isDuplicateExtraction(content, projectId, windowSeconds = 300) {
240
242
  const db = getDb();
241
243
  // Check for duplicate extraction content within the last N seconds
242
- const stmt = db.prepare(`
243
- SELECT COUNT(*) as count FROM extractions e
244
- JOIN conversations c ON e.conversation_id = c.id
245
- WHERE e.content = ?
246
- AND (c.project_id = ? OR (c.project_id IS NULL AND ? IS NULL))
247
- AND datetime(c.timestamp) > datetime('now', '-' || ? || ' seconds')
244
+ const stmt = db.prepare(`
245
+ SELECT COUNT(*) as count FROM extractions e
246
+ JOIN conversations c ON e.conversation_id = c.id
247
+ WHERE e.content = ?
248
+ AND (c.project_id = ? OR (c.project_id IS NULL AND ? IS NULL))
249
+ AND datetime(c.timestamp) > datetime('now', '-' || ? || ' seconds')
248
250
  `);
249
251
  const result = stmt.get(content.trim(), projectId, projectId, windowSeconds);
250
252
  return result.count > 0;
@@ -278,80 +280,97 @@ export function getConversationById(id) {
278
280
  // Get full conversations for a project (for long-term memory)
279
281
  export function getFullConversations(projectId, limit = 50, offset = 0) {
280
282
  const db = getDb();
281
- return db.prepare(`
282
- SELECT * FROM conversations
283
- WHERE project_id = ?
284
- ORDER BY timestamp DESC
285
- LIMIT ? OFFSET ?
283
+ return db.prepare(`
284
+ SELECT * FROM conversations
285
+ WHERE project_id = ?
286
+ ORDER BY timestamp DESC
287
+ LIMIT ? OFFSET ?
286
288
  `).all(projectId, limit, offset);
287
289
  }
290
+ // Get recent conversations (optionally scoped to project)
291
+ export function getRecentConversations(limit = 10, projectId) {
292
+ const db = getDb();
293
+ if (projectId) {
294
+ return db.prepare(`
295
+ SELECT * FROM conversations
296
+ WHERE project_id = ?
297
+ ORDER BY timestamp DESC
298
+ LIMIT ?
299
+ `).all(projectId, limit);
300
+ }
301
+ return db.prepare(`
302
+ SELECT * FROM conversations
303
+ ORDER BY timestamp DESC
304
+ LIMIT ?
305
+ `).all(limit);
306
+ }
288
307
  // Search conversations and return full content
289
308
  export function searchFullConversations(query, limit = 20, projectId) {
290
309
  const db = getDb();
291
310
  if (projectId) {
292
- return db.prepare(`
293
- SELECT c.* FROM conversations c
294
- JOIN conversations_fts fts ON c.id = fts.rowid
295
- WHERE conversations_fts MATCH ? AND c.project_id = ?
296
- ORDER BY rank
297
- LIMIT ?
311
+ return db.prepare(`
312
+ SELECT c.* FROM conversations c
313
+ JOIN conversations_fts fts ON c.id = fts.rowid
314
+ WHERE conversations_fts MATCH ? AND c.project_id = ?
315
+ ORDER BY rank
316
+ LIMIT ?
298
317
  `).all(query, projectId, limit);
299
318
  }
300
- return db.prepare(`
301
- SELECT c.* FROM conversations c
302
- JOIN conversations_fts fts ON c.id = fts.rowid
303
- WHERE conversations_fts MATCH ?
304
- ORDER BY rank
305
- LIMIT ?
319
+ return db.prepare(`
320
+ SELECT c.* FROM conversations c
321
+ JOIN conversations_fts fts ON c.id = fts.rowid
322
+ WHERE conversations_fts MATCH ?
323
+ ORDER BY rank
324
+ LIMIT ?
306
325
  `).all(query, limit);
307
326
  }
308
327
  // Get all structures for a project with file paths
309
328
  export function getAllProjectStructures(projectId) {
310
329
  const db = getDb();
311
- return db.prepare(`
312
- SELECT s.*, f.path as file_path
313
- FROM structures s
314
- JOIN files f ON s.file_id = f.id
315
- WHERE f.project_id = ?
316
- ORDER BY f.path, s.line_start
330
+ return db.prepare(`
331
+ SELECT s.*, f.path as file_path
332
+ FROM structures s
333
+ JOIN files f ON s.file_id = f.id
334
+ WHERE f.project_id = ?
335
+ ORDER BY f.path, s.line_start
317
336
  `).all(projectId);
318
337
  }
319
338
  // Get all links for a project
320
339
  export function getAllProjectLinks(projectId) {
321
340
  const db = getDb();
322
- return db.prepare(`
323
- SELECT DISTINCT l.* FROM links l
324
- LEFT JOIN structures s ON l.source_type = 'structure' AND l.source_id = s.id
325
- LEFT JOIN files f ON s.file_id = f.id
326
- LEFT JOIN conversations c ON l.source_type = 'conversation' AND l.source_id = c.id
327
- WHERE f.project_id = ? OR c.project_id = ?
341
+ return db.prepare(`
342
+ SELECT DISTINCT l.* FROM links l
343
+ LEFT JOIN structures s ON l.source_type = 'structure' AND l.source_id = s.id
344
+ LEFT JOIN files f ON s.file_id = f.id
345
+ LEFT JOIN conversations c ON l.source_type = 'conversation' AND l.source_id = c.id
346
+ WHERE f.project_id = ? OR c.project_id = ?
328
347
  `).all(projectId, projectId);
329
348
  }
330
349
  // Get all extractions for a project
331
350
  export function getAllProjectExtractions(projectId) {
332
351
  const db = getDb();
333
- return db.prepare(`
334
- SELECT e.* FROM extractions e
335
- JOIN conversations c ON e.conversation_id = c.id
336
- WHERE c.project_id = ?
337
- ORDER BY c.timestamp DESC
352
+ return db.prepare(`
353
+ SELECT e.* FROM extractions e
354
+ JOIN conversations c ON e.conversation_id = c.id
355
+ WHERE c.project_id = ?
356
+ ORDER BY c.timestamp DESC
338
357
  `).all(projectId);
339
358
  }
340
359
  // ============ Git Operations ============
341
360
  // Commit operations
342
361
  export function upsertCommit(projectId, hash, shortHash, authorName, authorEmail, timestamp, subject, body, parentHashes = []) {
343
362
  const db = getDb();
344
- const stmt = db.prepare(`
345
- INSERT INTO commits (project_id, hash, short_hash, author_name, author_email, timestamp, subject, body, parent_hashes)
346
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
347
- ON CONFLICT(project_id, hash) DO UPDATE SET
348
- short_hash = excluded.short_hash,
349
- author_name = excluded.author_name,
350
- author_email = excluded.author_email,
351
- subject = excluded.subject,
352
- body = excluded.body,
353
- parent_hashes = excluded.parent_hashes
354
- RETURNING *
363
+ const stmt = db.prepare(`
364
+ INSERT INTO commits (project_id, hash, short_hash, author_name, author_email, timestamp, subject, body, parent_hashes)
365
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
366
+ ON CONFLICT(project_id, hash) DO UPDATE SET
367
+ short_hash = excluded.short_hash,
368
+ author_name = excluded.author_name,
369
+ author_email = excluded.author_email,
370
+ subject = excluded.subject,
371
+ body = excluded.body,
372
+ parent_hashes = excluded.parent_hashes
373
+ RETURNING *
355
374
  `);
356
375
  return stmt.get(projectId, hash, shortHash, authorName, authorEmail, timestamp, subject, body, JSON.stringify(parentHashes));
357
376
  }
@@ -366,36 +385,36 @@ export function getCommitById(id) {
366
385
  export function searchCommits(query, limit = 20, projectId) {
367
386
  const db = getDb();
368
387
  if (projectId) {
369
- return db.prepare(`
370
- SELECT c.* FROM commits c
371
- JOIN commits_fts fts ON c.id = fts.rowid
372
- WHERE commits_fts MATCH ? AND c.project_id = ?
373
- ORDER BY rank
374
- LIMIT ?
388
+ return db.prepare(`
389
+ SELECT c.* FROM commits c
390
+ JOIN commits_fts fts ON c.id = fts.rowid
391
+ WHERE commits_fts MATCH ? AND c.project_id = ?
392
+ ORDER BY rank
393
+ LIMIT ?
375
394
  `).all(query, projectId, limit);
376
395
  }
377
- return db.prepare(`
378
- SELECT c.* FROM commits c
379
- JOIN commits_fts fts ON c.id = fts.rowid
380
- WHERE commits_fts MATCH ?
381
- ORDER BY rank
382
- LIMIT ?
396
+ return db.prepare(`
397
+ SELECT c.* FROM commits c
398
+ JOIN commits_fts fts ON c.id = fts.rowid
399
+ WHERE commits_fts MATCH ?
400
+ ORDER BY rank
401
+ LIMIT ?
383
402
  `).all(query, limit);
384
403
  }
385
404
  export function getRecentCommits(projectId, limit = 50) {
386
405
  const db = getDb();
387
- return db.prepare(`
388
- SELECT * FROM commits WHERE project_id = ?
389
- ORDER BY timestamp DESC LIMIT ?
406
+ return db.prepare(`
407
+ SELECT * FROM commits WHERE project_id = ?
408
+ ORDER BY timestamp DESC LIMIT ?
390
409
  `).all(projectId, limit);
391
410
  }
392
411
  // Commit link operations
393
412
  export function createCommitLink(commitId, targetType, targetId, linkType) {
394
413
  const db = getDb();
395
- const stmt = db.prepare(`
396
- INSERT OR IGNORE INTO commit_links (commit_id, target_type, target_id, link_type)
397
- VALUES (?, ?, ?, ?)
398
- RETURNING *
414
+ const stmt = db.prepare(`
415
+ INSERT OR IGNORE INTO commit_links (commit_id, target_type, target_id, link_type)
416
+ VALUES (?, ?, ?, ?)
417
+ RETURNING *
399
418
  `);
400
419
  return stmt.get(commitId, targetType, targetId, linkType);
401
420
  }
@@ -409,31 +428,31 @@ export function getLinksToCommit(targetType, targetId) {
409
428
  }
410
429
  export function getCommitsForStructure(structureId) {
411
430
  const db = getDb();
412
- return db.prepare(`
413
- SELECT c.* FROM commits c
414
- JOIN commit_links cl ON c.id = cl.commit_id
415
- WHERE cl.target_type = 'structure' AND cl.target_id = ?
416
- ORDER BY c.timestamp DESC
431
+ return db.prepare(`
432
+ SELECT c.* FROM commits c
433
+ JOIN commit_links cl ON c.id = cl.commit_id
434
+ WHERE cl.target_type = 'structure' AND cl.target_id = ?
435
+ ORDER BY c.timestamp DESC
417
436
  `).all(structureId);
418
437
  }
419
438
  export function getCommitsForExtraction(extractionId) {
420
439
  const db = getDb();
421
- return db.prepare(`
422
- SELECT c.* FROM commits c
423
- JOIN commit_links cl ON c.id = cl.commit_id
424
- WHERE cl.target_type = 'extraction' AND cl.target_id = ?
425
- ORDER BY c.timestamp DESC
440
+ return db.prepare(`
441
+ SELECT c.* FROM commits c
442
+ JOIN commit_links cl ON c.id = cl.commit_id
443
+ WHERE cl.target_type = 'extraction' AND cl.target_id = ?
444
+ ORDER BY c.timestamp DESC
426
445
  `).all(extractionId);
427
446
  }
428
447
  // Update structure authorship
429
448
  export function updateStructureAuthorship(structureId, author, authorEmail, commitHash) {
430
449
  const db = getDb();
431
- db.prepare(`
432
- UPDATE structures SET
433
- last_author = ?,
434
- last_author_email = ?,
435
- last_commit_hash = ?
436
- WHERE id = ?
450
+ db.prepare(`
451
+ UPDATE structures SET
452
+ last_author = ?,
453
+ last_author_email = ?,
454
+ last_commit_hash = ?
455
+ WHERE id = ?
437
456
  `).run(author, authorEmail, commitHash, structureId);
438
457
  }
439
458
  // Get uncommitted decisions (extractions created since last commit)
@@ -441,28 +460,329 @@ export function getUncommittedExtractions(projectId, sinceCommitHash) {
441
460
  const db = getDb();
442
461
  if (sinceCommitHash) {
443
462
  // Get extractions created after the specified commit's timestamp
444
- return db.prepare(`
445
- SELECT e.* FROM extractions e
446
- JOIN conversations c ON e.conversation_id = c.id
447
- WHERE c.project_id = ?
448
- AND c.timestamp > (
449
- SELECT timestamp FROM commits WHERE project_id = ? AND hash = ?
450
- )
451
- AND e.id NOT IN (
452
- SELECT target_id FROM commit_links WHERE target_type = 'extraction'
453
- )
454
- ORDER BY c.timestamp DESC
463
+ return db.prepare(`
464
+ SELECT e.* FROM extractions e
465
+ JOIN conversations c ON e.conversation_id = c.id
466
+ WHERE c.project_id = ?
467
+ AND c.timestamp > (
468
+ SELECT timestamp FROM commits WHERE project_id = ? AND hash = ?
469
+ )
470
+ AND e.id NOT IN (
471
+ SELECT target_id FROM commit_links WHERE target_type = 'extraction'
472
+ )
473
+ ORDER BY c.timestamp DESC
455
474
  `).all(projectId, projectId, sinceCommitHash);
456
475
  }
457
476
  // Get all extractions not linked to any commit
458
- return db.prepare(`
459
- SELECT e.* FROM extractions e
460
- JOIN conversations c ON e.conversation_id = c.id
461
- WHERE c.project_id = ?
462
- AND e.id NOT IN (
463
- SELECT target_id FROM commit_links WHERE target_type = 'extraction'
464
- )
465
- ORDER BY c.timestamp DESC
477
+ return db.prepare(`
478
+ SELECT e.* FROM extractions e
479
+ JOIN conversations c ON e.conversation_id = c.id
480
+ WHERE c.project_id = ?
481
+ AND e.id NOT IN (
482
+ SELECT target_id FROM commit_links WHERE target_type = 'extraction'
483
+ )
484
+ ORDER BY c.timestamp DESC
485
+ `).all(projectId);
486
+ }
487
+ // ============ Guardrails Operations (DIK) ============
488
+ // Guardrail CRUD
489
+ export function insertGuardrail(projectId, category, rule, rationale = null, severity = 'warn', source = 'explicit', sourceFile = null) {
490
+ const db = getDb();
491
+ const stmt = db.prepare(`
492
+ INSERT INTO guardrails (project_id, category, rule, rationale, severity, source, source_file)
493
+ VALUES (?, ?, ?, ?, ?, ?, ?)
494
+ RETURNING *
495
+ `);
496
+ return stmt.get(projectId, category, rule, rationale, severity, source, sourceFile);
497
+ }
498
+ export function getGuardrail(id) {
499
+ const db = getDb();
500
+ return db.prepare('SELECT * FROM guardrails WHERE id = ?').get(id);
501
+ }
502
+ export function getProjectGuardrails(projectId, options = {}) {
503
+ const db = getDb();
504
+ const { category, confirmedOnly = false, activeOnly = true } = options;
505
+ let sql = 'SELECT * FROM guardrails WHERE project_id = ?';
506
+ const params = [projectId];
507
+ if (category) {
508
+ sql += ' AND category = ?';
509
+ params.push(category);
510
+ }
511
+ if (confirmedOnly) {
512
+ sql += ' AND confirmed = 1';
513
+ }
514
+ if (activeOnly) {
515
+ sql += ' AND active = 1';
516
+ }
517
+ sql += ' ORDER BY created_at DESC';
518
+ return db.prepare(sql).all(...params);
519
+ }
520
+ export function confirmGuardrail(id) {
521
+ const db = getDb();
522
+ const result = db.prepare('UPDATE guardrails SET confirmed = 1 WHERE id = ?').run(id);
523
+ return result.changes > 0;
524
+ }
525
+ export function deactivateGuardrail(id) {
526
+ const db = getDb();
527
+ const result = db.prepare('UPDATE guardrails SET active = 0 WHERE id = ?').run(id);
528
+ return result.changes > 0;
529
+ }
530
+ export function deleteGuardrail(id) {
531
+ const db = getDb();
532
+ const result = db.prepare('DELETE FROM guardrails WHERE id = ?').run(id);
533
+ return result.changes > 0;
534
+ }
535
+ // Guardrail events
536
+ export function insertGuardrailEvent(guardrailId, eventType, context = null, response = null, dikLevel = null) {
537
+ const db = getDb();
538
+ const stmt = db.prepare(`
539
+ INSERT INTO guardrail_events (guardrail_id, event_type, context, response, dik_level)
540
+ VALUES (?, ?, ?, ?, ?)
541
+ RETURNING *
542
+ `);
543
+ return stmt.get(guardrailId, eventType, context, response, dikLevel);
544
+ }
545
+ export function getGuardrailEvents(guardrailId) {
546
+ const db = getDb();
547
+ return db.prepare('SELECT * FROM guardrail_events WHERE guardrail_id = ? ORDER BY timestamp DESC').all(guardrailId);
548
+ }
549
+ export function getGuardrailEvent(id) {
550
+ const db = getDb();
551
+ return db.prepare('SELECT * FROM guardrail_events WHERE id = ?').get(id);
552
+ }
553
+ export function getOverrideEvents(projectId) {
554
+ const db = getDb();
555
+ return db.prepare(`
556
+ SELECT e.* FROM guardrail_events e
557
+ JOIN guardrails g ON e.guardrail_id = g.id
558
+ WHERE g.project_id = ? AND e.event_type = 'overridden'
559
+ ORDER BY e.timestamp DESC
560
+ `).all(projectId);
561
+ }
562
+ // Project DIK
563
+ export function getOrCreateProjectDik(projectId) {
564
+ const db = getDb();
565
+ let dik = db.prepare('SELECT * FROM project_dik WHERE project_id = ?').get(projectId);
566
+ if (!dik) {
567
+ dik = db.prepare(`
568
+ INSERT INTO project_dik (project_id) VALUES (?) RETURNING *
569
+ `).get(projectId);
570
+ }
571
+ return dik;
572
+ }
573
+ export function updateProjectDik(projectId, updates) {
574
+ const db = getDb();
575
+ // Build dynamic update query
576
+ const fields = [];
577
+ const values = [];
578
+ if (updates.level !== undefined) {
579
+ fields.push('level = ?');
580
+ values.push(updates.level);
581
+ }
582
+ if (updates.rules_confirmed !== undefined) {
583
+ fields.push('rules_confirmed = ?');
584
+ values.push(updates.rules_confirmed);
585
+ }
586
+ if (updates.rules_inferred !== undefined) {
587
+ fields.push('rules_inferred = ?');
588
+ values.push(updates.rules_inferred);
589
+ }
590
+ if (updates.conversations !== undefined) {
591
+ fields.push('conversations = ?');
592
+ values.push(updates.conversations);
593
+ }
594
+ if (updates.corrections_made !== undefined) {
595
+ fields.push('corrections_made = ?');
596
+ values.push(updates.corrections_made);
597
+ }
598
+ if (updates.overrides_regretted !== undefined) {
599
+ fields.push('overrides_regretted = ?');
600
+ values.push(updates.overrides_regretted);
601
+ }
602
+ fields.push("last_updated = datetime('now')");
603
+ values.push(projectId);
604
+ const sql = `UPDATE project_dik SET ${fields.join(', ')} WHERE project_id = ? RETURNING *`;
605
+ return db.prepare(sql).get(...values);
606
+ }
607
+ export function incrementDikCounter(projectId, counter) {
608
+ const db = getDb();
609
+ // Ensure project_dik exists
610
+ getOrCreateProjectDik(projectId);
611
+ db.prepare(`UPDATE project_dik SET ${counter} = ${counter} + 1, last_updated = datetime('now') WHERE project_id = ?`).run(projectId);
612
+ }
613
+ // Get guardrail history for a specific rule (for response generation)
614
+ export function getGuardrailHistory(guardrailId) {
615
+ const db = getDb();
616
+ const events = db.prepare(`
617
+ SELECT event_type, COUNT(*) as count FROM guardrail_events
618
+ WHERE guardrail_id = ?
619
+ GROUP BY event_type
620
+ `).all(guardrailId);
621
+ let overrides = 0;
622
+ let vindicated = false;
623
+ for (const e of events) {
624
+ if (e.event_type === 'overridden')
625
+ overrides = e.count;
626
+ if (e.event_type === 'vindicated')
627
+ vindicated = true;
628
+ }
629
+ return { overrides, vindicated };
630
+ }
631
+ // Toggle ambient personality mode
632
+ export function setAmbientPersonality(projectId, enabled) {
633
+ const db = getDb();
634
+ // Ensure project_dik exists
635
+ getOrCreateProjectDik(projectId);
636
+ db.prepare(`UPDATE project_dik SET ambient_personality = ?, last_updated = datetime('now') WHERE project_id = ?`).run(enabled ? 1 : 0, projectId);
637
+ }
638
+ // Get guardrails config for a project
639
+ export function getGuardrailsConfig(projectId) {
640
+ const dik = getOrCreateProjectDik(projectId);
641
+ return {
642
+ enabled: true, // Guardrails always enabled if project_dik exists
643
+ ambient_personality: dik.ambient_personality === 1,
644
+ };
645
+ }
646
+ // Manually set DIK level (overrides calculated value)
647
+ export function setDikLevel(projectId, level) {
648
+ const db = getDb();
649
+ const clampedLevel = Math.max(1, Math.min(10, level));
650
+ getOrCreateProjectDik(projectId);
651
+ db.prepare(`UPDATE project_dik SET level = ?, last_updated = datetime('now') WHERE project_id = ?`).run(clampedLevel, projectId);
652
+ }
653
+ // Check if DIK level is manually set (level != 2 default and stats don't match)
654
+ export function isDikManuallySet(projectId) {
655
+ const dik = getOrCreateProjectDik(projectId);
656
+ // If level is non-default but stats are zero, it's manually set
657
+ return dik.level !== 2 && dik.rules_confirmed === 0 && dik.corrections_made === 0 && dik.overrides_regretted === 0 && dik.conversations === 0;
658
+ }
659
+ // ============ Vindication Operations ============
660
+ // Insert guardrail event with vindication context (enhanced version)
661
+ export function insertGuardrailEventWithContext(guardrailId, eventType, context = null, response = null, dikLevel = null, vindicationContext) {
662
+ const db = getDb();
663
+ if (vindicationContext && eventType === 'overridden') {
664
+ const stmt = db.prepare(`
665
+ INSERT INTO guardrail_events (
666
+ guardrail_id, event_type, context, response, dik_level,
667
+ suggestion, code_context, file_path, line_start, line_end, content_hash, vindication_pending
668
+ )
669
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 1)
670
+ RETURNING *
671
+ `);
672
+ return stmt.get(guardrailId, eventType, context, response, dikLevel, vindicationContext.suggestion || null, vindicationContext.codeContext || null, vindicationContext.filePath || null, vindicationContext.lineStart || null, vindicationContext.lineEnd || null, vindicationContext.contentHash || null);
673
+ }
674
+ // Fall back to regular insert for non-override events
675
+ return insertGuardrailEvent(guardrailId, eventType, context, response, dikLevel);
676
+ }
677
+ // Get pending vindications for a project
678
+ export function getPendingVindications(projectId) {
679
+ const db = getDb();
680
+ const rows = db.prepare(`
681
+ SELECT
682
+ e.id as eventId,
683
+ e.guardrail_id as guardrailId,
684
+ g.project_id as projectId,
685
+ e.suggestion,
686
+ e.file_path as filePath,
687
+ e.line_start as lineStart,
688
+ e.line_end as lineEnd,
689
+ e.code_context as originalCode,
690
+ e.content_hash as contentHash,
691
+ e.context as reason,
692
+ e.timestamp
693
+ FROM guardrail_events e
694
+ JOIN guardrails g ON e.guardrail_id = g.id
695
+ WHERE g.project_id = ?
696
+ AND e.event_type = 'overridden'
697
+ AND e.vindication_pending = 1
698
+ AND e.suggestion IS NOT NULL
699
+ AND e.file_path IS NOT NULL
700
+ ORDER BY e.timestamp DESC
701
+ `).all(projectId);
702
+ return rows;
703
+ }
704
+ // Get pending vindications for specific files
705
+ export function getPendingVindicationsForFile(projectId, filePath) {
706
+ const db = getDb();
707
+ const rows = db.prepare(`
708
+ SELECT
709
+ e.id as eventId,
710
+ e.guardrail_id as guardrailId,
711
+ g.project_id as projectId,
712
+ e.suggestion,
713
+ e.file_path as filePath,
714
+ e.line_start as lineStart,
715
+ e.line_end as lineEnd,
716
+ e.code_context as originalCode,
717
+ e.content_hash as contentHash,
718
+ e.context as reason,
719
+ e.timestamp
720
+ FROM guardrail_events e
721
+ JOIN guardrails g ON e.guardrail_id = g.id
722
+ WHERE g.project_id = ?
723
+ AND e.file_path = ?
724
+ AND e.event_type = 'overridden'
725
+ AND e.vindication_pending = 1
726
+ AND e.suggestion IS NOT NULL
727
+ ORDER BY e.timestamp DESC
728
+ `).all(projectId, filePath);
729
+ return rows;
730
+ }
731
+ // Mark a vindication as checked (not vindicated)
732
+ export function markVindicationChecked(eventId) {
733
+ const db = getDb();
734
+ db.prepare(`
735
+ UPDATE guardrail_events
736
+ SET vindication_pending = 0, checked_at = datetime('now')
737
+ WHERE id = ?
738
+ `).run(eventId);
739
+ }
740
+ // Mark a vindication as complete (vindicated)
741
+ export function markVindicated(eventId) {
742
+ const db = getDb();
743
+ db.prepare(`
744
+ UPDATE guardrail_events
745
+ SET vindication_pending = 0, checked_at = datetime('now')
746
+ WHERE id = ?
747
+ `).run(eventId);
748
+ }
749
+ // Expire old pending vindications
750
+ export function expireOldVindications(projectId, maxAgeDays = 30) {
751
+ const db = getDb();
752
+ const result = db.prepare(`
753
+ UPDATE guardrail_events
754
+ SET vindication_pending = 0, checked_at = datetime('now')
755
+ WHERE id IN (
756
+ SELECT e.id FROM guardrail_events e
757
+ JOIN guardrails g ON e.guardrail_id = g.id
758
+ WHERE g.project_id = ?
759
+ AND e.vindication_pending = 1
760
+ AND datetime(e.timestamp) < datetime('now', '-' || ? || ' days')
761
+ )
762
+ `).run(projectId, maxAgeDays);
763
+ return result.changes;
764
+ }
765
+ // Get all vindicated events for a project (for CLI display)
766
+ export function getVindicatedEvents(projectId) {
767
+ const db = getDb();
768
+ return db.prepare(`
769
+ SELECT e.* FROM guardrail_events e
770
+ JOIN guardrails g ON e.guardrail_id = g.id
771
+ WHERE g.project_id = ? AND e.event_type = 'vindicated'
772
+ ORDER BY e.timestamp DESC
466
773
  `).all(projectId);
467
774
  }
775
+ export function getOverrideEventsWithRules(projectId, pendingOnly = false) {
776
+ const db = getDb();
777
+ let sql = `
778
+ SELECT e.*, g.rule, g.category FROM guardrail_events e
779
+ JOIN guardrails g ON e.guardrail_id = g.id
780
+ WHERE g.project_id = ? AND e.event_type = 'overridden'
781
+ `;
782
+ if (pendingOnly) {
783
+ sql += ' AND e.vindication_pending = 1';
784
+ }
785
+ sql += ' ORDER BY e.timestamp DESC';
786
+ return db.prepare(sql).all(projectId);
787
+ }
468
788
  //# sourceMappingURL=index.js.map