learning-agent 0.2.2 → 0.2.3
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/CHANGELOG.md +46 -1
- package/README.md +103 -0
- package/dist/cli.js +1341 -871
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +28 -45
- package/dist/index.js +161 -103
- package/dist/index.js.map +1 -1
- package/package.json +21 -10
package/dist/index.d.ts
CHANGED
|
@@ -222,61 +222,40 @@ declare function appendLesson(repoRoot: string, lesson: Lesson): Promise<void>;
|
|
|
222
222
|
declare function readLessons(repoRoot: string, options?: ReadLessonsOptions): Promise<ReadLessonsResult>;
|
|
223
223
|
|
|
224
224
|
/**
|
|
225
|
-
* SQLite
|
|
226
|
-
*
|
|
227
|
-
* Rebuildable index - not the source of truth.
|
|
228
|
-
* Stored in .claude/.cache (gitignored).
|
|
225
|
+
* SQLite database connection management.
|
|
229
226
|
*/
|
|
230
227
|
|
|
231
228
|
/** Relative path to database file from repo root */
|
|
232
229
|
declare const DB_PATH = ".claude/.cache/lessons.sqlite";
|
|
233
230
|
/**
|
|
234
|
-
* Close the database connection
|
|
235
|
-
*
|
|
236
|
-
* **Resource lifecycle:**
|
|
237
|
-
* - The database is opened lazily on first call to `openDb()` or any function that uses it
|
|
238
|
-
* (e.g., `searchKeyword`, `rebuildIndex`, `syncIfNeeded`, `getCachedEmbedding`)
|
|
239
|
-
* - Once opened, the connection remains active until `closeDb()` is called
|
|
240
|
-
* - After closing, subsequent database operations will reopen the connection
|
|
241
|
-
*
|
|
242
|
-
* **When to call:**
|
|
243
|
-
* - At the end of CLI commands to ensure clean process exit
|
|
244
|
-
* - When transitioning between repositories in long-running processes
|
|
245
|
-
* - Before process exit in graceful shutdown handlers
|
|
246
|
-
*
|
|
247
|
-
* **Best practices for long-running processes:**
|
|
248
|
-
* - In single-operation scripts: call before exit
|
|
249
|
-
* - In daemon/server processes: call in shutdown handler
|
|
250
|
-
* - Not necessary to call between operations in the same repository
|
|
251
|
-
*
|
|
252
|
-
* @example
|
|
253
|
-
* ```typescript
|
|
254
|
-
* // CLI command pattern
|
|
255
|
-
* try {
|
|
256
|
-
* await searchKeyword(repoRoot, 'typescript', 10);
|
|
257
|
-
* // ... process results
|
|
258
|
-
* } finally {
|
|
259
|
-
* closeDb();
|
|
260
|
-
* }
|
|
261
|
-
*
|
|
262
|
-
* // Graceful shutdown pattern
|
|
263
|
-
* process.on('SIGTERM', () => {
|
|
264
|
-
* closeDb();
|
|
265
|
-
* process.exit(0);
|
|
266
|
-
* });
|
|
267
|
-
* ```
|
|
231
|
+
* Close the SQLite database connection.
|
|
268
232
|
*/
|
|
269
233
|
declare function closeDb(): void;
|
|
234
|
+
|
|
235
|
+
/**
|
|
236
|
+
* SQLite index synchronization with JSONL source of truth.
|
|
237
|
+
*/
|
|
238
|
+
|
|
270
239
|
/**
|
|
271
|
-
* Rebuild the SQLite index from
|
|
272
|
-
*
|
|
273
|
-
*
|
|
240
|
+
* Rebuild the SQLite index from JSONL source of truth.
|
|
241
|
+
* Gracefully degrades: no-op with warning if SQLite unavailable.
|
|
242
|
+
* Preserves cached embeddings when lesson content hasn't changed.
|
|
243
|
+
* @param repoRoot - Absolute path to repository root
|
|
274
244
|
*/
|
|
275
245
|
declare function rebuildIndex(repoRoot: string): Promise<void>;
|
|
246
|
+
|
|
247
|
+
/**
|
|
248
|
+
* SQLite search operations using FTS5 full-text search.
|
|
249
|
+
*/
|
|
250
|
+
|
|
276
251
|
/**
|
|
277
|
-
* Search lessons using FTS5
|
|
278
|
-
*
|
|
279
|
-
*
|
|
252
|
+
* Search lessons using FTS5 full-text search.
|
|
253
|
+
* Does NOT degrade gracefully: throws error if SQLite unavailable.
|
|
254
|
+
* @param repoRoot - Absolute path to repository root
|
|
255
|
+
* @param query - FTS5 query string
|
|
256
|
+
* @param limit - Maximum number of results
|
|
257
|
+
* @returns Matching lessons
|
|
258
|
+
* @throws Error if SQLite unavailable (FTS5 required)
|
|
280
259
|
*/
|
|
281
260
|
declare function searchKeyword(repoRoot: string, query: string, limit: number): Promise<Lesson[]>;
|
|
282
261
|
|
|
@@ -813,6 +792,10 @@ declare function formatLessonsCheck(lessons: ScoredLesson[]): string;
|
|
|
813
792
|
* @see {@link unloadEmbedding} for embedding model cleanup
|
|
814
793
|
* @module learning-agent
|
|
815
794
|
*/
|
|
816
|
-
|
|
795
|
+
/**
|
|
796
|
+
* Package version - must match package.json.
|
|
797
|
+
* Update this when releasing a new version.
|
|
798
|
+
*/
|
|
799
|
+
declare const VERSION = "0.2.3";
|
|
817
800
|
|
|
818
801
|
export { type ActionabilityResult, type Context, type CorrectionSignal, DB_PATH, type DetectedCorrection, type DetectedSelfCorrection, type DetectedTestFailure, type EditEntry, type EditHistory, LESSONS_PATH, type Lesson, LessonSchema, type LessonType, LessonTypeSchema, MODEL_FILENAME, MODEL_URI, type NoveltyOptions, type NoveltyResult, type ParseError, type PlanRetrievalResult, type ProposeResult, type RankedLesson, type ReadLessonsOptions, type ReadLessonsResult, type ScoredLesson, type SearchVectorOptions, type Severity, type Source, type SpecificityResult, type TestResult, type Tombstone, TombstoneSchema, VERSION, appendLesson, calculateScore, closeDb, confirmationBoost, cosineSimilarity, detectSelfCorrection, detectTestFailure, detectUserCorrection, embedText, embedTexts, formatLessonsCheck, generateId, getEmbedding, isActionable, isModelAvailable, isNovel, isSpecific, loadSessionLessons, rankLessons, readLessons, rebuildIndex, recencyBoost, resolveModel, retrieveForPlan, searchKeyword, searchVector, severityBoost, shouldPropose, unloadEmbedding };
|
package/dist/index.js
CHANGED
|
@@ -2,8 +2,8 @@ import { mkdir, appendFile, readFile } from 'fs/promises';
|
|
|
2
2
|
import { join, dirname } from 'path';
|
|
3
3
|
import { createHash } from 'crypto';
|
|
4
4
|
import { z } from 'zod';
|
|
5
|
+
import { createRequire } from 'module';
|
|
5
6
|
import { existsSync, mkdirSync, statSync } from 'fs';
|
|
6
|
-
import Database from 'better-sqlite3';
|
|
7
7
|
import { resolveModelFile, getLlama } from 'node-llama-cpp';
|
|
8
8
|
import { homedir } from 'os';
|
|
9
9
|
|
|
@@ -158,9 +158,45 @@ async function readLessons(repoRoot, options = {}) {
|
|
|
158
158
|
}
|
|
159
159
|
return { lessons: Array.from(lessons.values()), skippedCount };
|
|
160
160
|
}
|
|
161
|
-
var
|
|
161
|
+
var require2 = createRequire(import.meta.url);
|
|
162
|
+
var sqliteAvailable = null;
|
|
163
|
+
var sqliteWarningLogged = false;
|
|
164
|
+
var DatabaseConstructor = null;
|
|
165
|
+
function isSqliteAvailable() {
|
|
166
|
+
if (sqliteAvailable !== null) {
|
|
167
|
+
return sqliteAvailable;
|
|
168
|
+
}
|
|
169
|
+
try {
|
|
170
|
+
const module = require2("better-sqlite3");
|
|
171
|
+
const Constructor = module.default || module;
|
|
172
|
+
const testDb = new Constructor(":memory:");
|
|
173
|
+
testDb.close();
|
|
174
|
+
DatabaseConstructor = Constructor;
|
|
175
|
+
sqliteAvailable = true;
|
|
176
|
+
} catch {
|
|
177
|
+
sqliteAvailable = false;
|
|
178
|
+
if (!sqliteWarningLogged) {
|
|
179
|
+
console.warn("SQLite unavailable, running in JSONL-only mode");
|
|
180
|
+
sqliteWarningLogged = true;
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
return sqliteAvailable;
|
|
184
|
+
}
|
|
185
|
+
function logDegradationWarning() {
|
|
186
|
+
if (!sqliteAvailable && !sqliteWarningLogged) {
|
|
187
|
+
console.warn("SQLite unavailable, running in JSONL-only mode");
|
|
188
|
+
sqliteWarningLogged = true;
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
function getDatabaseConstructor() {
|
|
192
|
+
if (!isSqliteAvailable()) {
|
|
193
|
+
return null;
|
|
194
|
+
}
|
|
195
|
+
return DatabaseConstructor;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
// src/storage/sqlite/schema.ts
|
|
162
199
|
var SCHEMA_SQL = `
|
|
163
|
-
-- Main lessons table
|
|
164
200
|
CREATE TABLE IF NOT EXISTS lessons (
|
|
165
201
|
id TEXT PRIMARY KEY,
|
|
166
202
|
type TEXT NOT NULL,
|
|
@@ -180,7 +216,6 @@ var SCHEMA_SQL = `
|
|
|
180
216
|
last_retrieved TEXT,
|
|
181
217
|
embedding BLOB,
|
|
182
218
|
content_hash TEXT,
|
|
183
|
-
-- v0.2.2 fields
|
|
184
219
|
invalidated_at TEXT,
|
|
185
220
|
invalidation_reason TEXT,
|
|
186
221
|
citation_file TEXT,
|
|
@@ -190,29 +225,21 @@ var SCHEMA_SQL = `
|
|
|
190
225
|
compacted_at TEXT
|
|
191
226
|
);
|
|
192
227
|
|
|
193
|
-
-- FTS5 virtual table for full-text search
|
|
194
228
|
CREATE VIRTUAL TABLE IF NOT EXISTS lessons_fts USING fts5(
|
|
195
|
-
id,
|
|
196
|
-
|
|
197
|
-
insight,
|
|
198
|
-
tags,
|
|
199
|
-
content='lessons',
|
|
200
|
-
content_rowid='rowid'
|
|
229
|
+
id, trigger, insight, tags,
|
|
230
|
+
content='lessons', content_rowid='rowid'
|
|
201
231
|
);
|
|
202
232
|
|
|
203
|
-
-- Trigger to sync FTS on INSERT
|
|
204
233
|
CREATE TRIGGER IF NOT EXISTS lessons_ai AFTER INSERT ON lessons BEGIN
|
|
205
234
|
INSERT INTO lessons_fts(rowid, id, trigger, insight, tags)
|
|
206
235
|
VALUES (new.rowid, new.id, new.trigger, new.insight, new.tags);
|
|
207
236
|
END;
|
|
208
237
|
|
|
209
|
-
-- Trigger to sync FTS on DELETE
|
|
210
238
|
CREATE TRIGGER IF NOT EXISTS lessons_ad AFTER DELETE ON lessons BEGIN
|
|
211
239
|
INSERT INTO lessons_fts(lessons_fts, rowid, id, trigger, insight, tags)
|
|
212
240
|
VALUES ('delete', old.rowid, old.id, old.trigger, old.insight, old.tags);
|
|
213
241
|
END;
|
|
214
242
|
|
|
215
|
-
-- Trigger to sync FTS on UPDATE
|
|
216
243
|
CREATE TRIGGER IF NOT EXISTS lessons_au AFTER UPDATE ON lessons BEGIN
|
|
217
244
|
INSERT INTO lessons_fts(lessons_fts, rowid, id, trigger, insight, tags)
|
|
218
245
|
VALUES ('delete', old.rowid, old.id, old.trigger, old.insight, old.tags);
|
|
@@ -220,12 +247,10 @@ var SCHEMA_SQL = `
|
|
|
220
247
|
VALUES (new.rowid, new.id, new.trigger, new.insight, new.tags);
|
|
221
248
|
END;
|
|
222
249
|
|
|
223
|
-
-- Index for common queries
|
|
224
250
|
CREATE INDEX IF NOT EXISTS idx_lessons_created ON lessons(created);
|
|
225
251
|
CREATE INDEX IF NOT EXISTS idx_lessons_confirmed ON lessons(confirmed);
|
|
226
252
|
CREATE INDEX IF NOT EXISTS idx_lessons_severity ON lessons(severity);
|
|
227
253
|
|
|
228
|
-
-- Metadata table for sync tracking
|
|
229
254
|
CREATE TABLE IF NOT EXISTS metadata (
|
|
230
255
|
key TEXT PRIMARY KEY,
|
|
231
256
|
value TEXT NOT NULL
|
|
@@ -234,17 +259,35 @@ var SCHEMA_SQL = `
|
|
|
234
259
|
function createSchema(database) {
|
|
235
260
|
database.exec(SCHEMA_SQL);
|
|
236
261
|
}
|
|
262
|
+
|
|
263
|
+
// src/storage/sqlite/connection.ts
|
|
264
|
+
var DB_PATH = ".claude/.cache/lessons.sqlite";
|
|
237
265
|
var db = null;
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
const
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
266
|
+
var dbIsInMemory = false;
|
|
267
|
+
function openDb(repoRoot, options = {}) {
|
|
268
|
+
if (!isSqliteAvailable()) {
|
|
269
|
+
return null;
|
|
270
|
+
}
|
|
271
|
+
const { inMemory = false } = options;
|
|
272
|
+
if (db) {
|
|
273
|
+
if (inMemory !== dbIsInMemory) {
|
|
274
|
+
closeDb();
|
|
275
|
+
} else {
|
|
276
|
+
return db;
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
const Database = getDatabaseConstructor();
|
|
280
|
+
if (inMemory) {
|
|
281
|
+
db = new Database(":memory:");
|
|
282
|
+
dbIsInMemory = true;
|
|
283
|
+
} else {
|
|
284
|
+
const dbPath = join(repoRoot, DB_PATH);
|
|
285
|
+
const dir = dirname(dbPath);
|
|
286
|
+
mkdirSync(dir, { recursive: true });
|
|
287
|
+
db = new Database(dbPath);
|
|
288
|
+
dbIsInMemory = false;
|
|
289
|
+
db.pragma("journal_mode = WAL");
|
|
290
|
+
}
|
|
248
291
|
createSchema(db);
|
|
249
292
|
return db;
|
|
250
293
|
}
|
|
@@ -252,10 +295,18 @@ function closeDb() {
|
|
|
252
295
|
if (db) {
|
|
253
296
|
db.close();
|
|
254
297
|
db = null;
|
|
298
|
+
dbIsInMemory = false;
|
|
255
299
|
}
|
|
256
300
|
}
|
|
301
|
+
function contentHash(trigger, insight) {
|
|
302
|
+
return createHash("sha256").update(`${trigger} ${insight}`).digest("hex");
|
|
303
|
+
}
|
|
257
304
|
function getCachedEmbedding(repoRoot, lessonId, expectedHash) {
|
|
258
305
|
const database = openDb(repoRoot);
|
|
306
|
+
if (!database) {
|
|
307
|
+
logDegradationWarning();
|
|
308
|
+
return null;
|
|
309
|
+
}
|
|
259
310
|
const row = database.prepare("SELECT embedding, content_hash FROM lessons WHERE id = ?").get(lessonId);
|
|
260
311
|
if (!row || !row.embedding || !row.content_hash) {
|
|
261
312
|
return null;
|
|
@@ -272,60 +323,14 @@ function getCachedEmbedding(repoRoot, lessonId, expectedHash) {
|
|
|
272
323
|
}
|
|
273
324
|
function setCachedEmbedding(repoRoot, lessonId, embedding, hash) {
|
|
274
325
|
const database = openDb(repoRoot);
|
|
326
|
+
if (!database) {
|
|
327
|
+
logDegradationWarning();
|
|
328
|
+
return;
|
|
329
|
+
}
|
|
275
330
|
const float32 = embedding instanceof Float32Array ? embedding : new Float32Array(embedding);
|
|
276
331
|
const buffer = Buffer.from(float32.buffer, float32.byteOffset, float32.byteLength);
|
|
277
332
|
database.prepare("UPDATE lessons SET embedding = ?, content_hash = ? WHERE id = ?").run(buffer, hash, lessonId);
|
|
278
333
|
}
|
|
279
|
-
function rowToLesson(row) {
|
|
280
|
-
const lesson = {
|
|
281
|
-
id: row.id,
|
|
282
|
-
type: row.type,
|
|
283
|
-
trigger: row.trigger,
|
|
284
|
-
insight: row.insight,
|
|
285
|
-
tags: row.tags ? row.tags.split(",").filter(Boolean) : [],
|
|
286
|
-
source: row.source,
|
|
287
|
-
context: JSON.parse(row.context),
|
|
288
|
-
supersedes: JSON.parse(row.supersedes),
|
|
289
|
-
related: JSON.parse(row.related),
|
|
290
|
-
created: row.created,
|
|
291
|
-
confirmed: row.confirmed === 1
|
|
292
|
-
};
|
|
293
|
-
if (row.evidence !== null) {
|
|
294
|
-
lesson.evidence = row.evidence;
|
|
295
|
-
}
|
|
296
|
-
if (row.severity !== null) {
|
|
297
|
-
lesson.severity = row.severity;
|
|
298
|
-
}
|
|
299
|
-
if (row.deleted === 1) {
|
|
300
|
-
lesson.deleted = true;
|
|
301
|
-
}
|
|
302
|
-
if (row.retrieval_count > 0) {
|
|
303
|
-
lesson.retrievalCount = row.retrieval_count;
|
|
304
|
-
}
|
|
305
|
-
if (row.invalidated_at !== null) {
|
|
306
|
-
lesson.invalidatedAt = row.invalidated_at;
|
|
307
|
-
}
|
|
308
|
-
if (row.invalidation_reason !== null) {
|
|
309
|
-
lesson.invalidationReason = row.invalidation_reason;
|
|
310
|
-
}
|
|
311
|
-
if (row.citation_file !== null) {
|
|
312
|
-
lesson.citation = {
|
|
313
|
-
file: row.citation_file,
|
|
314
|
-
...row.citation_line !== null && { line: row.citation_line },
|
|
315
|
-
...row.citation_commit !== null && { commit: row.citation_commit }
|
|
316
|
-
};
|
|
317
|
-
}
|
|
318
|
-
if (row.compaction_level !== null && row.compaction_level !== 0) {
|
|
319
|
-
lesson.compactionLevel = row.compaction_level;
|
|
320
|
-
}
|
|
321
|
-
if (row.compacted_at !== null) {
|
|
322
|
-
lesson.compactedAt = row.compacted_at;
|
|
323
|
-
}
|
|
324
|
-
if (row.last_retrieved !== null) {
|
|
325
|
-
lesson.lastRetrieved = row.last_retrieved;
|
|
326
|
-
}
|
|
327
|
-
return lesson;
|
|
328
|
-
}
|
|
329
334
|
function collectCachedEmbeddings(database) {
|
|
330
335
|
const cache = /* @__PURE__ */ new Map();
|
|
331
336
|
const rows = database.prepare("SELECT id, embedding, content_hash FROM lessons WHERE embedding IS NOT NULL").all();
|
|
@@ -358,6 +363,10 @@ function setLastSyncMtime(database, mtime) {
|
|
|
358
363
|
}
|
|
359
364
|
async function rebuildIndex(repoRoot) {
|
|
360
365
|
const database = openDb(repoRoot);
|
|
366
|
+
if (!database) {
|
|
367
|
+
logDegradationWarning();
|
|
368
|
+
return;
|
|
369
|
+
}
|
|
361
370
|
const { lessons } = await readLessons(repoRoot);
|
|
362
371
|
const cachedEmbeddings = collectCachedEmbeddings(database);
|
|
363
372
|
database.exec("DELETE FROM lessons");
|
|
@@ -393,7 +402,6 @@ async function rebuildIndex(repoRoot) {
|
|
|
393
402
|
last_retrieved: lesson.lastRetrieved ?? null,
|
|
394
403
|
embedding: hasValidCache ? cached.embedding : null,
|
|
395
404
|
content_hash: hasValidCache ? cached.contentHash : null,
|
|
396
|
-
// v0.2.2 fields
|
|
397
405
|
invalidated_at: lesson.invalidatedAt ?? null,
|
|
398
406
|
invalidation_reason: lesson.invalidationReason ?? null,
|
|
399
407
|
citation_file: lesson.citation?.file ?? null,
|
|
@@ -411,12 +419,17 @@ async function rebuildIndex(repoRoot) {
|
|
|
411
419
|
}
|
|
412
420
|
}
|
|
413
421
|
async function syncIfNeeded(repoRoot, options = {}) {
|
|
422
|
+
if (!isSqliteAvailable()) {
|
|
423
|
+
logDegradationWarning();
|
|
424
|
+
return false;
|
|
425
|
+
}
|
|
414
426
|
const { force = false } = options;
|
|
415
427
|
const jsonlMtime = getJsonlMtime(repoRoot);
|
|
416
428
|
if (jsonlMtime === null && !force) {
|
|
417
429
|
return false;
|
|
418
430
|
}
|
|
419
431
|
const database = openDb(repoRoot);
|
|
432
|
+
if (!database) return false;
|
|
420
433
|
const lastSyncMtime = getLastSyncMtime(database);
|
|
421
434
|
const needsRebuild = force || lastSyncMtime === null || jsonlMtime !== null && jsonlMtime > lastSyncMtime;
|
|
422
435
|
if (needsRebuild) {
|
|
@@ -425,8 +438,70 @@ async function syncIfNeeded(repoRoot, options = {}) {
|
|
|
425
438
|
}
|
|
426
439
|
return false;
|
|
427
440
|
}
|
|
441
|
+
|
|
442
|
+
// src/storage/sqlite/search.ts
|
|
443
|
+
function rowToLesson(row) {
|
|
444
|
+
const lesson = {
|
|
445
|
+
id: row.id,
|
|
446
|
+
type: row.type,
|
|
447
|
+
trigger: row.trigger,
|
|
448
|
+
insight: row.insight,
|
|
449
|
+
tags: row.tags ? row.tags.split(",").filter(Boolean) : [],
|
|
450
|
+
source: row.source,
|
|
451
|
+
context: JSON.parse(row.context),
|
|
452
|
+
supersedes: JSON.parse(row.supersedes),
|
|
453
|
+
related: JSON.parse(row.related),
|
|
454
|
+
created: row.created,
|
|
455
|
+
confirmed: row.confirmed === 1
|
|
456
|
+
};
|
|
457
|
+
if (row.evidence !== null) lesson.evidence = row.evidence;
|
|
458
|
+
if (row.severity !== null) lesson.severity = row.severity;
|
|
459
|
+
if (row.deleted === 1) lesson.deleted = true;
|
|
460
|
+
if (row.retrieval_count > 0) lesson.retrievalCount = row.retrieval_count;
|
|
461
|
+
if (row.invalidated_at !== null) lesson.invalidatedAt = row.invalidated_at;
|
|
462
|
+
if (row.invalidation_reason !== null) lesson.invalidationReason = row.invalidation_reason;
|
|
463
|
+
if (row.citation_file !== null) {
|
|
464
|
+
lesson.citation = {
|
|
465
|
+
file: row.citation_file,
|
|
466
|
+
...row.citation_line !== null && { line: row.citation_line },
|
|
467
|
+
...row.citation_commit !== null && { commit: row.citation_commit }
|
|
468
|
+
};
|
|
469
|
+
}
|
|
470
|
+
if (row.compaction_level !== null && row.compaction_level !== 0) {
|
|
471
|
+
lesson.compactionLevel = row.compaction_level;
|
|
472
|
+
}
|
|
473
|
+
if (row.compacted_at !== null) lesson.compactedAt = row.compacted_at;
|
|
474
|
+
if (row.last_retrieved !== null) lesson.lastRetrieved = row.last_retrieved;
|
|
475
|
+
return lesson;
|
|
476
|
+
}
|
|
477
|
+
function incrementRetrievalCount(repoRoot, lessonIds) {
|
|
478
|
+
if (lessonIds.length === 0) return;
|
|
479
|
+
const database = openDb(repoRoot);
|
|
480
|
+
if (!database) {
|
|
481
|
+
logDegradationWarning();
|
|
482
|
+
return;
|
|
483
|
+
}
|
|
484
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
485
|
+
const update = database.prepare(`
|
|
486
|
+
UPDATE lessons
|
|
487
|
+
SET retrieval_count = retrieval_count + 1,
|
|
488
|
+
last_retrieved = ?
|
|
489
|
+
WHERE id = ?
|
|
490
|
+
`);
|
|
491
|
+
const updateMany = database.transaction((ids) => {
|
|
492
|
+
for (const id of ids) {
|
|
493
|
+
update.run(now, id);
|
|
494
|
+
}
|
|
495
|
+
});
|
|
496
|
+
updateMany(lessonIds);
|
|
497
|
+
}
|
|
428
498
|
async function searchKeyword(repoRoot, query, limit) {
|
|
429
499
|
const database = openDb(repoRoot);
|
|
500
|
+
if (!database) {
|
|
501
|
+
throw new Error(
|
|
502
|
+
"Keyword search requires SQLite (FTS5 required). Install native build tools or use vector search instead."
|
|
503
|
+
);
|
|
504
|
+
}
|
|
430
505
|
const countResult = database.prepare("SELECT COUNT(*) as cnt FROM lessons").get();
|
|
431
506
|
if (countResult.cnt === 0) return [];
|
|
432
507
|
const rows = database.prepare(
|
|
@@ -444,22 +519,13 @@ async function searchKeyword(repoRoot, query, limit) {
|
|
|
444
519
|
}
|
|
445
520
|
return rows.map(rowToLesson);
|
|
446
521
|
}
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
const
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
last_retrieved = ?
|
|
455
|
-
WHERE id = ?
|
|
456
|
-
`);
|
|
457
|
-
const updateMany = database.transaction((ids) => {
|
|
458
|
-
for (const id of ids) {
|
|
459
|
-
update.run(now, id);
|
|
460
|
-
}
|
|
461
|
-
});
|
|
462
|
-
updateMany(lessonIds);
|
|
522
|
+
|
|
523
|
+
// src/utils.ts
|
|
524
|
+
var MS_PER_DAY = 24 * 60 * 60 * 1e3;
|
|
525
|
+
function getLessonAgeDays(lesson) {
|
|
526
|
+
const created = new Date(lesson.created).getTime();
|
|
527
|
+
const now = Date.now();
|
|
528
|
+
return Math.floor((now - created) / MS_PER_DAY);
|
|
463
529
|
}
|
|
464
530
|
var MODEL_URI = "hf:ggml-org/embeddinggemma-300M-qat-q4_0-GGUF/embeddinggemma-300M-qat-Q4_0.gguf";
|
|
465
531
|
var MODEL_FILENAME = "hf_ggml-org_embeddinggemma-300M-qat-Q4_0.gguf";
|
|
@@ -544,14 +610,6 @@ async function searchVector(repoRoot, query, options) {
|
|
|
544
610
|
return scored.slice(0, limit);
|
|
545
611
|
}
|
|
546
612
|
|
|
547
|
-
// src/utils.ts
|
|
548
|
-
var MS_PER_DAY = 24 * 60 * 60 * 1e3;
|
|
549
|
-
function getLessonAgeDays(lesson) {
|
|
550
|
-
const created = new Date(lesson.created).getTime();
|
|
551
|
-
const now = Date.now();
|
|
552
|
-
return Math.floor((now - created) / MS_PER_DAY);
|
|
553
|
-
}
|
|
554
|
-
|
|
555
613
|
// src/search/ranking.ts
|
|
556
614
|
var RECENCY_THRESHOLD_DAYS = 30;
|
|
557
615
|
var HIGH_SEVERITY_BOOST = 1.5;
|
|
@@ -783,7 +841,7 @@ async function retrieveForPlan(repoRoot, planText, limit = DEFAULT_LIMIT3) {
|
|
|
783
841
|
return { lessons: topLessons, message };
|
|
784
842
|
}
|
|
785
843
|
function formatLessonsCheck(lessons) {
|
|
786
|
-
const header = "
|
|
844
|
+
const header = "Lessons Check\n" + "\u2500".repeat(40);
|
|
787
845
|
if (lessons.length === 0) {
|
|
788
846
|
return `${header}
|
|
789
847
|
No relevant lessons found for this plan.`;
|
|
@@ -798,7 +856,7 @@ ${lessonLines.join("\n")}`;
|
|
|
798
856
|
}
|
|
799
857
|
|
|
800
858
|
// src/index.ts
|
|
801
|
-
var VERSION = "0.
|
|
859
|
+
var VERSION = "0.2.3";
|
|
802
860
|
|
|
803
861
|
export { DB_PATH, LESSONS_PATH, LessonSchema, LessonTypeSchema, MODEL_FILENAME, MODEL_URI, TombstoneSchema, VERSION, appendLesson, calculateScore, closeDb, confirmationBoost, cosineSimilarity, detectSelfCorrection, detectTestFailure, detectUserCorrection, embedText, embedTexts, formatLessonsCheck, generateId, getEmbedding, isActionable, isModelAvailable, isNovel, isSpecific, loadSessionLessons, rankLessons, readLessons, rebuildIndex, recencyBoost, resolveModel, retrieveForPlan, searchKeyword, searchVector, severityBoost, shouldPropose, unloadEmbedding };
|
|
804
862
|
//# sourceMappingURL=index.js.map
|