@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.
- package/README.md +588 -432
- package/dist/cli/commands/git.d.ts +0 -0
- package/dist/cli/commands/git.d.ts.map +0 -0
- package/dist/cli/commands/git.js +0 -0
- package/dist/cli/commands/git.js.map +0 -0
- package/dist/cli/commands/guardrails.d.ts +3 -0
- package/dist/cli/commands/guardrails.d.ts.map +1 -0
- package/dist/cli/commands/guardrails.js +429 -0
- package/dist/cli/commands/guardrails.js.map +1 -0
- package/dist/cli/commands/import.d.ts +0 -0
- package/dist/cli/commands/import.d.ts.map +0 -0
- package/dist/cli/commands/import.js +0 -0
- package/dist/cli/commands/import.js.map +0 -0
- package/dist/cli/commands/init.d.ts +0 -0
- package/dist/cli/commands/init.d.ts.map +0 -0
- package/dist/cli/commands/init.js +0 -0
- package/dist/cli/commands/init.js.map +0 -0
- package/dist/cli/commands/mcp-serve.d.ts +0 -0
- package/dist/cli/commands/mcp-serve.d.ts.map +0 -0
- package/dist/cli/commands/mcp-serve.js +0 -0
- package/dist/cli/commands/mcp-serve.js.map +0 -0
- package/dist/cli/commands/query.d.ts +0 -0
- package/dist/cli/commands/query.d.ts.map +0 -0
- package/dist/cli/commands/query.js +0 -0
- package/dist/cli/commands/query.js.map +0 -0
- package/dist/cli/commands/reindex.d.ts +7 -0
- package/dist/cli/commands/reindex.d.ts.map +1 -0
- package/dist/cli/commands/reindex.js +117 -0
- package/dist/cli/commands/reindex.js.map +1 -0
- package/dist/cli/commands/setup.d.ts +0 -0
- package/dist/cli/commands/setup.d.ts.map +0 -0
- package/dist/cli/commands/setup.js +0 -0
- package/dist/cli/commands/setup.js.map +0 -0
- package/dist/cli/commands/start.d.ts +0 -0
- package/dist/cli/commands/start.d.ts.map +0 -0
- package/dist/cli/commands/start.js +0 -0
- package/dist/cli/commands/start.js.map +0 -0
- package/dist/cli/commands/status.d.ts +0 -0
- package/dist/cli/commands/status.d.ts.map +0 -0
- package/dist/cli/commands/status.js +0 -0
- package/dist/cli/commands/status.js.map +0 -0
- package/dist/cli/commands/stop.d.ts +0 -0
- package/dist/cli/commands/stop.d.ts.map +0 -0
- package/dist/cli/commands/stop.js +0 -0
- package/dist/cli/commands/stop.js.map +0 -0
- package/dist/cli/commands/visualize.d.ts +0 -0
- package/dist/cli/commands/visualize.d.ts.map +0 -0
- package/dist/cli/commands/visualize.js +0 -0
- package/dist/cli/commands/visualize.js.map +0 -0
- package/dist/cli/index.d.ts +0 -0
- package/dist/cli/index.d.ts.map +0 -0
- package/dist/cli/index.js +15 -0
- package/dist/cli/index.js.map +1 -1
- package/dist/db/index.d.ts +45 -1
- package/dist/db/index.d.ts.map +1 -1
- package/dist/db/index.js +489 -169
- package/dist/db/index.js.map +1 -1
- package/dist/db/schema.d.ts +1 -0
- package/dist/db/schema.d.ts.map +1 -1
- package/dist/db/schema.js +251 -188
- package/dist/db/schema.js.map +1 -1
- package/dist/extractor/index.d.ts +0 -0
- package/dist/extractor/index.d.ts.map +0 -0
- package/dist/extractor/index.js +0 -0
- package/dist/extractor/index.js.map +0 -0
- package/dist/git/extractor.d.ts +0 -0
- package/dist/git/extractor.d.ts.map +0 -0
- package/dist/git/extractor.js +0 -0
- package/dist/git/extractor.js.map +0 -0
- package/dist/git/hooks.d.ts +0 -0
- package/dist/git/hooks.d.ts.map +0 -0
- package/dist/git/hooks.js +0 -0
- package/dist/git/hooks.js.map +0 -0
- package/dist/git/index.d.ts +0 -0
- package/dist/git/index.d.ts.map +0 -0
- package/dist/git/index.js +0 -0
- package/dist/git/index.js.map +0 -0
- package/dist/guardrails/analyzer.d.ts +20 -0
- package/dist/guardrails/analyzer.d.ts.map +1 -0
- package/dist/guardrails/analyzer.js +329 -0
- package/dist/guardrails/analyzer.js.map +1 -0
- package/dist/guardrails/calculator.d.ts +46 -0
- package/dist/guardrails/calculator.d.ts.map +1 -0
- package/dist/guardrails/calculator.js +97 -0
- package/dist/guardrails/calculator.js.map +1 -0
- package/dist/guardrails/enforcer.d.ts +29 -0
- package/dist/guardrails/enforcer.d.ts.map +1 -0
- package/dist/guardrails/enforcer.js +180 -0
- package/dist/guardrails/enforcer.js.map +1 -0
- package/dist/guardrails/index.d.ts +14 -0
- package/dist/guardrails/index.d.ts.map +1 -0
- package/dist/guardrails/index.js +21 -0
- package/dist/guardrails/index.js.map +1 -0
- package/dist/guardrails/linter-import.d.ts +31 -0
- package/dist/guardrails/linter-import.d.ts.map +1 -0
- package/dist/guardrails/linter-import.js +547 -0
- package/dist/guardrails/linter-import.js.map +1 -0
- package/dist/guardrails/responder.d.ts +28 -0
- package/dist/guardrails/responder.d.ts.map +1 -0
- package/dist/guardrails/responder.js +98 -0
- package/dist/guardrails/responder.js.map +1 -0
- package/dist/guardrails/vindication-checker.d.ts +24 -0
- package/dist/guardrails/vindication-checker.d.ts.map +1 -0
- package/dist/guardrails/vindication-checker.js +209 -0
- package/dist/guardrails/vindication-checker.js.map +1 -0
- package/dist/guardrails/vindication-queue.d.ts +63 -0
- package/dist/guardrails/vindication-queue.d.ts.map +1 -0
- package/dist/guardrails/vindication-queue.js +98 -0
- package/dist/guardrails/vindication-queue.js.map +1 -0
- package/dist/indexer/index.d.ts +0 -0
- package/dist/indexer/index.d.ts.map +0 -0
- package/dist/indexer/index.js +0 -0
- package/dist/indexer/index.js.map +0 -0
- package/dist/indexer/parsers/base.d.ts +0 -0
- package/dist/indexer/parsers/base.d.ts.map +0 -0
- package/dist/indexer/parsers/base.js +0 -0
- package/dist/indexer/parsers/base.js.map +0 -0
- package/dist/indexer/parsers/cpp.d.ts +0 -0
- package/dist/indexer/parsers/cpp.d.ts.map +0 -0
- package/dist/indexer/parsers/cpp.js +0 -0
- package/dist/indexer/parsers/cpp.js.map +0 -0
- package/dist/indexer/parsers/go.d.ts +0 -0
- package/dist/indexer/parsers/go.d.ts.map +0 -0
- package/dist/indexer/parsers/go.js +0 -0
- package/dist/indexer/parsers/go.js.map +0 -0
- package/dist/indexer/parsers/java.d.ts +0 -0
- package/dist/indexer/parsers/java.d.ts.map +0 -0
- package/dist/indexer/parsers/java.js +0 -0
- package/dist/indexer/parsers/java.js.map +0 -0
- package/dist/indexer/parsers/javascript.d.ts +0 -0
- package/dist/indexer/parsers/javascript.d.ts.map +0 -0
- package/dist/indexer/parsers/javascript.js +0 -0
- package/dist/indexer/parsers/javascript.js.map +0 -0
- package/dist/indexer/parsers/kotlin.d.ts +0 -0
- package/dist/indexer/parsers/kotlin.d.ts.map +0 -0
- package/dist/indexer/parsers/kotlin.js +0 -0
- package/dist/indexer/parsers/kotlin.js.map +0 -0
- package/dist/indexer/parsers/php.d.ts +0 -0
- package/dist/indexer/parsers/php.d.ts.map +0 -0
- package/dist/indexer/parsers/php.js +0 -0
- package/dist/indexer/parsers/php.js.map +0 -0
- package/dist/indexer/parsers/python.d.ts +0 -0
- package/dist/indexer/parsers/python.d.ts.map +0 -0
- package/dist/indexer/parsers/python.js +0 -0
- package/dist/indexer/parsers/python.js.map +0 -0
- package/dist/indexer/parsers/ruby.d.ts +0 -0
- package/dist/indexer/parsers/ruby.d.ts.map +0 -0
- package/dist/indexer/parsers/ruby.js +0 -0
- package/dist/indexer/parsers/ruby.js.map +0 -0
- package/dist/indexer/parsers/rust.d.ts +0 -0
- package/dist/indexer/parsers/rust.d.ts.map +0 -0
- package/dist/indexer/parsers/rust.js +0 -0
- package/dist/indexer/parsers/rust.js.map +0 -0
- package/dist/indexer/watcher-daemon.d.ts +0 -0
- package/dist/indexer/watcher-daemon.d.ts.map +0 -0
- package/dist/indexer/watcher-daemon.js +10 -0
- package/dist/indexer/watcher-daemon.js.map +1 -1
- package/dist/indexer/watcher.d.ts +2 -0
- package/dist/indexer/watcher.d.ts.map +1 -1
- package/dist/indexer/watcher.js +8 -0
- package/dist/indexer/watcher.js.map +1 -1
- package/dist/mcp/server.d.ts +0 -0
- package/dist/mcp/server.d.ts.map +1 -1
- package/dist/mcp/server.js +343 -45
- package/dist/mcp/server.js.map +1 -1
- package/dist/proxy/interceptor-mockttp.d.ts +0 -0
- package/dist/proxy/interceptor-mockttp.d.ts.map +0 -0
- package/dist/proxy/interceptor-mockttp.js +0 -0
- package/dist/proxy/interceptor-mockttp.js.map +0 -0
- package/dist/proxy/proxy-daemon.d.ts +0 -0
- package/dist/proxy/proxy-daemon.d.ts.map +0 -0
- package/dist/proxy/proxy-daemon.js +0 -0
- package/dist/proxy/proxy-daemon.js.map +0 -0
- package/dist/query/index.d.ts +0 -0
- package/dist/query/index.d.ts.map +0 -0
- package/dist/query/index.js +0 -0
- package/dist/query/index.js.map +0 -0
- package/dist/types/index.d.ts +96 -0
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/index.js +0 -0
- package/dist/types/index.js.map +0 -0
- package/dist/visualize/index.d.ts +0 -0
- package/dist/visualize/index.d.ts.map +0 -0
- package/dist/visualize/index.js +0 -0
- package/dist/visualize/index.js.map +0 -0
- package/dist/visualize/server.d.ts +0 -0
- package/dist/visualize/server.d.ts.map +0 -0
- package/dist/visualize/server.js +0 -0
- package/dist/visualize/server.js.map +0 -0
- package/dist/visualize/template.d.ts +0 -0
- package/dist/visualize/template.d.ts.map +0 -0
- package/dist/visualize/template.js +0 -0
- package/dist/visualize/template.js.map +0 -0
- package/package.json +67 -56
- package/dist/cli/commands/hook-session-end.d.ts +0 -7
- package/dist/cli/commands/hook-session-end.d.ts.map +0 -1
- package/dist/cli/commands/hook-session-end.js +0 -109
- package/dist/cli/commands/hook-session-end.js.map +0 -1
- package/dist/cli/commands/hook-session-start.d.ts +0 -7
- package/dist/cli/commands/hook-session-start.d.ts.map +0 -1
- package/dist/cli/commands/hook-session-start.js +0 -116
- 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
|