@coreyuan/vector-mind 1.0.53 → 1.1.0
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 +99 -72
- package/admin-panel/README.md +77 -0
- package/admin-panel/dist/client/assets/index-Bzyjdhh2.css +1 -0
- package/admin-panel/dist/client/assets/index-Ph73ewzt.js +9 -0
- package/admin-panel/dist/client/index.html +14 -0
- package/admin-panel/package.json +25 -0
- package/admin-panel/server/index.mjs +1424 -0
- package/admin-panel/server/security.mjs +117 -0
- package/admin-panel/server/security.test.mjs +113 -0
- package/admin-panel/server/storage.mjs +49 -0
- package/admin-panel/server/storage.test.mjs +36 -0
- package/dist/builtin-conventions.js +1 -1
- package/dist/builtin-conventions.js.map +1 -1
- package/dist/builtin-instructions.d.ts +1 -1
- package/dist/builtin-instructions.js +1 -1
- package/dist/builtin-instructions.js.map +1 -1
- package/dist/checkpoint-snapshot.d.ts +56 -0
- package/dist/checkpoint-snapshot.js +411 -0
- package/dist/checkpoint-snapshot.js.map +1 -0
- package/dist/config.d.ts +4 -1
- package/dist/config.js +25 -1
- package/dist/config.js.map +1 -1
- package/dist/context-governance.d.ts +56 -0
- package/dist/context-governance.js +176 -0
- package/dist/context-governance.js.map +1 -0
- package/dist/database-runtime.d.ts +6 -1
- package/dist/database-runtime.js +555 -127
- package/dist/database-runtime.js.map +1 -1
- package/dist/development-warnings.d.ts +2 -1
- package/dist/development-warnings.js +92 -22
- package/dist/development-warnings.js.map +1 -1
- package/dist/file-indexing.d.ts +2 -1
- package/dist/file-indexing.js +127 -47
- package/dist/file-indexing.js.map +1 -1
- package/dist/grep.js +4 -0
- package/dist/grep.js.map +1 -1
- package/dist/index.js +8 -2
- package/dist/index.js.map +1 -1
- package/dist/large-file-split.d.ts +53 -8
- package/dist/large-file-split.js +294 -75
- package/dist/large-file-split.js.map +1 -1
- package/dist/memory-maintenance/compaction.d.ts +7 -0
- package/dist/memory-maintenance/compaction.js +133 -0
- package/dist/memory-maintenance/compaction.js.map +1 -0
- package/dist/memory-maintenance/context.d.ts +37 -0
- package/dist/memory-maintenance/context.js +52 -0
- package/dist/memory-maintenance/context.js.map +1 -0
- package/dist/memory-maintenance/index-prune.d.ts +22 -0
- package/dist/memory-maintenance/index-prune.js +243 -0
- package/dist/memory-maintenance/index-prune.js.map +1 -0
- package/dist/memory-maintenance/metrics.d.ts +5 -0
- package/dist/memory-maintenance/metrics.js +30 -0
- package/dist/memory-maintenance/metrics.js.map +1 -0
- package/dist/memory-maintenance/pending.d.ts +4 -0
- package/dist/memory-maintenance/pending.js +74 -0
- package/dist/memory-maintenance/pending.js.map +1 -0
- package/dist/memory-maintenance/purge.d.ts +7 -0
- package/dist/memory-maintenance/purge.js +115 -0
- package/dist/memory-maintenance/purge.js.map +1 -0
- package/dist/memory-maintenance/runner.d.ts +5 -0
- package/dist/memory-maintenance/runner.js +182 -0
- package/dist/memory-maintenance/runner.js.map +1 -0
- package/dist/memory-maintenance/sqlite.d.ts +10 -0
- package/dist/memory-maintenance/sqlite.js +67 -0
- package/dist/memory-maintenance/sqlite.js.map +1 -0
- package/dist/memory-maintenance/types.d.ts +87 -0
- package/dist/memory-maintenance/types.js +2 -0
- package/dist/memory-maintenance/types.js.map +1 -0
- package/dist/memory-maintenance.d.ts +5 -80
- package/dist/memory-maintenance.js +3 -536
- package/dist/memory-maintenance.js.map +1 -1
- package/dist/memory-mutations.d.ts +3 -3
- package/dist/memory-mutations.js +109 -43
- package/dist/memory-mutations.js.map +1 -1
- package/dist/memory-recall.d.ts +3 -1
- package/dist/memory-recall.js +33 -1
- package/dist/memory-recall.js.map +1 -1
- package/dist/operation-scope.js +42 -12
- package/dist/operation-scope.js.map +1 -1
- package/dist/path-containment.d.ts +5 -0
- package/dist/path-containment.js +64 -0
- package/dist/path-containment.js.map +1 -0
- package/dist/path-filters.d.ts +1 -1
- package/dist/path-filters.js +7 -6
- package/dist/path-filters.js.map +1 -1
- package/dist/path-rules.d.ts +1 -0
- package/dist/path-rules.js +23 -1
- package/dist/path-rules.js.map +1 -1
- package/dist/pending-changes.d.ts +5 -0
- package/dist/pending-changes.js +30 -16
- package/dist/pending-changes.js.map +1 -1
- package/dist/project-files.d.ts +1 -0
- package/dist/project-files.js +19 -10
- package/dist/project-files.js.map +1 -1
- package/dist/root.d.ts +4 -1
- package/dist/root.js +34 -4
- package/dist/root.js.map +1 -1
- package/dist/rtk-integrity.d.ts +5 -0
- package/dist/rtk-integrity.js +35 -0
- package/dist/rtk-integrity.js.map +1 -0
- package/dist/rtk-shim.js +22 -9
- package/dist/rtk-shim.js.map +1 -1
- package/dist/rtk-tools.d.ts +8 -0
- package/dist/rtk-tools.js +59 -42
- package/dist/rtk-tools.js.map +1 -1
- package/dist/server-instructions.js +8 -88
- package/dist/server-instructions.js.map +1 -1
- package/dist/tool-catalog.d.ts +2 -0
- package/dist/tool-catalog.js +77 -24
- package/dist/tool-catalog.js.map +1 -1
- package/dist/tool-handlers/context-recovery.js +79 -54
- package/dist/tool-handlers/context-recovery.js.map +1 -1
- package/dist/tool-handlers/large-files.js +615 -41
- package/dist/tool-handlers/large-files.js.map +1 -1
- package/dist/tool-handlers/memory-diagnostics.js +240 -45
- package/dist/tool-handlers/memory-diagnostics.js.map +1 -1
- package/dist/tool-handlers/memory.js +167 -22
- package/dist/tool-handlers/memory.js.map +1 -1
- package/dist/tool-handlers/operations.js +28 -8
- package/dist/tool-handlers/operations.js.map +1 -1
- package/dist/tool-handlers/requirement-status.d.ts +5 -0
- package/dist/tool-handlers/requirement-status.js +267 -0
- package/dist/tool-handlers/requirement-status.js.map +1 -0
- package/dist/tool-handlers/requirements.js +851 -115
- package/dist/tool-handlers/requirements.js.map +1 -1
- package/dist/tool-handlers.d.ts +4 -0
- package/dist/tool-handlers.js +439 -6
- package/dist/tool-handlers.js.map +1 -1
- package/dist/tool-output.d.ts +71 -2
- package/dist/tool-output.js +56 -13
- package/dist/tool-output.js.map +1 -1
- package/dist/tool-schemas.d.ts +243 -0
- package/dist/tool-schemas.js +112 -26
- package/dist/tool-schemas.js.map +1 -1
- package/dist/types.d.ts +10 -1
- package/docs/capability-matrix.md +23 -17
- package/package.json +21 -5
- package/skills/vector-mind-autopilot/SKILL.md +82 -23
- package/skills/vector-mind-autopilot/references/claude-project-instructions.md +14 -11
- package/skills/vector-mind-autopilot/references/universal-system-prompt.md +24 -16
package/dist/database-runtime.js
CHANGED
|
@@ -2,20 +2,26 @@ import fs from "node:fs";
|
|
|
2
2
|
import path from "node:path";
|
|
3
3
|
import Database from "better-sqlite3";
|
|
4
4
|
import { MEMORY_ITEMS_FTS_TABLE } from "./memory-recall.js";
|
|
5
|
+
import { shouldIgnoreDbFilePath } from "./path-rules.js";
|
|
5
6
|
export const FTS_TABLE_NAME = MEMORY_ITEMS_FTS_TABLE;
|
|
6
7
|
let db;
|
|
7
8
|
let dbPath = "";
|
|
8
9
|
let ftsAvailable = false;
|
|
9
10
|
let insertRequirementStmt;
|
|
10
11
|
let getActiveRequirementStmt;
|
|
12
|
+
let getActiveRequirementByIdStmt;
|
|
13
|
+
let getActiveRequirementByGoalKeyStmt;
|
|
11
14
|
let listActiveRequirementsStmt;
|
|
12
15
|
let listRecentRequirementsStmt;
|
|
13
16
|
let completeAllActiveRequirementsStmt;
|
|
14
17
|
let completeRequirementByIdStmt;
|
|
15
|
-
let completeAllActiveRequirementMemoryItemsStmt;
|
|
16
18
|
let completeRequirementMemoryItemByReqIdStmt;
|
|
17
19
|
let listChangeLogsForRequirementStmt;
|
|
18
20
|
let insertChangeLogStmt;
|
|
21
|
+
let upsertSyncedFileStateStmt;
|
|
22
|
+
let getSyncedFileStateStmt;
|
|
23
|
+
let insertMcpGuardEventStmt;
|
|
24
|
+
let insertMcpToolMetricStmt;
|
|
19
25
|
let insertMemoryItemStmt;
|
|
20
26
|
let getMemoryItemByIdStmt;
|
|
21
27
|
let getRequirementMemoryItemIdStmt;
|
|
@@ -50,66 +56,69 @@ let indexFileSymbolsTx = null;
|
|
|
50
56
|
function initMemoryItemsFts() {
|
|
51
57
|
ftsAvailable = false;
|
|
52
58
|
try {
|
|
53
|
-
const
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
59
|
+
const initializeFts = db.transaction(() => {
|
|
60
|
+
const existed = db
|
|
61
|
+
.prepare(`SELECT 1 FROM sqlite_master WHERE type='table' AND name=?`)
|
|
62
|
+
.get(FTS_TABLE_NAME);
|
|
63
|
+
const alreadyExists = !!existed;
|
|
64
|
+
if (!alreadyExists) {
|
|
65
|
+
try {
|
|
66
|
+
db.exec(`
|
|
67
|
+
CREATE VIRTUAL TABLE ${FTS_TABLE_NAME} USING fts5(
|
|
68
|
+
kind,
|
|
69
|
+
title,
|
|
70
|
+
content,
|
|
71
|
+
file_path,
|
|
72
|
+
metadata_json,
|
|
73
|
+
content='memory_items',
|
|
74
|
+
content_rowid='id',
|
|
75
|
+
tokenize='trigram'
|
|
76
|
+
);
|
|
77
|
+
`);
|
|
78
|
+
}
|
|
79
|
+
catch {
|
|
80
|
+
db.exec(`
|
|
81
|
+
CREATE VIRTUAL TABLE ${FTS_TABLE_NAME} USING fts5(
|
|
82
|
+
kind,
|
|
83
|
+
title,
|
|
84
|
+
content,
|
|
85
|
+
file_path,
|
|
86
|
+
metadata_json,
|
|
87
|
+
content='memory_items',
|
|
88
|
+
content_rowid='id'
|
|
89
|
+
);
|
|
90
|
+
`);
|
|
91
|
+
}
|
|
92
|
+
try {
|
|
93
|
+
db.exec(`INSERT INTO ${FTS_TABLE_NAME}(${FTS_TABLE_NAME}) VALUES('rebuild');`);
|
|
94
|
+
}
|
|
95
|
+
catch (err) {
|
|
96
|
+
console.error("[vectormind] fts rebuild failed:", err);
|
|
97
|
+
}
|
|
71
98
|
}
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
);
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
INSERT INTO ${FTS_TABLE_NAME}(rowid, kind, title, content, file_path, metadata_json)
|
|
96
|
-
VALUES (new.id, new.kind, new.title, new.content, new.file_path, new.metadata_json);
|
|
97
|
-
END;
|
|
98
|
-
|
|
99
|
-
CREATE TRIGGER IF NOT EXISTS vectormind_memory_items_fts_ad
|
|
100
|
-
AFTER DELETE ON memory_items BEGIN
|
|
101
|
-
INSERT INTO ${FTS_TABLE_NAME}(${FTS_TABLE_NAME}, rowid, kind, title, content, file_path, metadata_json)
|
|
102
|
-
VALUES ('delete', old.id, old.kind, old.title, old.content, old.file_path, old.metadata_json);
|
|
103
|
-
END;
|
|
104
|
-
|
|
105
|
-
CREATE TRIGGER IF NOT EXISTS vectormind_memory_items_fts_au
|
|
106
|
-
AFTER UPDATE ON memory_items BEGIN
|
|
107
|
-
INSERT INTO ${FTS_TABLE_NAME}(${FTS_TABLE_NAME}, rowid, kind, title, content, file_path, metadata_json)
|
|
108
|
-
VALUES ('delete', old.id, old.kind, old.title, old.content, old.file_path, old.metadata_json);
|
|
109
|
-
INSERT INTO ${FTS_TABLE_NAME}(rowid, kind, title, content, file_path, metadata_json)
|
|
110
|
-
VALUES (new.id, new.kind, new.title, new.content, new.file_path, new.metadata_json);
|
|
111
|
-
END;
|
|
112
|
-
`);
|
|
99
|
+
db.exec(`
|
|
100
|
+
CREATE TRIGGER IF NOT EXISTS vectormind_memory_items_fts_ai
|
|
101
|
+
AFTER INSERT ON memory_items BEGIN
|
|
102
|
+
INSERT INTO ${FTS_TABLE_NAME}(rowid, kind, title, content, file_path, metadata_json)
|
|
103
|
+
VALUES (new.id, new.kind, new.title, new.content, new.file_path, new.metadata_json);
|
|
104
|
+
END;
|
|
105
|
+
|
|
106
|
+
CREATE TRIGGER IF NOT EXISTS vectormind_memory_items_fts_ad
|
|
107
|
+
AFTER DELETE ON memory_items BEGIN
|
|
108
|
+
INSERT INTO ${FTS_TABLE_NAME}(${FTS_TABLE_NAME}, rowid, kind, title, content, file_path, metadata_json)
|
|
109
|
+
VALUES ('delete', old.id, old.kind, old.title, old.content, old.file_path, old.metadata_json);
|
|
110
|
+
END;
|
|
111
|
+
|
|
112
|
+
CREATE TRIGGER IF NOT EXISTS vectormind_memory_items_fts_au
|
|
113
|
+
AFTER UPDATE ON memory_items BEGIN
|
|
114
|
+
INSERT INTO ${FTS_TABLE_NAME}(${FTS_TABLE_NAME}, rowid, kind, title, content, file_path, metadata_json)
|
|
115
|
+
VALUES ('delete', old.id, old.kind, old.title, old.content, old.file_path, old.metadata_json);
|
|
116
|
+
INSERT INTO ${FTS_TABLE_NAME}(rowid, kind, title, content, file_path, metadata_json)
|
|
117
|
+
VALUES (new.id, new.kind, new.title, new.content, new.file_path, new.metadata_json);
|
|
118
|
+
END;
|
|
119
|
+
`);
|
|
120
|
+
});
|
|
121
|
+
initializeFts.immediate();
|
|
113
122
|
db.prepare(`SELECT rowid FROM ${FTS_TABLE_NAME} LIMIT 1`).get();
|
|
114
123
|
ftsAvailable = true;
|
|
115
124
|
}
|
|
@@ -126,6 +135,295 @@ function columnExists(table, column) {
|
|
|
126
135
|
return false;
|
|
127
136
|
}
|
|
128
137
|
}
|
|
138
|
+
function safeParseObject(value) {
|
|
139
|
+
if (!value)
|
|
140
|
+
return {};
|
|
141
|
+
try {
|
|
142
|
+
const parsed = JSON.parse(value);
|
|
143
|
+
return parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : {};
|
|
144
|
+
}
|
|
145
|
+
catch {
|
|
146
|
+
return {};
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
function stringValue(value) {
|
|
150
|
+
return typeof value === "string" && value.length > 0 ? value : undefined;
|
|
151
|
+
}
|
|
152
|
+
function nullableStringValue(value) {
|
|
153
|
+
if (value === null)
|
|
154
|
+
return null;
|
|
155
|
+
return stringValue(value);
|
|
156
|
+
}
|
|
157
|
+
function mergeStringArrays(values) {
|
|
158
|
+
const merged = [];
|
|
159
|
+
for (const value of values) {
|
|
160
|
+
if (!Array.isArray(value))
|
|
161
|
+
continue;
|
|
162
|
+
for (const item of value) {
|
|
163
|
+
if (typeof item === "string" && item.length > 0 && !merged.includes(item))
|
|
164
|
+
merged.push(item);
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
return merged;
|
|
168
|
+
}
|
|
169
|
+
function migrationFileKey(filePath) {
|
|
170
|
+
const normalized = filePath.replace(/\\/g, "/");
|
|
171
|
+
return process.platform === "win32" ? normalized.toLowerCase() : normalized;
|
|
172
|
+
}
|
|
173
|
+
function addMigrationFileEntry(filesByPath, filePath, metadata = {}) {
|
|
174
|
+
const normalizedFilePath = stringValue(filePath)?.replace(/\\/g, "/");
|
|
175
|
+
if (!normalizedFilePath || normalizedFilePath === "(multiple)" || normalizedFilePath === "(unspecified)")
|
|
176
|
+
return;
|
|
177
|
+
if (shouldIgnoreDbFilePath(normalizedFilePath))
|
|
178
|
+
return;
|
|
179
|
+
const key = migrationFileKey(normalizedFilePath);
|
|
180
|
+
const existing = filesByPath.get(key) ?? { file_path: normalizedFilePath };
|
|
181
|
+
const event = stringValue(metadata.event);
|
|
182
|
+
const source = stringValue(metadata.source);
|
|
183
|
+
const fileStateHash = nullableStringValue(metadata.file_state_hash);
|
|
184
|
+
if (event && !existing.event)
|
|
185
|
+
existing.event = event;
|
|
186
|
+
if (source && !existing.source)
|
|
187
|
+
existing.source = source;
|
|
188
|
+
if (fileStateHash !== undefined && existing.file_state_hash === undefined)
|
|
189
|
+
existing.file_state_hash = fileStateHash;
|
|
190
|
+
filesByPath.set(key, existing);
|
|
191
|
+
}
|
|
192
|
+
function addMigrationFilesFromMetadata(filesByPath, metadata) {
|
|
193
|
+
const files = metadata.files;
|
|
194
|
+
if (!Array.isArray(files))
|
|
195
|
+
return;
|
|
196
|
+
for (const item of files) {
|
|
197
|
+
if (!item || typeof item !== "object" || Array.isArray(item))
|
|
198
|
+
continue;
|
|
199
|
+
const entry = item;
|
|
200
|
+
addMigrationFileEntry(filesByPath, entry.file_path, entry);
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
function migrationFilesJson(filesByPath) {
|
|
204
|
+
return JSON.stringify([...filesByPath.values()].map((file) => ({
|
|
205
|
+
file_path: file.file_path,
|
|
206
|
+
event: file.event,
|
|
207
|
+
source: file.source,
|
|
208
|
+
file_state_hash: file.file_state_hash ?? null,
|
|
209
|
+
})));
|
|
210
|
+
}
|
|
211
|
+
function repairDuplicateChangeLogs() {
|
|
212
|
+
const groups = db
|
|
213
|
+
.prepare(`SELECT req_id, timestamp, intent_summary, MIN(id) AS keep_id, COUNT(*) AS row_count
|
|
214
|
+
FROM change_logs
|
|
215
|
+
GROUP BY req_id, timestamp, intent_summary
|
|
216
|
+
HAVING COUNT(*) > 1`)
|
|
217
|
+
.all();
|
|
218
|
+
if (!groups.length)
|
|
219
|
+
return 0;
|
|
220
|
+
const selectGroupRows = db.prepare(`SELECT id, req_id, file_path, intent_summary, files_json, file_count, timestamp
|
|
221
|
+
FROM change_logs
|
|
222
|
+
WHERE req_id IS ? AND timestamp IS ? AND intent_summary IS ?
|
|
223
|
+
ORDER BY id ASC`);
|
|
224
|
+
const updateKeepRow = db.prepare(`UPDATE change_logs
|
|
225
|
+
SET file_path = ?, files_json = ?, file_count = ?
|
|
226
|
+
WHERE id = ?`);
|
|
227
|
+
const deleteRow = db.prepare(`DELETE FROM change_logs WHERE id = ?`);
|
|
228
|
+
let removedRows = 0;
|
|
229
|
+
const tx = db.transaction(() => {
|
|
230
|
+
for (const group of groups) {
|
|
231
|
+
const rows = selectGroupRows.all(group.req_id, group.timestamp, group.intent_summary);
|
|
232
|
+
if (rows.length <= 1)
|
|
233
|
+
continue;
|
|
234
|
+
const distinctLegacyFiles = new Set(rows.map((row) => row.file_path).filter((value) => !!value));
|
|
235
|
+
const hasAggregateRows = rows.some((row) => {
|
|
236
|
+
try {
|
|
237
|
+
const parsed = row.files_json ? JSON.parse(row.files_json) : null;
|
|
238
|
+
return Array.isArray(parsed) && parsed.length > 0;
|
|
239
|
+
}
|
|
240
|
+
catch {
|
|
241
|
+
return false;
|
|
242
|
+
}
|
|
243
|
+
});
|
|
244
|
+
if (hasAggregateRows || distinctLegacyFiles.size < 2)
|
|
245
|
+
continue;
|
|
246
|
+
const filesByPath = new Map();
|
|
247
|
+
for (const row of rows) {
|
|
248
|
+
addMigrationFilesFromMetadata(filesByPath, safeParseObject(row.files_json));
|
|
249
|
+
addMigrationFileEntry(filesByPath, row.file_path);
|
|
250
|
+
}
|
|
251
|
+
const files = [...filesByPath.values()];
|
|
252
|
+
const primaryFilePath = files.length === 1 ? files[0].file_path : files.length > 1 ? "(multiple)" : null;
|
|
253
|
+
const keepId = Number(group.keep_id);
|
|
254
|
+
updateKeepRow.run(primaryFilePath, migrationFilesJson(filesByPath), files.length, keepId);
|
|
255
|
+
for (const row of rows) {
|
|
256
|
+
if (row.id === keepId)
|
|
257
|
+
continue;
|
|
258
|
+
deleteRow.run(row.id);
|
|
259
|
+
removedRows += 1;
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
});
|
|
263
|
+
tx();
|
|
264
|
+
return removedRows;
|
|
265
|
+
}
|
|
266
|
+
function repairDuplicateChangeIntentMemoryItems() {
|
|
267
|
+
const groups = db
|
|
268
|
+
.prepare(`SELECT req_id, content, MIN(id) AS keep_id, COUNT(*) AS row_count
|
|
269
|
+
FROM memory_items
|
|
270
|
+
WHERE kind = 'change_intent'
|
|
271
|
+
GROUP BY req_id, content
|
|
272
|
+
HAVING COUNT(*) > 1`)
|
|
273
|
+
.all();
|
|
274
|
+
if (!groups.length)
|
|
275
|
+
return 0;
|
|
276
|
+
const selectGroupRows = db.prepare(`SELECT id, title, content, file_path, req_id, metadata_json, updated_at
|
|
277
|
+
FROM memory_items
|
|
278
|
+
WHERE kind = 'change_intent'
|
|
279
|
+
AND req_id IS ?
|
|
280
|
+
AND content IS ?
|
|
281
|
+
ORDER BY id ASC`);
|
|
282
|
+
const updateKeepRow = db.prepare(`UPDATE memory_items
|
|
283
|
+
SET file_path = ?, metadata_json = ?
|
|
284
|
+
WHERE id = ?`);
|
|
285
|
+
const deleteRow = db.prepare(`DELETE FROM memory_items WHERE id = ?`);
|
|
286
|
+
let removedRows = 0;
|
|
287
|
+
const tx = db.transaction(() => {
|
|
288
|
+
for (const group of groups) {
|
|
289
|
+
const rows = selectGroupRows.all(group.req_id, group.content);
|
|
290
|
+
if (rows.length <= 1)
|
|
291
|
+
continue;
|
|
292
|
+
const filesByPath = new Map();
|
|
293
|
+
const metadataList = rows.map((row) => safeParseObject(row.metadata_json));
|
|
294
|
+
const distinctLegacyFiles = new Set(rows.map((row) => row.file_path).filter((value) => !!value));
|
|
295
|
+
if (metadataList.some((metadata) => Array.isArray(metadata.files)) || distinctLegacyFiles.size < 2)
|
|
296
|
+
continue;
|
|
297
|
+
for (let i = 0; i < rows.length; i += 1) {
|
|
298
|
+
const row = rows[i];
|
|
299
|
+
const metadata = metadataList[i] ?? {};
|
|
300
|
+
addMigrationFilesFromMetadata(filesByPath, metadata);
|
|
301
|
+
addMigrationFileEntry(filesByPath, row.file_path, metadata);
|
|
302
|
+
}
|
|
303
|
+
const files = [...filesByPath.values()];
|
|
304
|
+
const keepId = Number(group.keep_id);
|
|
305
|
+
const keepRow = rows.find((row) => row.id === keepId) ?? rows[0];
|
|
306
|
+
const keepMetadata = safeParseObject(keepRow.metadata_json);
|
|
307
|
+
const sourceChangeIds = new Set();
|
|
308
|
+
for (const metadata of metadataList) {
|
|
309
|
+
if (typeof metadata.change_log_id === "number")
|
|
310
|
+
sourceChangeIds.add(metadata.change_log_id);
|
|
311
|
+
if (Array.isArray(metadata.source_change_ids)) {
|
|
312
|
+
for (const id of metadata.source_change_ids) {
|
|
313
|
+
if (typeof id === "number")
|
|
314
|
+
sourceChangeIds.add(id);
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
const nextMetadata = {
|
|
319
|
+
...keepMetadata,
|
|
320
|
+
files: files.map((file) => ({
|
|
321
|
+
file_path: file.file_path,
|
|
322
|
+
event: file.event,
|
|
323
|
+
source: file.source,
|
|
324
|
+
file_state_hash: file.file_state_hash ?? null,
|
|
325
|
+
})),
|
|
326
|
+
file_count: files.length,
|
|
327
|
+
};
|
|
328
|
+
const verification = mergeStringArrays(metadataList.map((metadata) => metadata.verification));
|
|
329
|
+
const verificationGaps = mergeStringArrays(metadataList.map((metadata) => metadata.verification_gaps));
|
|
330
|
+
if (verification.length)
|
|
331
|
+
nextMetadata.verification = verification;
|
|
332
|
+
if (verificationGaps.length)
|
|
333
|
+
nextMetadata.verification_gaps = verificationGaps;
|
|
334
|
+
if (sourceChangeIds.size)
|
|
335
|
+
nextMetadata.source_change_ids = [...sourceChangeIds];
|
|
336
|
+
if (files.length === 1) {
|
|
337
|
+
const onlyFile = files[0];
|
|
338
|
+
nextMetadata.event = onlyFile.event;
|
|
339
|
+
nextMetadata.source = onlyFile.source;
|
|
340
|
+
nextMetadata.file_state_hash = onlyFile.file_state_hash ?? null;
|
|
341
|
+
}
|
|
342
|
+
else {
|
|
343
|
+
delete nextMetadata.event;
|
|
344
|
+
delete nextMetadata.source;
|
|
345
|
+
delete nextMetadata.file_state_hash;
|
|
346
|
+
}
|
|
347
|
+
updateKeepRow.run(files.length === 1 ? files[0].file_path : null, JSON.stringify(nextMetadata), keepId);
|
|
348
|
+
for (const row of rows) {
|
|
349
|
+
if (row.id === keepId)
|
|
350
|
+
continue;
|
|
351
|
+
deleteRow.run(row.id);
|
|
352
|
+
removedRows += 1;
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
});
|
|
356
|
+
tx();
|
|
357
|
+
return removedRows;
|
|
358
|
+
}
|
|
359
|
+
function backfillSyncedFileStatesFromChangeIntentMemory() {
|
|
360
|
+
const rows = db
|
|
361
|
+
.prepare(`SELECT id, file_path, metadata_json, updated_at
|
|
362
|
+
FROM memory_items
|
|
363
|
+
WHERE kind = 'change_intent'
|
|
364
|
+
ORDER BY updated_at ASC, id ASC`)
|
|
365
|
+
.all();
|
|
366
|
+
if (!rows.length)
|
|
367
|
+
return 0;
|
|
368
|
+
const upsert = db.prepare(`INSERT INTO synced_file_states (file_path, file_state_hash, source_change_id, updated_at)
|
|
369
|
+
VALUES (?, ?, ?, COALESCE(?, CURRENT_TIMESTAMP))
|
|
370
|
+
ON CONFLICT(file_path) DO UPDATE SET
|
|
371
|
+
file_state_hash = excluded.file_state_hash,
|
|
372
|
+
source_change_id = excluded.source_change_id,
|
|
373
|
+
updated_at = CASE
|
|
374
|
+
WHEN synced_file_states.updated_at IS NULL OR synced_file_states.updated_at <= excluded.updated_at
|
|
375
|
+
THEN excluded.updated_at
|
|
376
|
+
ELSE synced_file_states.updated_at
|
|
377
|
+
END`);
|
|
378
|
+
let written = 0;
|
|
379
|
+
const tx = db.transaction(() => {
|
|
380
|
+
for (const row of rows) {
|
|
381
|
+
const metadata = safeParseObject(row.metadata_json);
|
|
382
|
+
const sourceChangeId = typeof metadata.change_log_id === "number"
|
|
383
|
+
? metadata.change_log_id
|
|
384
|
+
: Array.isArray(metadata.source_change_ids) && typeof metadata.source_change_ids[0] === "number"
|
|
385
|
+
? metadata.source_change_ids[0]
|
|
386
|
+
: null;
|
|
387
|
+
const filesByPath = new Map();
|
|
388
|
+
addMigrationFilesFromMetadata(filesByPath, metadata);
|
|
389
|
+
addMigrationFileEntry(filesByPath, row.file_path, metadata);
|
|
390
|
+
for (const file of filesByPath.values()) {
|
|
391
|
+
if (typeof file.file_state_hash !== "string")
|
|
392
|
+
continue;
|
|
393
|
+
upsert.run(file.file_path, file.file_state_hash, sourceChangeId, row.updated_at);
|
|
394
|
+
written += 1;
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
});
|
|
398
|
+
tx();
|
|
399
|
+
return written;
|
|
400
|
+
}
|
|
401
|
+
function repairHistoricalDuplicateChangeRecords() {
|
|
402
|
+
const migrationKey = "migration:aggregate_change_records:v1";
|
|
403
|
+
try {
|
|
404
|
+
if (db.prepare(`SELECT 1 FROM meta_kv WHERE key = ? LIMIT 1`).get(migrationKey))
|
|
405
|
+
return;
|
|
406
|
+
const removedChangeLogs = repairDuplicateChangeLogs();
|
|
407
|
+
const removedChangeIntentMemory = repairDuplicateChangeIntentMemoryItems();
|
|
408
|
+
const syncedFileStates = backfillSyncedFileStatesFromChangeIntentMemory();
|
|
409
|
+
const result = JSON.stringify({
|
|
410
|
+
removed_change_logs: removedChangeLogs,
|
|
411
|
+
removed_change_intent_memory: removedChangeIntentMemory,
|
|
412
|
+
synced_file_states: syncedFileStates,
|
|
413
|
+
ran_at: new Date().toISOString(),
|
|
414
|
+
});
|
|
415
|
+
db.transaction(() => {
|
|
416
|
+
db.prepare(`INSERT INTO meta_kv (key, value, updated_at)
|
|
417
|
+
VALUES (?, ?, CURRENT_TIMESTAMP)`).run(migrationKey, result);
|
|
418
|
+
db.prepare(`INSERT INTO meta_kv (key, value, updated_at)
|
|
419
|
+
VALUES ('maintenance:dedupe_change_records:last_run', ?, CURRENT_TIMESTAMP)
|
|
420
|
+
ON CONFLICT(key) DO UPDATE SET value = excluded.value, updated_at = CURRENT_TIMESTAMP`).run(result);
|
|
421
|
+
})();
|
|
422
|
+
}
|
|
423
|
+
catch (err) {
|
|
424
|
+
console.error("[vectormind] historical duplicate change-record repair failed:", err);
|
|
425
|
+
}
|
|
426
|
+
}
|
|
129
427
|
export function openDatabaseRuntime(projectRoot) {
|
|
130
428
|
const vmDir = path.join(projectRoot, ".vectormind");
|
|
131
429
|
try {
|
|
@@ -139,23 +437,26 @@ export function openDatabaseRuntime(projectRoot) {
|
|
|
139
437
|
db.pragma("journal_mode = WAL");
|
|
140
438
|
db.pragma("foreign_keys = ON");
|
|
141
439
|
db.exec(`
|
|
142
|
-
CREATE TABLE IF NOT EXISTS requirements (
|
|
143
|
-
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
144
|
-
title TEXT NOT NULL,
|
|
145
|
-
status TEXT DEFAULT 'active',
|
|
146
|
-
context_data TEXT,
|
|
147
|
-
|
|
148
|
-
|
|
440
|
+
CREATE TABLE IF NOT EXISTS requirements (
|
|
441
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
442
|
+
title TEXT NOT NULL,
|
|
443
|
+
status TEXT DEFAULT 'active',
|
|
444
|
+
context_data TEXT,
|
|
445
|
+
goal_key TEXT,
|
|
446
|
+
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
|
447
|
+
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP
|
|
149
448
|
);
|
|
150
449
|
|
|
151
|
-
CREATE TABLE IF NOT EXISTS change_logs (
|
|
152
|
-
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
153
|
-
req_id INTEGER,
|
|
154
|
-
file_path TEXT,
|
|
155
|
-
intent_summary TEXT,
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
450
|
+
CREATE TABLE IF NOT EXISTS change_logs (
|
|
451
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
452
|
+
req_id INTEGER,
|
|
453
|
+
file_path TEXT,
|
|
454
|
+
intent_summary TEXT,
|
|
455
|
+
files_json TEXT,
|
|
456
|
+
file_count INTEGER DEFAULT 1,
|
|
457
|
+
timestamp DATETIME DEFAULT CURRENT_TIMESTAMP,
|
|
458
|
+
FOREIGN KEY(req_id) REFERENCES requirements(id)
|
|
459
|
+
);
|
|
159
460
|
|
|
160
461
|
CREATE TABLE IF NOT EXISTS symbols (
|
|
161
462
|
name TEXT,
|
|
@@ -165,11 +466,55 @@ export function openDatabaseRuntime(projectRoot) {
|
|
|
165
466
|
PRIMARY KEY(name, file_path)
|
|
166
467
|
);
|
|
167
468
|
|
|
168
|
-
CREATE INDEX IF NOT EXISTS idx_change_logs_req_id_timestamp
|
|
169
|
-
ON change_logs(req_id, timestamp DESC);
|
|
170
|
-
|
|
171
|
-
CREATE
|
|
172
|
-
|
|
469
|
+
CREATE INDEX IF NOT EXISTS idx_change_logs_req_id_timestamp
|
|
470
|
+
ON change_logs(req_id, timestamp DESC);
|
|
471
|
+
|
|
472
|
+
CREATE TABLE IF NOT EXISTS synced_file_states (
|
|
473
|
+
file_path TEXT PRIMARY KEY,
|
|
474
|
+
file_state_hash TEXT,
|
|
475
|
+
source_change_id INTEGER,
|
|
476
|
+
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP
|
|
477
|
+
);
|
|
478
|
+
|
|
479
|
+
CREATE INDEX IF NOT EXISTS idx_synced_file_states_updated_at
|
|
480
|
+
ON synced_file_states(updated_at DESC);
|
|
481
|
+
|
|
482
|
+
CREATE TABLE IF NOT EXISTS mcp_guard_events (
|
|
483
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
484
|
+
tool_name TEXT NOT NULL,
|
|
485
|
+
event_type TEXT NOT NULL,
|
|
486
|
+
severity TEXT DEFAULT 'info',
|
|
487
|
+
title TEXT NOT NULL,
|
|
488
|
+
detail TEXT,
|
|
489
|
+
metadata_json TEXT,
|
|
490
|
+
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
|
|
491
|
+
);
|
|
492
|
+
|
|
493
|
+
CREATE INDEX IF NOT EXISTS idx_mcp_guard_events_created_at
|
|
494
|
+
ON mcp_guard_events(created_at DESC, id DESC);
|
|
495
|
+
|
|
496
|
+
CREATE INDEX IF NOT EXISTS idx_mcp_guard_events_type_created_at
|
|
497
|
+
ON mcp_guard_events(event_type, created_at DESC);
|
|
498
|
+
|
|
499
|
+
CREATE TABLE IF NOT EXISTS mcp_tool_metrics (
|
|
500
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
501
|
+
tool_name TEXT NOT NULL,
|
|
502
|
+
duration_ms REAL NOT NULL,
|
|
503
|
+
raw_output_chars INTEGER NOT NULL,
|
|
504
|
+
output_chars INTEGER NOT NULL,
|
|
505
|
+
is_error INTEGER NOT NULL DEFAULT 0,
|
|
506
|
+
root_source TEXT,
|
|
507
|
+
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
|
|
508
|
+
);
|
|
509
|
+
|
|
510
|
+
CREATE INDEX IF NOT EXISTS idx_mcp_tool_metrics_created_at
|
|
511
|
+
ON mcp_tool_metrics(created_at DESC, id DESC);
|
|
512
|
+
|
|
513
|
+
CREATE INDEX IF NOT EXISTS idx_mcp_tool_metrics_tool_created_at
|
|
514
|
+
ON mcp_tool_metrics(tool_name, created_at DESC);
|
|
515
|
+
|
|
516
|
+
CREATE INDEX IF NOT EXISTS idx_symbols_name
|
|
517
|
+
ON symbols(name);
|
|
173
518
|
|
|
174
519
|
CREATE TABLE IF NOT EXISTS memory_items (
|
|
175
520
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
@@ -259,58 +604,118 @@ export function openDatabaseRuntime(projectRoot) {
|
|
|
259
604
|
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP
|
|
260
605
|
);
|
|
261
606
|
`);
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
607
|
+
const migrateLegacyColumns = db.transaction(() => {
|
|
608
|
+
if (!columnExists("requirements", "updated_at")) {
|
|
609
|
+
db.exec(`ALTER TABLE requirements ADD COLUMN updated_at DATETIME`);
|
|
610
|
+
db.exec(`UPDATE requirements SET updated_at = COALESCE(created_at, CURRENT_TIMESTAMP) WHERE updated_at IS NULL`);
|
|
611
|
+
}
|
|
612
|
+
if (!columnExists("requirements", "goal_key")) {
|
|
613
|
+
db.exec(`ALTER TABLE requirements ADD COLUMN goal_key TEXT`);
|
|
614
|
+
}
|
|
615
|
+
if (!columnExists("change_logs", "files_json")) {
|
|
616
|
+
db.exec(`ALTER TABLE change_logs ADD COLUMN files_json TEXT`);
|
|
617
|
+
}
|
|
618
|
+
if (!columnExists("change_logs", "file_count")) {
|
|
619
|
+
db.exec(`ALTER TABLE change_logs ADD COLUMN file_count INTEGER DEFAULT 1`);
|
|
620
|
+
db.exec(`UPDATE change_logs SET file_count = 1 WHERE file_count IS NULL`);
|
|
621
|
+
}
|
|
622
|
+
});
|
|
623
|
+
migrateLegacyColumns.immediate();
|
|
624
|
+
db.transaction(() => {
|
|
625
|
+
db.exec(`
|
|
626
|
+
UPDATE requirements SET updated_at = COALESCE(updated_at, created_at, CURRENT_TIMESTAMP) WHERE updated_at IS NULL;
|
|
627
|
+
UPDATE requirements
|
|
628
|
+
SET updated_at = (
|
|
629
|
+
SELECT MAX(mi.updated_at)
|
|
630
|
+
FROM memory_items mi
|
|
631
|
+
WHERE mi.kind = 'requirement' AND mi.req_id = requirements.id
|
|
632
|
+
)
|
|
633
|
+
WHERE (
|
|
634
|
+
SELECT MAX(mi.updated_at)
|
|
635
|
+
FROM memory_items mi
|
|
636
|
+
WHERE mi.kind = 'requirement' AND mi.req_id = requirements.id
|
|
637
|
+
) IS NOT NULL
|
|
638
|
+
AND datetime((
|
|
639
|
+
SELECT MAX(mi.updated_at)
|
|
640
|
+
FROM memory_items mi
|
|
641
|
+
WHERE mi.kind = 'requirement' AND mi.req_id = requirements.id
|
|
642
|
+
)) > datetime(COALESCE(requirements.updated_at, requirements.created_at, '1970-01-01 00:00:00'));
|
|
643
|
+
UPDATE requirements
|
|
644
|
+
SET status = 'superseded', updated_at = CURRENT_TIMESTAMP
|
|
645
|
+
WHERE status = 'active'
|
|
646
|
+
AND goal_key IS NOT NULL
|
|
647
|
+
AND EXISTS (
|
|
648
|
+
SELECT 1 FROM requirements newer
|
|
649
|
+
WHERE newer.status = 'active'
|
|
650
|
+
AND newer.goal_key = requirements.goal_key
|
|
651
|
+
AND newer.id > requirements.id
|
|
652
|
+
);
|
|
653
|
+
UPDATE memory_items
|
|
654
|
+
SET metadata_json = json_set(
|
|
655
|
+
CASE
|
|
656
|
+
WHEN json_valid(COALESCE(metadata_json, '{}')) THEN COALESCE(metadata_json, '{}')
|
|
657
|
+
ELSE '{}'
|
|
658
|
+
END,
|
|
659
|
+
'$.status',
|
|
660
|
+
'superseded',
|
|
661
|
+
'$.superseded',
|
|
662
|
+
json('true')
|
|
663
|
+
),
|
|
664
|
+
updated_at = CURRENT_TIMESTAMP
|
|
665
|
+
WHERE kind = 'requirement'
|
|
666
|
+
AND req_id IN (
|
|
667
|
+
SELECT id FROM requirements WHERE status = 'superseded'
|
|
668
|
+
)
|
|
669
|
+
AND COALESCE(json_extract(
|
|
670
|
+
CASE
|
|
671
|
+
WHEN json_valid(COALESCE(metadata_json, '{}')) THEN COALESCE(metadata_json, '{}')
|
|
672
|
+
ELSE '{}'
|
|
673
|
+
END,
|
|
674
|
+
'$.status'
|
|
675
|
+
), 'active') <> 'superseded';
|
|
676
|
+
CREATE INDEX IF NOT EXISTS idx_requirements_status_updated_at
|
|
677
|
+
ON requirements(status, updated_at DESC, id DESC);
|
|
678
|
+
CREATE INDEX IF NOT EXISTS idx_requirements_goal_key_status
|
|
679
|
+
ON requirements(goal_key, status, updated_at DESC, id DESC);
|
|
680
|
+
CREATE UNIQUE INDEX IF NOT EXISTS idx_requirements_active_goal_key_unique
|
|
681
|
+
ON requirements(goal_key) WHERE status = 'active' AND goal_key IS NOT NULL;
|
|
682
|
+
CREATE TRIGGER IF NOT EXISTS vectormind_requirements_touch_updated_at
|
|
683
|
+
AFTER UPDATE ON requirements
|
|
684
|
+
FOR EACH ROW
|
|
685
|
+
WHEN NEW.updated_at = OLD.updated_at
|
|
686
|
+
BEGIN
|
|
687
|
+
UPDATE requirements SET updated_at = CURRENT_TIMESTAMP WHERE id = NEW.id;
|
|
688
|
+
END;
|
|
689
|
+
`);
|
|
690
|
+
})();
|
|
278
691
|
initMemoryItemsFts();
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
692
|
+
repairHistoricalDuplicateChangeRecords();
|
|
693
|
+
insertRequirementStmt = db.prepare(`INSERT INTO requirements (title, context_data, goal_key, status) VALUES (?, ?, ?, 'active')`);
|
|
694
|
+
completeAllActiveRequirementsStmt = db.prepare(`UPDATE requirements SET status = 'completed', updated_at = CURRENT_TIMESTAMP WHERE status = 'active'`);
|
|
695
|
+
completeRequirementByIdStmt = db.prepare(`UPDATE requirements SET status = 'completed', updated_at = CURRENT_TIMESTAMP WHERE id = ?`);
|
|
696
|
+
getActiveRequirementStmt = db.prepare(`SELECT id, title, status, context_data, goal_key, created_at
|
|
697
|
+
FROM requirements
|
|
284
698
|
WHERE status = 'active'
|
|
285
699
|
ORDER BY updated_at DESC, created_at DESC, id DESC
|
|
286
700
|
LIMIT 1`);
|
|
287
|
-
|
|
701
|
+
getActiveRequirementByIdStmt = db.prepare(`SELECT id, title, status, context_data, goal_key, created_at
|
|
702
|
+
FROM requirements
|
|
703
|
+
WHERE status = 'active' AND id = ?
|
|
704
|
+
LIMIT 1`);
|
|
705
|
+
getActiveRequirementByGoalKeyStmt = db.prepare(`SELECT id, title, status, context_data, goal_key, created_at
|
|
706
|
+
FROM requirements
|
|
707
|
+
WHERE status = 'active' AND goal_key = ?
|
|
708
|
+
ORDER BY updated_at DESC, created_at DESC, id DESC
|
|
709
|
+
LIMIT 1`);
|
|
710
|
+
listActiveRequirementsStmt = db.prepare(`SELECT id, title, status, context_data, goal_key, created_at
|
|
288
711
|
FROM requirements
|
|
289
712
|
WHERE status = 'active'
|
|
290
713
|
ORDER BY updated_at DESC, created_at DESC, id DESC
|
|
291
714
|
LIMIT ?`);
|
|
292
|
-
listRecentRequirementsStmt = db.prepare(`SELECT id, title, status, context_data, created_at
|
|
715
|
+
listRecentRequirementsStmt = db.prepare(`SELECT id, title, status, context_data, goal_key, created_at
|
|
293
716
|
FROM requirements
|
|
294
717
|
ORDER BY updated_at DESC, created_at DESC, id DESC
|
|
295
718
|
LIMIT ?`);
|
|
296
|
-
completeAllActiveRequirementMemoryItemsStmt = db.prepare(`UPDATE memory_items
|
|
297
|
-
SET metadata_json = json_set(
|
|
298
|
-
CASE
|
|
299
|
-
WHEN json_valid(COALESCE(metadata_json, '{}')) THEN COALESCE(metadata_json, '{}')
|
|
300
|
-
ELSE '{}'
|
|
301
|
-
END,
|
|
302
|
-
'$.status',
|
|
303
|
-
?
|
|
304
|
-
),
|
|
305
|
-
updated_at = CURRENT_TIMESTAMP
|
|
306
|
-
WHERE kind = 'requirement'
|
|
307
|
-
AND json_extract(
|
|
308
|
-
CASE
|
|
309
|
-
WHEN json_valid(COALESCE(metadata_json, '{}')) THEN COALESCE(metadata_json, '{}')
|
|
310
|
-
ELSE '{}'
|
|
311
|
-
END,
|
|
312
|
-
'$.status'
|
|
313
|
-
) = ?`);
|
|
314
719
|
completeRequirementMemoryItemByReqIdStmt = db.prepare(`UPDATE memory_items
|
|
315
720
|
SET metadata_json = json_set(
|
|
316
721
|
CASE
|
|
@@ -323,12 +728,30 @@ export function openDatabaseRuntime(projectRoot) {
|
|
|
323
728
|
updated_at = CURRENT_TIMESTAMP
|
|
324
729
|
WHERE kind = 'requirement'
|
|
325
730
|
AND req_id = ?`);
|
|
326
|
-
listChangeLogsForRequirementStmt = db.prepare(`SELECT id, req_id, file_path, intent_summary, timestamp
|
|
327
|
-
FROM change_logs
|
|
328
|
-
WHERE req_id = ?
|
|
329
|
-
ORDER BY timestamp DESC, id DESC
|
|
731
|
+
listChangeLogsForRequirementStmt = db.prepare(`SELECT id, req_id, file_path, intent_summary, files_json, file_count, timestamp
|
|
732
|
+
FROM change_logs
|
|
733
|
+
WHERE req_id = ?
|
|
734
|
+
ORDER BY timestamp DESC, id DESC
|
|
330
735
|
LIMIT ?`);
|
|
331
|
-
insertChangeLogStmt = db.prepare(`INSERT INTO change_logs (req_id, file_path, intent_summary
|
|
736
|
+
insertChangeLogStmt = db.prepare(`INSERT INTO change_logs (req_id, file_path, intent_summary, files_json, file_count)
|
|
737
|
+
VALUES (?, ?, ?, ?, ?)`);
|
|
738
|
+
upsertSyncedFileStateStmt = db.prepare(`INSERT INTO synced_file_states (file_path, file_state_hash, source_change_id, updated_at)
|
|
739
|
+
VALUES (?, ?, ?, CURRENT_TIMESTAMP)
|
|
740
|
+
ON CONFLICT(file_path) DO UPDATE SET
|
|
741
|
+
file_state_hash = excluded.file_state_hash,
|
|
742
|
+
source_change_id = excluded.source_change_id,
|
|
743
|
+
updated_at = CURRENT_TIMESTAMP`);
|
|
744
|
+
getSyncedFileStateStmt = db.prepare(`SELECT file_path, file_state_hash, source_change_id, updated_at
|
|
745
|
+
FROM synced_file_states
|
|
746
|
+
WHERE file_path = ?
|
|
747
|
+
LIMIT 1`);
|
|
748
|
+
insertMcpGuardEventStmt = db.prepare(`INSERT INTO mcp_guard_events
|
|
749
|
+
(tool_name, event_type, severity, title, detail, metadata_json)
|
|
750
|
+
VALUES
|
|
751
|
+
(?, ?, ?, ?, ?, ?)`);
|
|
752
|
+
insertMcpToolMetricStmt = db.prepare(`INSERT INTO mcp_tool_metrics
|
|
753
|
+
(tool_name, duration_ms, raw_output_chars, output_chars, is_error, root_source)
|
|
754
|
+
VALUES (?, ?, ?, ?, ?, ?)`);
|
|
332
755
|
insertMemoryItemStmt = db.prepare(`INSERT INTO memory_items
|
|
333
756
|
(kind, title, content, file_path, start_line, end_line, req_id, metadata_json, content_hash)
|
|
334
757
|
VALUES
|
|
@@ -468,14 +891,19 @@ export function openDatabaseRuntime(projectRoot) {
|
|
|
468
891
|
statements: {
|
|
469
892
|
insertRequirementStmt,
|
|
470
893
|
getActiveRequirementStmt,
|
|
894
|
+
getActiveRequirementByIdStmt,
|
|
895
|
+
getActiveRequirementByGoalKeyStmt,
|
|
471
896
|
listActiveRequirementsStmt,
|
|
472
897
|
listRecentRequirementsStmt,
|
|
473
898
|
completeAllActiveRequirementsStmt,
|
|
474
899
|
completeRequirementByIdStmt,
|
|
475
|
-
completeAllActiveRequirementMemoryItemsStmt,
|
|
476
900
|
completeRequirementMemoryItemByReqIdStmt,
|
|
477
901
|
listChangeLogsForRequirementStmt,
|
|
478
902
|
insertChangeLogStmt,
|
|
903
|
+
upsertSyncedFileStateStmt,
|
|
904
|
+
getSyncedFileStateStmt,
|
|
905
|
+
insertMcpGuardEventStmt,
|
|
906
|
+
insertMcpToolMetricStmt,
|
|
479
907
|
insertMemoryItemStmt,
|
|
480
908
|
getMemoryItemByIdStmt,
|
|
481
909
|
getRequirementMemoryItemIdStmt,
|