@su-record/vibe 2.6.25 → 2.6.27
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/CLAUDE.md +49 -0
- package/README.md +31 -0
- package/agents/docs/api-documenter.md +99 -0
- package/agents/docs/changelog-writer.md +93 -0
- package/agents/planning/requirements-analyst.md +84 -0
- package/agents/planning/ux-advisor.md +83 -0
- package/agents/qa/acceptance-tester.md +86 -0
- package/agents/qa/edge-case-finder.md +93 -0
- package/dist/lib/MemoryManager.d.ts +30 -0
- package/dist/lib/MemoryManager.d.ts.map +1 -1
- package/dist/lib/MemoryManager.js +74 -0
- package/dist/lib/MemoryManager.js.map +1 -1
- package/dist/lib/memory/ObservationStore.d.ts.map +1 -1
- package/dist/lib/memory/ObservationStore.js +0 -1
- package/dist/lib/memory/ObservationStore.js.map +1 -1
- package/dist/lib/memory/SessionRAGRetriever.d.ts +66 -0
- package/dist/lib/memory/SessionRAGRetriever.d.ts.map +1 -0
- package/dist/lib/memory/SessionRAGRetriever.js +196 -0
- package/dist/lib/memory/SessionRAGRetriever.js.map +1 -0
- package/dist/lib/memory/SessionRAGRetriever.test.d.ts +2 -0
- package/dist/lib/memory/SessionRAGRetriever.test.d.ts.map +1 -0
- package/dist/lib/memory/SessionRAGRetriever.test.js +180 -0
- package/dist/lib/memory/SessionRAGRetriever.test.js.map +1 -0
- package/dist/lib/memory/SessionRAGStore.d.ts +153 -0
- package/dist/lib/memory/SessionRAGStore.d.ts.map +1 -0
- package/dist/lib/memory/SessionRAGStore.js +673 -0
- package/dist/lib/memory/SessionRAGStore.js.map +1 -0
- package/dist/lib/memory/SessionRAGStore.test.d.ts +2 -0
- package/dist/lib/memory/SessionRAGStore.test.d.ts.map +1 -0
- package/dist/lib/memory/SessionRAGStore.test.js +326 -0
- package/dist/lib/memory/SessionRAGStore.test.js.map +1 -0
- package/dist/tools/index.d.ts +5 -0
- package/dist/tools/index.d.ts.map +1 -1
- package/dist/tools/index.js +4 -0
- package/dist/tools/index.js.map +1 -1
- package/dist/tools/memory/index.d.ts +3 -0
- package/dist/tools/memory/index.d.ts.map +1 -1
- package/dist/tools/memory/index.js +4 -0
- package/dist/tools/memory/index.js.map +1 -1
- package/dist/tools/memory/manageGoals.d.ts +11 -0
- package/dist/tools/memory/manageGoals.d.ts.map +1 -0
- package/dist/tools/memory/manageGoals.js +152 -0
- package/dist/tools/memory/manageGoals.js.map +1 -0
- package/dist/tools/memory/retrieveSessionContext.d.ts +9 -0
- package/dist/tools/memory/retrieveSessionContext.d.ts.map +1 -0
- package/dist/tools/memory/retrieveSessionContext.js +140 -0
- package/dist/tools/memory/retrieveSessionContext.js.map +1 -0
- package/dist/tools/memory/saveSessionItem.d.ts +26 -0
- package/dist/tools/memory/saveSessionItem.d.ts.map +1 -0
- package/dist/tools/memory/saveSessionItem.js +218 -0
- package/dist/tools/memory/saveSessionItem.js.map +1 -0
- package/dist/tools/memory/startSession.d.ts.map +1 -1
- package/dist/tools/memory/startSession.js +29 -0
- package/dist/tools/memory/startSession.js.map +1 -1
- package/dist/tools/spec/e2eTestGenerator.d.ts +62 -0
- package/dist/tools/spec/e2eTestGenerator.d.ts.map +1 -0
- package/dist/tools/spec/e2eTestGenerator.js +256 -0
- package/dist/tools/spec/e2eTestGenerator.js.map +1 -0
- package/dist/tools/spec/index.d.ts +2 -0
- package/dist/tools/spec/index.d.ts.map +1 -1
- package/dist/tools/spec/index.js +2 -0
- package/dist/tools/spec/index.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,673 @@
|
|
|
1
|
+
// Session RAG Store - Structured session context storage
|
|
2
|
+
// Manages Decisions, Constraints, Goals, Evidence with FTS5 search
|
|
3
|
+
// ============================================================================
|
|
4
|
+
// SessionRAGStore
|
|
5
|
+
// ============================================================================
|
|
6
|
+
export class SessionRAGStore {
|
|
7
|
+
db;
|
|
8
|
+
fts5Available = false;
|
|
9
|
+
constructor(storage) {
|
|
10
|
+
this.db = storage.getDatabase();
|
|
11
|
+
this.fts5Available = storage.isFTS5Available();
|
|
12
|
+
this.initializeTables();
|
|
13
|
+
}
|
|
14
|
+
// ==========================================================================
|
|
15
|
+
// Schema Initialization
|
|
16
|
+
// ==========================================================================
|
|
17
|
+
initializeTables() {
|
|
18
|
+
this.db.exec(`
|
|
19
|
+
CREATE TABLE IF NOT EXISTS session_decisions (
|
|
20
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
21
|
+
sessionId TEXT,
|
|
22
|
+
title TEXT NOT NULL,
|
|
23
|
+
description TEXT,
|
|
24
|
+
rationale TEXT,
|
|
25
|
+
alternatives TEXT,
|
|
26
|
+
impact TEXT,
|
|
27
|
+
status TEXT DEFAULT 'active' CHECK(status IN ('active','superseded','cancelled')),
|
|
28
|
+
priority INTEGER DEFAULT 1,
|
|
29
|
+
relatedFiles TEXT,
|
|
30
|
+
tags TEXT,
|
|
31
|
+
timestamp TEXT NOT NULL
|
|
32
|
+
);
|
|
33
|
+
|
|
34
|
+
CREATE INDEX IF NOT EXISTS idx_sdec_session ON session_decisions(sessionId);
|
|
35
|
+
CREATE INDEX IF NOT EXISTS idx_sdec_status ON session_decisions(status);
|
|
36
|
+
CREATE INDEX IF NOT EXISTS idx_sdec_priority ON session_decisions(priority);
|
|
37
|
+
CREATE INDEX IF NOT EXISTS idx_sdec_timestamp ON session_decisions(timestamp);
|
|
38
|
+
`);
|
|
39
|
+
this.db.exec(`
|
|
40
|
+
CREATE TABLE IF NOT EXISTS session_constraints (
|
|
41
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
42
|
+
sessionId TEXT,
|
|
43
|
+
title TEXT NOT NULL,
|
|
44
|
+
description TEXT,
|
|
45
|
+
type TEXT NOT NULL CHECK(type IN ('technical','business','resource','quality')),
|
|
46
|
+
severity TEXT DEFAULT 'medium' CHECK(severity IN ('low','medium','high','critical')),
|
|
47
|
+
scope TEXT,
|
|
48
|
+
timestamp TEXT NOT NULL
|
|
49
|
+
);
|
|
50
|
+
|
|
51
|
+
CREATE INDEX IF NOT EXISTS idx_scon_session ON session_constraints(sessionId);
|
|
52
|
+
CREATE INDEX IF NOT EXISTS idx_scon_type ON session_constraints(type);
|
|
53
|
+
CREATE INDEX IF NOT EXISTS idx_scon_severity ON session_constraints(severity);
|
|
54
|
+
`);
|
|
55
|
+
this.db.exec(`
|
|
56
|
+
CREATE TABLE IF NOT EXISTS session_goals (
|
|
57
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
58
|
+
sessionId TEXT,
|
|
59
|
+
parentId INTEGER,
|
|
60
|
+
title TEXT NOT NULL,
|
|
61
|
+
description TEXT,
|
|
62
|
+
status TEXT DEFAULT 'active' CHECK(status IN ('active','completed','blocked','cancelled')),
|
|
63
|
+
priority INTEGER DEFAULT 1,
|
|
64
|
+
progressPercent INTEGER DEFAULT 0 CHECK(progressPercent >= 0 AND progressPercent <= 100),
|
|
65
|
+
successCriteria TEXT,
|
|
66
|
+
timestamp TEXT NOT NULL,
|
|
67
|
+
completedAt TEXT,
|
|
68
|
+
FOREIGN KEY(parentId) REFERENCES session_goals(id)
|
|
69
|
+
);
|
|
70
|
+
|
|
71
|
+
CREATE INDEX IF NOT EXISTS idx_sgoal_session ON session_goals(sessionId);
|
|
72
|
+
CREATE INDEX IF NOT EXISTS idx_sgoal_status ON session_goals(status);
|
|
73
|
+
CREATE INDEX IF NOT EXISTS idx_sgoal_priority ON session_goals(priority);
|
|
74
|
+
CREATE INDEX IF NOT EXISTS idx_sgoal_parent ON session_goals(parentId);
|
|
75
|
+
`);
|
|
76
|
+
this.db.exec(`
|
|
77
|
+
CREATE TABLE IF NOT EXISTS session_evidence (
|
|
78
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
79
|
+
sessionId TEXT,
|
|
80
|
+
type TEXT NOT NULL CHECK(type IN ('test','build','lint','coverage','hud','review')),
|
|
81
|
+
title TEXT NOT NULL,
|
|
82
|
+
status TEXT NOT NULL CHECK(status IN ('pass','fail','warning','info')),
|
|
83
|
+
details TEXT,
|
|
84
|
+
metrics TEXT,
|
|
85
|
+
relatedGoals TEXT,
|
|
86
|
+
timestamp TEXT NOT NULL
|
|
87
|
+
);
|
|
88
|
+
|
|
89
|
+
CREATE INDEX IF NOT EXISTS idx_sevi_session ON session_evidence(sessionId);
|
|
90
|
+
CREATE INDEX IF NOT EXISTS idx_sevi_type ON session_evidence(type);
|
|
91
|
+
CREATE INDEX IF NOT EXISTS idx_sevi_status ON session_evidence(status);
|
|
92
|
+
CREATE INDEX IF NOT EXISTS idx_sevi_timestamp ON session_evidence(timestamp);
|
|
93
|
+
`);
|
|
94
|
+
this.initializeFTS5();
|
|
95
|
+
}
|
|
96
|
+
initializeFTS5() {
|
|
97
|
+
if (!this.fts5Available)
|
|
98
|
+
return;
|
|
99
|
+
try {
|
|
100
|
+
// Decisions FTS
|
|
101
|
+
this.db.exec(`
|
|
102
|
+
CREATE VIRTUAL TABLE IF NOT EXISTS session_decisions_fts
|
|
103
|
+
USING fts5(title, description, rationale, content=session_decisions, content_rowid=id);
|
|
104
|
+
|
|
105
|
+
CREATE TRIGGER IF NOT EXISTS sdec_fts_ai AFTER INSERT ON session_decisions BEGIN
|
|
106
|
+
INSERT INTO session_decisions_fts(rowid, title, description, rationale)
|
|
107
|
+
VALUES (new.id, new.title, new.description, new.rationale);
|
|
108
|
+
END;
|
|
109
|
+
CREATE TRIGGER IF NOT EXISTS sdec_fts_ad AFTER DELETE ON session_decisions BEGIN
|
|
110
|
+
INSERT INTO session_decisions_fts(session_decisions_fts, rowid, title, description, rationale)
|
|
111
|
+
VALUES('delete', old.id, old.title, old.description, old.rationale);
|
|
112
|
+
END;
|
|
113
|
+
CREATE TRIGGER IF NOT EXISTS sdec_fts_au AFTER UPDATE ON session_decisions BEGIN
|
|
114
|
+
INSERT INTO session_decisions_fts(session_decisions_fts, rowid, title, description, rationale)
|
|
115
|
+
VALUES('delete', old.id, old.title, old.description, old.rationale);
|
|
116
|
+
INSERT INTO session_decisions_fts(rowid, title, description, rationale)
|
|
117
|
+
VALUES (new.id, new.title, new.description, new.rationale);
|
|
118
|
+
END;
|
|
119
|
+
`);
|
|
120
|
+
// Constraints FTS
|
|
121
|
+
this.db.exec(`
|
|
122
|
+
CREATE VIRTUAL TABLE IF NOT EXISTS session_constraints_fts
|
|
123
|
+
USING fts5(title, description, content=session_constraints, content_rowid=id);
|
|
124
|
+
|
|
125
|
+
CREATE TRIGGER IF NOT EXISTS scon_fts_ai AFTER INSERT ON session_constraints BEGIN
|
|
126
|
+
INSERT INTO session_constraints_fts(rowid, title, description)
|
|
127
|
+
VALUES (new.id, new.title, new.description);
|
|
128
|
+
END;
|
|
129
|
+
CREATE TRIGGER IF NOT EXISTS scon_fts_ad AFTER DELETE ON session_constraints BEGIN
|
|
130
|
+
INSERT INTO session_constraints_fts(session_constraints_fts, rowid, title, description)
|
|
131
|
+
VALUES('delete', old.id, old.title, old.description);
|
|
132
|
+
END;
|
|
133
|
+
CREATE TRIGGER IF NOT EXISTS scon_fts_au AFTER UPDATE ON session_constraints BEGIN
|
|
134
|
+
INSERT INTO session_constraints_fts(session_constraints_fts, rowid, title, description)
|
|
135
|
+
VALUES('delete', old.id, old.title, old.description);
|
|
136
|
+
INSERT INTO session_constraints_fts(rowid, title, description)
|
|
137
|
+
VALUES (new.id, new.title, new.description);
|
|
138
|
+
END;
|
|
139
|
+
`);
|
|
140
|
+
// Goals FTS
|
|
141
|
+
this.db.exec(`
|
|
142
|
+
CREATE VIRTUAL TABLE IF NOT EXISTS session_goals_fts
|
|
143
|
+
USING fts5(title, description, content=session_goals, content_rowid=id);
|
|
144
|
+
|
|
145
|
+
CREATE TRIGGER IF NOT EXISTS sgoal_fts_ai AFTER INSERT ON session_goals BEGIN
|
|
146
|
+
INSERT INTO session_goals_fts(rowid, title, description)
|
|
147
|
+
VALUES (new.id, new.title, new.description);
|
|
148
|
+
END;
|
|
149
|
+
CREATE TRIGGER IF NOT EXISTS sgoal_fts_ad AFTER DELETE ON session_goals BEGIN
|
|
150
|
+
INSERT INTO session_goals_fts(session_goals_fts, rowid, title, description)
|
|
151
|
+
VALUES('delete', old.id, old.title, old.description);
|
|
152
|
+
END;
|
|
153
|
+
CREATE TRIGGER IF NOT EXISTS sgoal_fts_au AFTER UPDATE ON session_goals BEGIN
|
|
154
|
+
INSERT INTO session_goals_fts(session_goals_fts, rowid, title, description)
|
|
155
|
+
VALUES('delete', old.id, old.title, old.description);
|
|
156
|
+
INSERT INTO session_goals_fts(rowid, title, description)
|
|
157
|
+
VALUES (new.id, new.title, new.description);
|
|
158
|
+
END;
|
|
159
|
+
`);
|
|
160
|
+
// Evidence FTS
|
|
161
|
+
this.db.exec(`
|
|
162
|
+
CREATE VIRTUAL TABLE IF NOT EXISTS session_evidence_fts
|
|
163
|
+
USING fts5(title, details, content=session_evidence, content_rowid=id);
|
|
164
|
+
|
|
165
|
+
CREATE TRIGGER IF NOT EXISTS sevi_fts_ai AFTER INSERT ON session_evidence BEGIN
|
|
166
|
+
INSERT INTO session_evidence_fts(rowid, title, details)
|
|
167
|
+
VALUES (new.id, new.title, new.details);
|
|
168
|
+
END;
|
|
169
|
+
CREATE TRIGGER IF NOT EXISTS sevi_fts_ad AFTER DELETE ON session_evidence BEGIN
|
|
170
|
+
INSERT INTO session_evidence_fts(session_evidence_fts, rowid, title, details)
|
|
171
|
+
VALUES('delete', old.id, old.title, old.details);
|
|
172
|
+
END;
|
|
173
|
+
CREATE TRIGGER IF NOT EXISTS sevi_fts_au AFTER UPDATE ON session_evidence BEGIN
|
|
174
|
+
INSERT INTO session_evidence_fts(session_evidence_fts, rowid, title, details)
|
|
175
|
+
VALUES('delete', old.id, old.title, old.details);
|
|
176
|
+
INSERT INTO session_evidence_fts(rowid, title, details)
|
|
177
|
+
VALUES (new.id, new.title, new.details);
|
|
178
|
+
END;
|
|
179
|
+
`);
|
|
180
|
+
}
|
|
181
|
+
catch {
|
|
182
|
+
// FTS5 initialization failed for session RAG tables - non-critical
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
// ==========================================================================
|
|
186
|
+
// Decisions
|
|
187
|
+
// ==========================================================================
|
|
188
|
+
addDecision(input) {
|
|
189
|
+
const timestamp = new Date().toISOString();
|
|
190
|
+
const stmt = this.db.prepare(`
|
|
191
|
+
INSERT INTO session_decisions (sessionId, title, description, rationale, alternatives, impact, status, priority, relatedFiles, tags, timestamp)
|
|
192
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
193
|
+
`);
|
|
194
|
+
const result = stmt.run(input.sessionId || null, input.title, input.description || null, input.rationale || null, input.alternatives ? JSON.stringify(input.alternatives) : null, input.impact || null, input.status || 'active', input.priority ?? 1, input.relatedFiles ? JSON.stringify(input.relatedFiles) : null, input.tags ? JSON.stringify(input.tags) : null, timestamp);
|
|
195
|
+
return result.lastInsertRowid;
|
|
196
|
+
}
|
|
197
|
+
getDecision(id) {
|
|
198
|
+
const row = this.db.prepare(`SELECT * FROM session_decisions WHERE id = ?`).get(id);
|
|
199
|
+
return row ? this.rowToDecision(row) : null;
|
|
200
|
+
}
|
|
201
|
+
updateDecision(id, updates) {
|
|
202
|
+
const fields = [];
|
|
203
|
+
const values = [];
|
|
204
|
+
if (updates.title !== undefined) {
|
|
205
|
+
fields.push('title = ?');
|
|
206
|
+
values.push(updates.title);
|
|
207
|
+
}
|
|
208
|
+
if (updates.description !== undefined) {
|
|
209
|
+
fields.push('description = ?');
|
|
210
|
+
values.push(updates.description);
|
|
211
|
+
}
|
|
212
|
+
if (updates.rationale !== undefined) {
|
|
213
|
+
fields.push('rationale = ?');
|
|
214
|
+
values.push(updates.rationale);
|
|
215
|
+
}
|
|
216
|
+
if (updates.alternatives !== undefined) {
|
|
217
|
+
fields.push('alternatives = ?');
|
|
218
|
+
values.push(JSON.stringify(updates.alternatives));
|
|
219
|
+
}
|
|
220
|
+
if (updates.impact !== undefined) {
|
|
221
|
+
fields.push('impact = ?');
|
|
222
|
+
values.push(updates.impact);
|
|
223
|
+
}
|
|
224
|
+
if (updates.status !== undefined) {
|
|
225
|
+
fields.push('status = ?');
|
|
226
|
+
values.push(updates.status);
|
|
227
|
+
}
|
|
228
|
+
if (updates.priority !== undefined) {
|
|
229
|
+
fields.push('priority = ?');
|
|
230
|
+
values.push(updates.priority);
|
|
231
|
+
}
|
|
232
|
+
if (updates.relatedFiles !== undefined) {
|
|
233
|
+
fields.push('relatedFiles = ?');
|
|
234
|
+
values.push(JSON.stringify(updates.relatedFiles));
|
|
235
|
+
}
|
|
236
|
+
if (updates.tags !== undefined) {
|
|
237
|
+
fields.push('tags = ?');
|
|
238
|
+
values.push(JSON.stringify(updates.tags));
|
|
239
|
+
}
|
|
240
|
+
if (fields.length === 0)
|
|
241
|
+
return false;
|
|
242
|
+
values.push(id);
|
|
243
|
+
const result = this.db.prepare(`UPDATE session_decisions SET ${fields.join(', ')} WHERE id = ?`).run(...values);
|
|
244
|
+
return result.changes > 0;
|
|
245
|
+
}
|
|
246
|
+
listDecisions(sessionId, status, limit = 50) {
|
|
247
|
+
let sql = 'SELECT * FROM session_decisions WHERE 1=1';
|
|
248
|
+
const params = [];
|
|
249
|
+
if (sessionId) {
|
|
250
|
+
sql += ' AND sessionId = ?';
|
|
251
|
+
params.push(sessionId);
|
|
252
|
+
}
|
|
253
|
+
if (status) {
|
|
254
|
+
sql += ' AND status = ?';
|
|
255
|
+
params.push(status);
|
|
256
|
+
}
|
|
257
|
+
sql += ' ORDER BY priority DESC, timestamp DESC LIMIT ?';
|
|
258
|
+
params.push(limit);
|
|
259
|
+
const rows = this.db.prepare(sql).all(...params);
|
|
260
|
+
return rows.map(this.rowToDecision);
|
|
261
|
+
}
|
|
262
|
+
searchDecisions(query, limit = 20) {
|
|
263
|
+
if (this.fts5Available) {
|
|
264
|
+
try {
|
|
265
|
+
const rows = this.db.prepare(`
|
|
266
|
+
SELECT d.*, bm25(session_decisions_fts) as rank
|
|
267
|
+
FROM session_decisions_fts fts
|
|
268
|
+
JOIN session_decisions d ON d.id = fts.rowid
|
|
269
|
+
WHERE session_decisions_fts MATCH ?
|
|
270
|
+
ORDER BY rank
|
|
271
|
+
LIMIT ?
|
|
272
|
+
`).all(query, limit);
|
|
273
|
+
return rows.map(this.rowToDecision);
|
|
274
|
+
}
|
|
275
|
+
catch {
|
|
276
|
+
// FTS5 query failed, fall through to LIKE
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
const pattern = `%${query}%`;
|
|
280
|
+
const rows = this.db.prepare(`
|
|
281
|
+
SELECT * FROM session_decisions
|
|
282
|
+
WHERE title LIKE ? OR description LIKE ? OR rationale LIKE ?
|
|
283
|
+
ORDER BY priority DESC, timestamp DESC LIMIT ?
|
|
284
|
+
`).all(pattern, pattern, pattern, limit);
|
|
285
|
+
return rows.map(this.rowToDecision);
|
|
286
|
+
}
|
|
287
|
+
deleteDecision(id) {
|
|
288
|
+
const result = this.db.prepare('DELETE FROM session_decisions WHERE id = ?').run(id);
|
|
289
|
+
return result.changes > 0;
|
|
290
|
+
}
|
|
291
|
+
rowToDecision(row) {
|
|
292
|
+
return {
|
|
293
|
+
id: row.id,
|
|
294
|
+
sessionId: row.sessionId,
|
|
295
|
+
title: row.title,
|
|
296
|
+
description: row.description,
|
|
297
|
+
rationale: row.rationale,
|
|
298
|
+
alternatives: row.alternatives ? JSON.parse(row.alternatives) : [],
|
|
299
|
+
impact: row.impact,
|
|
300
|
+
status: row.status,
|
|
301
|
+
priority: row.priority,
|
|
302
|
+
relatedFiles: row.relatedFiles ? JSON.parse(row.relatedFiles) : [],
|
|
303
|
+
tags: row.tags ? JSON.parse(row.tags) : [],
|
|
304
|
+
timestamp: row.timestamp,
|
|
305
|
+
};
|
|
306
|
+
}
|
|
307
|
+
// ==========================================================================
|
|
308
|
+
// Constraints
|
|
309
|
+
// ==========================================================================
|
|
310
|
+
addConstraint(input) {
|
|
311
|
+
const timestamp = new Date().toISOString();
|
|
312
|
+
const stmt = this.db.prepare(`
|
|
313
|
+
INSERT INTO session_constraints (sessionId, title, description, type, severity, scope, timestamp)
|
|
314
|
+
VALUES (?, ?, ?, ?, ?, ?, ?)
|
|
315
|
+
`);
|
|
316
|
+
const result = stmt.run(input.sessionId || null, input.title, input.description || null, input.type, input.severity || 'medium', input.scope || null, timestamp);
|
|
317
|
+
return result.lastInsertRowid;
|
|
318
|
+
}
|
|
319
|
+
getConstraint(id) {
|
|
320
|
+
const row = this.db.prepare('SELECT * FROM session_constraints WHERE id = ?').get(id);
|
|
321
|
+
return row ? this.rowToConstraint(row) : null;
|
|
322
|
+
}
|
|
323
|
+
updateConstraint(id, updates) {
|
|
324
|
+
const fields = [];
|
|
325
|
+
const values = [];
|
|
326
|
+
if (updates.title !== undefined) {
|
|
327
|
+
fields.push('title = ?');
|
|
328
|
+
values.push(updates.title);
|
|
329
|
+
}
|
|
330
|
+
if (updates.description !== undefined) {
|
|
331
|
+
fields.push('description = ?');
|
|
332
|
+
values.push(updates.description);
|
|
333
|
+
}
|
|
334
|
+
if (updates.type !== undefined) {
|
|
335
|
+
fields.push('type = ?');
|
|
336
|
+
values.push(updates.type);
|
|
337
|
+
}
|
|
338
|
+
if (updates.severity !== undefined) {
|
|
339
|
+
fields.push('severity = ?');
|
|
340
|
+
values.push(updates.severity);
|
|
341
|
+
}
|
|
342
|
+
if (updates.scope !== undefined) {
|
|
343
|
+
fields.push('scope = ?');
|
|
344
|
+
values.push(updates.scope);
|
|
345
|
+
}
|
|
346
|
+
if (fields.length === 0)
|
|
347
|
+
return false;
|
|
348
|
+
values.push(id);
|
|
349
|
+
const result = this.db.prepare(`UPDATE session_constraints SET ${fields.join(', ')} WHERE id = ?`).run(...values);
|
|
350
|
+
return result.changes > 0;
|
|
351
|
+
}
|
|
352
|
+
listConstraints(sessionId, type, severity, limit = 50) {
|
|
353
|
+
let sql = 'SELECT * FROM session_constraints WHERE 1=1';
|
|
354
|
+
const params = [];
|
|
355
|
+
if (sessionId) {
|
|
356
|
+
sql += ' AND sessionId = ?';
|
|
357
|
+
params.push(sessionId);
|
|
358
|
+
}
|
|
359
|
+
if (type) {
|
|
360
|
+
sql += ' AND type = ?';
|
|
361
|
+
params.push(type);
|
|
362
|
+
}
|
|
363
|
+
if (severity) {
|
|
364
|
+
sql += ' AND severity = ?';
|
|
365
|
+
params.push(severity);
|
|
366
|
+
}
|
|
367
|
+
sql += ' ORDER BY CASE severity WHEN \'critical\' THEN 0 WHEN \'high\' THEN 1 WHEN \'medium\' THEN 2 WHEN \'low\' THEN 3 END, timestamp DESC LIMIT ?';
|
|
368
|
+
params.push(limit);
|
|
369
|
+
const rows = this.db.prepare(sql).all(...params);
|
|
370
|
+
return rows.map(this.rowToConstraint);
|
|
371
|
+
}
|
|
372
|
+
searchConstraints(query, limit = 20) {
|
|
373
|
+
if (this.fts5Available) {
|
|
374
|
+
try {
|
|
375
|
+
const rows = this.db.prepare(`
|
|
376
|
+
SELECT c.*, bm25(session_constraints_fts) as rank
|
|
377
|
+
FROM session_constraints_fts fts
|
|
378
|
+
JOIN session_constraints c ON c.id = fts.rowid
|
|
379
|
+
WHERE session_constraints_fts MATCH ?
|
|
380
|
+
ORDER BY rank
|
|
381
|
+
LIMIT ?
|
|
382
|
+
`).all(query, limit);
|
|
383
|
+
return rows.map(this.rowToConstraint);
|
|
384
|
+
}
|
|
385
|
+
catch {
|
|
386
|
+
// Fall through to LIKE
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
const pattern = `%${query}%`;
|
|
390
|
+
const rows = this.db.prepare(`
|
|
391
|
+
SELECT * FROM session_constraints
|
|
392
|
+
WHERE title LIKE ? OR description LIKE ?
|
|
393
|
+
ORDER BY timestamp DESC LIMIT ?
|
|
394
|
+
`).all(pattern, pattern, limit);
|
|
395
|
+
return rows.map(this.rowToConstraint);
|
|
396
|
+
}
|
|
397
|
+
deleteConstraint(id) {
|
|
398
|
+
const result = this.db.prepare('DELETE FROM session_constraints WHERE id = ?').run(id);
|
|
399
|
+
return result.changes > 0;
|
|
400
|
+
}
|
|
401
|
+
rowToConstraint(row) {
|
|
402
|
+
return {
|
|
403
|
+
id: row.id,
|
|
404
|
+
sessionId: row.sessionId,
|
|
405
|
+
title: row.title,
|
|
406
|
+
description: row.description,
|
|
407
|
+
type: row.type,
|
|
408
|
+
severity: row.severity,
|
|
409
|
+
scope: row.scope,
|
|
410
|
+
timestamp: row.timestamp,
|
|
411
|
+
};
|
|
412
|
+
}
|
|
413
|
+
// ==========================================================================
|
|
414
|
+
// Goals
|
|
415
|
+
// ==========================================================================
|
|
416
|
+
addGoal(input) {
|
|
417
|
+
const timestamp = new Date().toISOString();
|
|
418
|
+
const stmt = this.db.prepare(`
|
|
419
|
+
INSERT INTO session_goals (sessionId, parentId, title, description, status, priority, progressPercent, successCriteria, timestamp, completedAt)
|
|
420
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
421
|
+
`);
|
|
422
|
+
const result = stmt.run(input.sessionId || null, input.parentId || null, input.title, input.description || null, input.status || 'active', input.priority ?? 1, input.progressPercent ?? 0, input.successCriteria ? JSON.stringify(input.successCriteria) : null, timestamp, null);
|
|
423
|
+
return result.lastInsertRowid;
|
|
424
|
+
}
|
|
425
|
+
getGoal(id) {
|
|
426
|
+
const row = this.db.prepare('SELECT * FROM session_goals WHERE id = ?').get(id);
|
|
427
|
+
return row ? this.rowToGoal(row) : null;
|
|
428
|
+
}
|
|
429
|
+
updateGoal(id, updates) {
|
|
430
|
+
const fields = [];
|
|
431
|
+
const values = [];
|
|
432
|
+
if (updates.title !== undefined) {
|
|
433
|
+
fields.push('title = ?');
|
|
434
|
+
values.push(updates.title);
|
|
435
|
+
}
|
|
436
|
+
if (updates.description !== undefined) {
|
|
437
|
+
fields.push('description = ?');
|
|
438
|
+
values.push(updates.description);
|
|
439
|
+
}
|
|
440
|
+
if (updates.status !== undefined) {
|
|
441
|
+
fields.push('status = ?');
|
|
442
|
+
values.push(updates.status);
|
|
443
|
+
if (updates.status === 'completed') {
|
|
444
|
+
fields.push('completedAt = ?');
|
|
445
|
+
values.push(new Date().toISOString());
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
if (updates.priority !== undefined) {
|
|
449
|
+
fields.push('priority = ?');
|
|
450
|
+
values.push(updates.priority);
|
|
451
|
+
}
|
|
452
|
+
if (updates.progressPercent !== undefined) {
|
|
453
|
+
fields.push('progressPercent = ?');
|
|
454
|
+
values.push(updates.progressPercent);
|
|
455
|
+
}
|
|
456
|
+
if (updates.successCriteria !== undefined) {
|
|
457
|
+
fields.push('successCriteria = ?');
|
|
458
|
+
values.push(JSON.stringify(updates.successCriteria));
|
|
459
|
+
}
|
|
460
|
+
if (updates.parentId !== undefined) {
|
|
461
|
+
fields.push('parentId = ?');
|
|
462
|
+
values.push(updates.parentId);
|
|
463
|
+
}
|
|
464
|
+
if (fields.length === 0)
|
|
465
|
+
return false;
|
|
466
|
+
values.push(id);
|
|
467
|
+
const result = this.db.prepare(`UPDATE session_goals SET ${fields.join(', ')} WHERE id = ?`).run(...values);
|
|
468
|
+
return result.changes > 0;
|
|
469
|
+
}
|
|
470
|
+
listGoals(sessionId, status, limit = 50) {
|
|
471
|
+
let sql = 'SELECT * FROM session_goals WHERE 1=1';
|
|
472
|
+
const params = [];
|
|
473
|
+
if (sessionId) {
|
|
474
|
+
sql += ' AND sessionId = ?';
|
|
475
|
+
params.push(sessionId);
|
|
476
|
+
}
|
|
477
|
+
if (status) {
|
|
478
|
+
sql += ' AND status = ?';
|
|
479
|
+
params.push(status);
|
|
480
|
+
}
|
|
481
|
+
sql += ' ORDER BY priority DESC, timestamp DESC LIMIT ?';
|
|
482
|
+
params.push(limit);
|
|
483
|
+
const rows = this.db.prepare(sql).all(...params);
|
|
484
|
+
return rows.map(this.rowToGoal);
|
|
485
|
+
}
|
|
486
|
+
getActiveGoals(limit = 10) {
|
|
487
|
+
const rows = this.db.prepare(`
|
|
488
|
+
SELECT * FROM session_goals
|
|
489
|
+
WHERE status = 'active'
|
|
490
|
+
ORDER BY priority DESC, timestamp DESC
|
|
491
|
+
LIMIT ?
|
|
492
|
+
`).all(limit);
|
|
493
|
+
return rows.map(this.rowToGoal);
|
|
494
|
+
}
|
|
495
|
+
getGoalHierarchy(rootId) {
|
|
496
|
+
if (rootId) {
|
|
497
|
+
// Get root + all descendants (max 3 levels)
|
|
498
|
+
const rows = this.db.prepare(`
|
|
499
|
+
SELECT * FROM session_goals
|
|
500
|
+
WHERE id = ? OR parentId = ?
|
|
501
|
+
ORDER BY parentId NULLS FIRST, priority DESC
|
|
502
|
+
`).all(rootId, rootId);
|
|
503
|
+
return rows.map(this.rowToGoal);
|
|
504
|
+
}
|
|
505
|
+
// Get all top-level goals with children
|
|
506
|
+
const rows = this.db.prepare(`
|
|
507
|
+
SELECT * FROM session_goals
|
|
508
|
+
ORDER BY parentId NULLS FIRST, priority DESC, timestamp DESC
|
|
509
|
+
`).all();
|
|
510
|
+
return rows.map(this.rowToGoal);
|
|
511
|
+
}
|
|
512
|
+
searchGoals(query, limit = 20) {
|
|
513
|
+
if (this.fts5Available) {
|
|
514
|
+
try {
|
|
515
|
+
const rows = this.db.prepare(`
|
|
516
|
+
SELECT g.*, bm25(session_goals_fts) as rank
|
|
517
|
+
FROM session_goals_fts fts
|
|
518
|
+
JOIN session_goals g ON g.id = fts.rowid
|
|
519
|
+
WHERE session_goals_fts MATCH ?
|
|
520
|
+
ORDER BY rank
|
|
521
|
+
LIMIT ?
|
|
522
|
+
`).all(query, limit);
|
|
523
|
+
return rows.map(this.rowToGoal);
|
|
524
|
+
}
|
|
525
|
+
catch {
|
|
526
|
+
// Fall through to LIKE
|
|
527
|
+
}
|
|
528
|
+
}
|
|
529
|
+
const pattern = `%${query}%`;
|
|
530
|
+
const rows = this.db.prepare(`
|
|
531
|
+
SELECT * FROM session_goals
|
|
532
|
+
WHERE title LIKE ? OR description LIKE ?
|
|
533
|
+
ORDER BY priority DESC, timestamp DESC LIMIT ?
|
|
534
|
+
`).all(pattern, pattern, limit);
|
|
535
|
+
return rows.map(this.rowToGoal);
|
|
536
|
+
}
|
|
537
|
+
deleteGoal(id) {
|
|
538
|
+
const result = this.db.prepare('DELETE FROM session_goals WHERE id = ?').run(id);
|
|
539
|
+
return result.changes > 0;
|
|
540
|
+
}
|
|
541
|
+
rowToGoal(row) {
|
|
542
|
+
return {
|
|
543
|
+
id: row.id,
|
|
544
|
+
sessionId: row.sessionId,
|
|
545
|
+
parentId: row.parentId,
|
|
546
|
+
title: row.title,
|
|
547
|
+
description: row.description,
|
|
548
|
+
status: row.status,
|
|
549
|
+
priority: row.priority,
|
|
550
|
+
progressPercent: row.progressPercent,
|
|
551
|
+
successCriteria: row.successCriteria ? JSON.parse(row.successCriteria) : [],
|
|
552
|
+
timestamp: row.timestamp,
|
|
553
|
+
completedAt: row.completedAt,
|
|
554
|
+
};
|
|
555
|
+
}
|
|
556
|
+
// ==========================================================================
|
|
557
|
+
// Evidence
|
|
558
|
+
// ==========================================================================
|
|
559
|
+
addEvidence(input) {
|
|
560
|
+
const timestamp = new Date().toISOString();
|
|
561
|
+
const stmt = this.db.prepare(`
|
|
562
|
+
INSERT INTO session_evidence (sessionId, type, title, status, details, metrics, relatedGoals, timestamp)
|
|
563
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?)
|
|
564
|
+
`);
|
|
565
|
+
const result = stmt.run(input.sessionId || null, input.type, input.title, input.status, input.details ? JSON.stringify(input.details) : null, input.metrics ? JSON.stringify(input.metrics) : null, input.relatedGoals ? JSON.stringify(input.relatedGoals) : null, timestamp);
|
|
566
|
+
return result.lastInsertRowid;
|
|
567
|
+
}
|
|
568
|
+
getEvidence(id) {
|
|
569
|
+
const row = this.db.prepare('SELECT * FROM session_evidence WHERE id = ?').get(id);
|
|
570
|
+
return row ? this.rowToEvidence(row) : null;
|
|
571
|
+
}
|
|
572
|
+
listEvidence(sessionId, type, status, limit = 50) {
|
|
573
|
+
let sql = 'SELECT * FROM session_evidence WHERE 1=1';
|
|
574
|
+
const params = [];
|
|
575
|
+
if (sessionId) {
|
|
576
|
+
sql += ' AND sessionId = ?';
|
|
577
|
+
params.push(sessionId);
|
|
578
|
+
}
|
|
579
|
+
if (type) {
|
|
580
|
+
sql += ' AND type = ?';
|
|
581
|
+
params.push(type);
|
|
582
|
+
}
|
|
583
|
+
if (status) {
|
|
584
|
+
sql += ' AND status = ?';
|
|
585
|
+
params.push(status);
|
|
586
|
+
}
|
|
587
|
+
sql += ' ORDER BY timestamp DESC LIMIT ?';
|
|
588
|
+
params.push(limit);
|
|
589
|
+
const rows = this.db.prepare(sql).all(...params);
|
|
590
|
+
return rows.map(this.rowToEvidence);
|
|
591
|
+
}
|
|
592
|
+
getRecentEvidence(limit = 10) {
|
|
593
|
+
const rows = this.db.prepare(`
|
|
594
|
+
SELECT * FROM session_evidence
|
|
595
|
+
ORDER BY timestamp DESC
|
|
596
|
+
LIMIT ?
|
|
597
|
+
`).all(limit);
|
|
598
|
+
return rows.map(this.rowToEvidence);
|
|
599
|
+
}
|
|
600
|
+
searchEvidence(query, limit = 20) {
|
|
601
|
+
if (this.fts5Available) {
|
|
602
|
+
try {
|
|
603
|
+
const rows = this.db.prepare(`
|
|
604
|
+
SELECT e.*, bm25(session_evidence_fts) as rank
|
|
605
|
+
FROM session_evidence_fts fts
|
|
606
|
+
JOIN session_evidence e ON e.id = fts.rowid
|
|
607
|
+
WHERE session_evidence_fts MATCH ?
|
|
608
|
+
ORDER BY rank
|
|
609
|
+
LIMIT ?
|
|
610
|
+
`).all(query, limit);
|
|
611
|
+
return rows.map(this.rowToEvidence);
|
|
612
|
+
}
|
|
613
|
+
catch {
|
|
614
|
+
// Fall through to LIKE
|
|
615
|
+
}
|
|
616
|
+
}
|
|
617
|
+
const pattern = `%${query}%`;
|
|
618
|
+
const rows = this.db.prepare(`
|
|
619
|
+
SELECT * FROM session_evidence
|
|
620
|
+
WHERE title LIKE ? OR details LIKE ?
|
|
621
|
+
ORDER BY timestamp DESC LIMIT ?
|
|
622
|
+
`).all(pattern, pattern, limit);
|
|
623
|
+
return rows.map(this.rowToEvidence);
|
|
624
|
+
}
|
|
625
|
+
deleteEvidence(id) {
|
|
626
|
+
const result = this.db.prepare('DELETE FROM session_evidence WHERE id = ?').run(id);
|
|
627
|
+
return result.changes > 0;
|
|
628
|
+
}
|
|
629
|
+
rowToEvidence(row) {
|
|
630
|
+
return {
|
|
631
|
+
id: row.id,
|
|
632
|
+
sessionId: row.sessionId,
|
|
633
|
+
type: row.type,
|
|
634
|
+
title: row.title,
|
|
635
|
+
status: row.status,
|
|
636
|
+
details: row.details ? JSON.parse(row.details) : null,
|
|
637
|
+
metrics: row.metrics ? JSON.parse(row.metrics) : null,
|
|
638
|
+
relatedGoals: row.relatedGoals ? JSON.parse(row.relatedGoals) : [],
|
|
639
|
+
timestamp: row.timestamp,
|
|
640
|
+
};
|
|
641
|
+
}
|
|
642
|
+
// ==========================================================================
|
|
643
|
+
// Stats
|
|
644
|
+
// ==========================================================================
|
|
645
|
+
getStats() {
|
|
646
|
+
const decStatuses = this.db.prepare('SELECT status, COUNT(*) as count FROM session_decisions GROUP BY status').all();
|
|
647
|
+
const conTypes = this.db.prepare('SELECT type, COUNT(*) as count FROM session_constraints GROUP BY type').all();
|
|
648
|
+
const goalStatuses = this.db.prepare('SELECT status, COUNT(*) as count FROM session_goals GROUP BY status').all();
|
|
649
|
+
const eviTypes = this.db.prepare('SELECT type, COUNT(*) as count FROM session_evidence GROUP BY type').all();
|
|
650
|
+
const toRecord = (rows, keyField) => {
|
|
651
|
+
const record = {};
|
|
652
|
+
let total = 0;
|
|
653
|
+
for (const row of rows) {
|
|
654
|
+
const key = row[keyField];
|
|
655
|
+
const count = row.count;
|
|
656
|
+
record[key] = count;
|
|
657
|
+
total += count;
|
|
658
|
+
}
|
|
659
|
+
return { total, record };
|
|
660
|
+
};
|
|
661
|
+
const dec = toRecord(decStatuses, 'status');
|
|
662
|
+
const con = toRecord(conTypes, 'type');
|
|
663
|
+
const goal = toRecord(goalStatuses, 'status');
|
|
664
|
+
const evi = toRecord(eviTypes, 'type');
|
|
665
|
+
return {
|
|
666
|
+
decisions: { total: dec.total, byStatus: dec.record },
|
|
667
|
+
constraints: { total: con.total, byType: con.record },
|
|
668
|
+
goals: { total: goal.total, byStatus: goal.record },
|
|
669
|
+
evidence: { total: evi.total, byType: evi.record },
|
|
670
|
+
};
|
|
671
|
+
}
|
|
672
|
+
}
|
|
673
|
+
//# sourceMappingURL=SessionRAGStore.js.map
|