learning-agent 0.2.1 → 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 +92 -1
- package/README.md +109 -81
- package/dist/cli.js +2026 -1466
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +66 -45
- package/dist/index.js +214 -83
- package/dist/index.js.map +1 -1
- package/package.json +21 -11
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/types.ts","../src/storage/jsonl.ts","../src/storage/sqlite.ts","../src/embeddings/model.ts","../src/embeddings/nomic.ts","../src/search/vector.ts","../src/search/ranking.ts","../src/capture/quality.ts","../src/capture/triggers.ts","../src/retrieval/session.ts","../src/retrieval/plan.ts","../src/index.ts"],"names":["createHash","join","dirname","mtime","DEFAULT_LIMIT"],"mappings":"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","file":"index.js","sourcesContent":["/**\n * Lesson type definitions using Zod schemas\n */\n\nimport { createHash } from 'node:crypto';\nimport { z } from 'zod';\n\n// Source of lesson capture\nexport const SourceSchema = z.enum([\n 'user_correction',\n 'self_correction',\n 'test_failure',\n 'manual',\n]);\n\n// Context about when lesson was learned\nexport const ContextSchema = z.object({\n tool: z.string(),\n intent: z.string(),\n});\n\n// Code pattern (bad -> good)\nexport const PatternSchema = z.object({\n bad: z.string(),\n good: z.string(),\n});\n\n// Severity levels for lessons\nexport const SeveritySchema = z.enum(['high', 'medium', 'low']);\n\n// Lesson type - semantic marker for lesson quality tier\nexport const LessonTypeSchema = z.enum(['quick', 'full']);\n\n/**\n * Unified Lesson schema.\n *\n * The `type` field is a semantic marker:\n * - 'quick': Minimal lesson for fast capture\n * - 'full': Important lesson (typically has evidence/severity)\n *\n * All fields except core identity are optional for flexibility.\n * Semantic meaning is preserved through convention, not schema enforcement.\n */\nexport const LessonSchema = z.object({\n // Core identity (required)\n id: z.string(),\n type: LessonTypeSchema,\n trigger: z.string(),\n insight: z.string(),\n\n // Metadata (required)\n tags: z.array(z.string()),\n source: SourceSchema,\n context: ContextSchema,\n created: z.string(), // ISO8601\n confirmed: z.boolean(),\n\n // Relationships (required, can be empty arrays)\n supersedes: z.array(z.string()),\n related: z.array(z.string()),\n\n // Extended fields (optional - typically present for 'full' type)\n evidence: z.string().optional(),\n severity: SeveritySchema.optional(),\n pattern: PatternSchema.optional(),\n\n // Lifecycle fields (optional)\n deleted: z.boolean().optional(),\n retrievalCount: z.number().optional(),\n});\n\n// Tombstone for deletions (append-only delete marker)\nexport const TombstoneSchema = z.object({\n id: z.string(),\n deleted: z.literal(true),\n deletedAt: z.string(), // ISO8601\n});\n\n// Type exports\nexport type Lesson = z.infer<typeof LessonSchema>;\nexport type LessonType = z.infer<typeof LessonTypeSchema>;\nexport type Tombstone = z.infer<typeof TombstoneSchema>;\nexport type Source = z.infer<typeof SourceSchema>;\nexport type Severity = z.infer<typeof SeveritySchema>;\nexport type Context = z.infer<typeof ContextSchema>;\nexport type Pattern = z.infer<typeof PatternSchema>;\n\n/**\n * Generate deterministic lesson ID from insight text.\n * Format: L + 8 hex characters from SHA-256 hash\n */\nexport function generateId(insight: string): string {\n const hash = createHash('sha256').update(insight).digest('hex');\n return `L${hash.slice(0, 8)}`;\n}\n","/**\n * JSONL storage layer for lessons\n *\n * Append-only storage with last-write-wins deduplication.\n * Source of truth - git trackable.\n */\n\nimport { appendFile, mkdir, readFile } from 'node:fs/promises';\nimport { dirname, join } from 'node:path';\nimport { LessonSchema, type Lesson } from '../types.js';\n\n/** Relative path to lessons file from repo root */\nexport const LESSONS_PATH = '.claude/lessons/index.jsonl';\n\n/** Options for reading lessons */\nexport interface ReadLessonsOptions {\n /** If true, throw on first parse error. Default: false (skip errors) */\n strict?: boolean;\n /** Callback for each parse error in non-strict mode */\n onParseError?: (error: ParseError) => void;\n}\n\n/** Parse error details */\nexport interface ParseError {\n /** 1-based line number */\n line: number;\n /** Error message */\n message: string;\n /** Original error */\n cause: unknown;\n}\n\n/** Result of reading lessons */\nexport interface ReadLessonsResult {\n /** Successfully parsed lessons */\n lessons: Lesson[];\n /** Number of lines skipped due to errors */\n skippedCount: number;\n}\n\n/**\n * Append a lesson to the JSONL file.\n * Creates directory structure if missing.\n */\nexport async function appendLesson(repoRoot: string, lesson: Lesson): Promise<void> {\n const filePath = join(repoRoot, LESSONS_PATH);\n await mkdir(dirname(filePath), { recursive: true });\n\n const line = JSON.stringify(lesson) + '\\n';\n await appendFile(filePath, line, 'utf-8');\n}\n\n/**\n * Parse and validate a single JSON line.\n * @returns Parsed lesson or null if invalid\n */\nfunction parseJsonLine(\n line: string,\n lineNumber: number,\n strict: boolean,\n onParseError?: (error: ParseError) => void\n): Lesson | null {\n // Try to parse JSON\n let parsed: unknown;\n try {\n parsed = JSON.parse(line);\n } catch (err) {\n const parseError: ParseError = {\n line: lineNumber,\n message: `Invalid JSON: ${(err as Error).message}`,\n cause: err,\n };\n if (strict) {\n throw new Error(`Parse error on line ${lineNumber}: ${parseError.message}`);\n }\n onParseError?.(parseError);\n return null;\n }\n\n // Validate against schema\n const result = LessonSchema.safeParse(parsed);\n if (!result.success) {\n const parseError: ParseError = {\n line: lineNumber,\n message: `Schema validation failed: ${result.error.message}`,\n cause: result.error,\n };\n if (strict) {\n throw new Error(`Parse error on line ${lineNumber}: ${parseError.message}`);\n }\n onParseError?.(parseError);\n return null;\n }\n\n return result.data;\n}\n\n/**\n * Read all non-deleted lessons from the JSONL file.\n * Applies last-write-wins deduplication by ID.\n * Returns result object with lessons and skippedCount.\n *\n * @param repoRoot - Repository root directory\n * @param options - Optional settings for error handling\n * @returns Result with lessons array and count of skipped lines\n */\nexport async function readLessons(\n repoRoot: string,\n options: ReadLessonsOptions = {}\n): Promise<ReadLessonsResult> {\n const { strict = false, onParseError } = options;\n const filePath = join(repoRoot, LESSONS_PATH);\n\n let content: string;\n try {\n content = await readFile(filePath, 'utf-8');\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code === 'ENOENT') {\n return { lessons: [], skippedCount: 0 };\n }\n throw err;\n }\n\n const lessons = new Map<string, Lesson>();\n let skippedCount = 0;\n\n const lines = content.split('\\n');\n for (let i = 0; i < lines.length; i++) {\n const trimmed = lines[i]!.trim();\n if (!trimmed) continue;\n\n const lesson = parseJsonLine(trimmed, i + 1, strict, onParseError);\n if (!lesson) {\n skippedCount++;\n continue;\n }\n\n if (lesson.deleted) {\n lessons.delete(lesson.id);\n } else {\n lessons.set(lesson.id, lesson);\n }\n }\n\n return { lessons: Array.from(lessons.values()), skippedCount };\n}\n","/**\n * SQLite storage layer with FTS5 for full-text search\n *\n * Rebuildable index - not the source of truth.\n * Stored in .claude/.cache (gitignored).\n */\n\nimport { createHash } from 'node:crypto';\nimport { mkdirSync, statSync } from 'node:fs';\nimport { dirname, join } from 'node:path';\nimport Database from 'better-sqlite3';\nimport type { Database as DatabaseType } from 'better-sqlite3';\n\nimport type { Lesson } from '../types.js';\n\nimport { LESSONS_PATH, readLessons } from './jsonl.js';\n\n/** Relative path to database file from repo root */\nexport const DB_PATH = '.claude/.cache/lessons.sqlite';\n\n/** SQL schema for lessons database */\nconst SCHEMA_SQL = `\n -- Main lessons table\n CREATE TABLE IF NOT EXISTS lessons (\n id TEXT PRIMARY KEY,\n type TEXT NOT NULL,\n trigger TEXT NOT NULL,\n insight TEXT NOT NULL,\n evidence TEXT,\n severity TEXT,\n tags TEXT NOT NULL DEFAULT '',\n source TEXT NOT NULL,\n context TEXT NOT NULL DEFAULT '{}',\n supersedes TEXT NOT NULL DEFAULT '[]',\n related TEXT NOT NULL DEFAULT '[]',\n created TEXT NOT NULL,\n confirmed INTEGER NOT NULL DEFAULT 0,\n deleted INTEGER NOT NULL DEFAULT 0,\n retrieval_count INTEGER NOT NULL DEFAULT 0,\n last_retrieved TEXT,\n embedding BLOB,\n content_hash TEXT\n );\n\n -- FTS5 virtual table for full-text search\n CREATE VIRTUAL TABLE IF NOT EXISTS lessons_fts USING fts5(\n id,\n trigger,\n insight,\n tags,\n content='lessons',\n content_rowid='rowid'\n );\n\n -- Trigger to sync FTS on INSERT\n CREATE TRIGGER IF NOT EXISTS lessons_ai AFTER INSERT ON lessons BEGIN\n INSERT INTO lessons_fts(rowid, id, trigger, insight, tags)\n VALUES (new.rowid, new.id, new.trigger, new.insight, new.tags);\n END;\n\n -- Trigger to sync FTS on DELETE\n CREATE TRIGGER IF NOT EXISTS lessons_ad AFTER DELETE ON lessons BEGIN\n INSERT INTO lessons_fts(lessons_fts, rowid, id, trigger, insight, tags)\n VALUES ('delete', old.rowid, old.id, old.trigger, old.insight, old.tags);\n END;\n\n -- Trigger to sync FTS on UPDATE\n CREATE TRIGGER IF NOT EXISTS lessons_au AFTER UPDATE ON lessons BEGIN\n INSERT INTO lessons_fts(lessons_fts, rowid, id, trigger, insight, tags)\n VALUES ('delete', old.rowid, old.id, old.trigger, old.insight, old.tags);\n INSERT INTO lessons_fts(rowid, id, trigger, insight, tags)\n VALUES (new.rowid, new.id, new.trigger, new.insight, new.tags);\n END;\n\n -- Index for common queries\n CREATE INDEX IF NOT EXISTS idx_lessons_created ON lessons(created);\n CREATE INDEX IF NOT EXISTS idx_lessons_confirmed ON lessons(confirmed);\n CREATE INDEX IF NOT EXISTS idx_lessons_severity ON lessons(severity);\n\n -- Metadata table for sync tracking\n CREATE TABLE IF NOT EXISTS metadata (\n key TEXT PRIMARY KEY,\n value TEXT NOT NULL\n );\n`;\n\n/**\n * Create database schema for lessons storage.\n */\nfunction createSchema(database: DatabaseType): void {\n database.exec(SCHEMA_SQL);\n}\n\nlet db: DatabaseType | null = null;\n\n/**\n * Compute deterministic content hash for embedding cache validation.\n * Format: SHA-256 hex of \"trigger insight\"\n */\nexport function contentHash(trigger: string, insight: string): string {\n return createHash('sha256').update(`${trigger} ${insight}`).digest('hex');\n}\n\n/**\n * Open or create the SQLite database.\n *\n * Creates directory structure and schema if needed.\n * Returns a singleton instance - subsequent calls return the same connection.\n *\n * **Resource lifecycle:**\n * - First call creates the database file (if needed) and opens a connection\n * - Connection uses WAL mode for better concurrent access\n * - Connection remains open until `closeDb()` is called\n *\n * **Note:** Most code should not call this directly. Higher-level functions\n * like `searchKeyword` and `rebuildIndex` call it internally.\n *\n * @param repoRoot - Path to repository root (database stored at `.claude/.cache/lessons.sqlite`)\n * @returns The singleton database connection\n *\n * @see {@link closeDb} for releasing resources\n */\nexport function openDb(repoRoot: string): DatabaseType {\n if (db) return db;\n\n const dbPath = join(repoRoot, DB_PATH);\n\n // Create directory synchronously (better-sqlite3 is sync)\n const dir = dirname(dbPath);\n mkdirSync(dir, { recursive: true });\n\n db = new Database(dbPath);\n\n // Enable WAL mode for better concurrent access\n db.pragma('journal_mode = WAL');\n\n createSchema(db);\n\n return db;\n}\n\n/**\n * Close the database connection and release resources.\n *\n * **Resource lifecycle:**\n * - The database is opened lazily on first call to `openDb()` or any function that uses it\n * (e.g., `searchKeyword`, `rebuildIndex`, `syncIfNeeded`, `getCachedEmbedding`)\n * - Once opened, the connection remains active until `closeDb()` is called\n * - After closing, subsequent database operations will reopen the connection\n *\n * **When to call:**\n * - At the end of CLI commands to ensure clean process exit\n * - When transitioning between repositories in long-running processes\n * - Before process exit in graceful shutdown handlers\n *\n * **Best practices for long-running processes:**\n * - In single-operation scripts: call before exit\n * - In daemon/server processes: call in shutdown handler\n * - Not necessary to call between operations in the same repository\n *\n * @example\n * ```typescript\n * // CLI command pattern\n * try {\n * await searchKeyword(repoRoot, 'typescript', 10);\n * // ... process results\n * } finally {\n * closeDb();\n * }\n *\n * // Graceful shutdown pattern\n * process.on('SIGTERM', () => {\n * closeDb();\n * process.exit(0);\n * });\n * ```\n */\nexport function closeDb(): void {\n if (db) {\n db.close();\n db = null;\n }\n}\n\n/**\n * Get cached embedding for a lesson if content hash matches.\n * Returns null if no cache exists or hash mismatches.\n */\nexport function getCachedEmbedding(\n repoRoot: string,\n lessonId: string,\n expectedHash?: string\n): number[] | null {\n const database = openDb(repoRoot);\n const row = database\n .prepare('SELECT embedding, content_hash FROM lessons WHERE id = ?')\n .get(lessonId) as { embedding: Buffer | null; content_hash: string | null } | undefined;\n\n if (!row || !row.embedding || !row.content_hash) {\n return null;\n }\n\n // If expected hash provided, validate it matches\n if (expectedHash && row.content_hash !== expectedHash) {\n return null;\n }\n\n // Convert Buffer to Float32Array then to number[]\n const float32 = new Float32Array(\n row.embedding.buffer,\n row.embedding.byteOffset,\n row.embedding.byteLength / 4\n );\n return Array.from(float32);\n}\n\n/**\n * Cache an embedding for a lesson with content hash.\n */\nexport function setCachedEmbedding(\n repoRoot: string,\n lessonId: string,\n embedding: Float32Array | number[],\n hash: string\n): void {\n const database = openDb(repoRoot);\n\n // Convert to Buffer for storage\n const float32 = embedding instanceof Float32Array ? embedding : new Float32Array(embedding);\n const buffer = Buffer.from(float32.buffer, float32.byteOffset, float32.byteLength);\n\n database\n .prepare('UPDATE lessons SET embedding = ?, content_hash = ? WHERE id = ?')\n .run(buffer, hash, lessonId);\n}\n\n/** DB row type for lessons table */\ninterface LessonRow {\n id: string;\n type: string;\n trigger: string;\n insight: string;\n evidence: string | null;\n severity: string | null;\n tags: string;\n source: string;\n context: string;\n supersedes: string;\n related: string;\n created: string;\n confirmed: number;\n deleted: number;\n retrieval_count: number;\n last_retrieved: string | null;\n embedding: Buffer | null;\n}\n\n/**\n * Convert a database row to a typed Lesson object.\n * Maps NULL to undefined for optional fields (lossless roundtrip).\n */\nfunction rowToLesson(row: LessonRow): Lesson {\n const lesson: Lesson = {\n id: row.id,\n type: row.type as 'quick' | 'full',\n trigger: row.trigger,\n insight: row.insight,\n tags: row.tags ? row.tags.split(',').filter(Boolean) : [],\n source: row.source as Lesson['source'],\n context: JSON.parse(row.context) as Lesson['context'],\n supersedes: JSON.parse(row.supersedes) as string[],\n related: JSON.parse(row.related) as string[],\n created: row.created,\n confirmed: row.confirmed === 1,\n };\n\n // Optional fields: map NULL -> undefined (lossless roundtrip)\n if (row.evidence !== null) {\n lesson.evidence = row.evidence;\n }\n if (row.severity !== null) {\n lesson.severity = row.severity as 'high' | 'medium' | 'low';\n }\n if (row.deleted === 1) {\n lesson.deleted = true;\n }\n if (row.retrieval_count > 0) {\n lesson.retrievalCount = row.retrieval_count;\n }\n\n return lesson;\n}\n\n/** Cached embedding with its content hash */\ninterface CachedEmbeddingData {\n embedding: Buffer;\n contentHash: string;\n}\n\n/**\n * Collect cached embeddings from existing lessons for preservation.\n */\nfunction collectCachedEmbeddings(database: DatabaseType): Map<string, CachedEmbeddingData> {\n const cache = new Map<string, CachedEmbeddingData>();\n const rows = database\n .prepare('SELECT id, embedding, content_hash FROM lessons WHERE embedding IS NOT NULL')\n .all() as Array<{ id: string; embedding: Buffer; content_hash: string | null }>;\n\n for (const row of rows) {\n if (row.embedding && row.content_hash) {\n cache.set(row.id, { embedding: row.embedding, contentHash: row.content_hash });\n }\n }\n return cache;\n}\n\n/** SQL for inserting a lesson row */\nconst INSERT_LESSON_SQL = `\n INSERT INTO lessons (id, type, trigger, insight, evidence, severity, tags, source, context, supersedes, related, created, confirmed, deleted, retrieval_count, last_retrieved, embedding, content_hash)\n VALUES (@id, @type, @trigger, @insight, @evidence, @severity, @tags, @source, @context, @supersedes, @related, @created, @confirmed, @deleted, @retrieval_count, @last_retrieved, @embedding, @content_hash)\n`;\n\n/**\n * Get the mtime of the JSONL file, or null if it doesn't exist.\n */\nfunction getJsonlMtime(repoRoot: string): number | null {\n const jsonlPath = join(repoRoot, LESSONS_PATH);\n try {\n const stat = statSync(jsonlPath);\n return stat.mtimeMs;\n } catch {\n return null;\n }\n}\n\n/**\n * Get the last synced mtime from metadata table.\n */\nfunction getLastSyncMtime(database: DatabaseType): number | null {\n const row = database\n .prepare('SELECT value FROM metadata WHERE key = ?')\n .get('last_sync_mtime') as { value: string } | undefined;\n return row ? parseFloat(row.value) : null;\n}\n\n/**\n * Store the last synced mtime in metadata table.\n */\nfunction setLastSyncMtime(database: DatabaseType, mtime: number): void {\n database\n .prepare('INSERT OR REPLACE INTO metadata (key, value) VALUES (?, ?)')\n .run('last_sync_mtime', mtime.toString());\n}\n\n/**\n * Rebuild the SQLite index from the JSONL source of truth.\n * Preserves embeddings where content hash is unchanged.\n * Updates the last sync mtime after successful rebuild.\n */\nexport async function rebuildIndex(repoRoot: string): Promise<void> {\n const database = openDb(repoRoot);\n const { lessons } = await readLessons(repoRoot);\n\n const cachedEmbeddings = collectCachedEmbeddings(database);\n database.exec('DELETE FROM lessons');\n\n if (lessons.length === 0) {\n // Still update mtime even for empty file\n const mtime = getJsonlMtime(repoRoot);\n if (mtime !== null) {\n setLastSyncMtime(database, mtime);\n }\n return;\n }\n\n const insert = database.prepare(INSERT_LESSON_SQL);\n const insertMany = database.transaction((items: Lesson[]) => {\n for (const lesson of items) {\n const newHash = contentHash(lesson.trigger, lesson.insight);\n const cached = cachedEmbeddings.get(lesson.id);\n const hasValidCache = cached && cached.contentHash === newHash;\n\n insert.run({\n id: lesson.id,\n type: lesson.type,\n trigger: lesson.trigger,\n insight: lesson.insight,\n evidence: lesson.evidence ?? null,\n severity: lesson.severity ?? null,\n tags: lesson.tags.join(','),\n source: lesson.source,\n context: JSON.stringify(lesson.context),\n supersedes: JSON.stringify(lesson.supersedes),\n related: JSON.stringify(lesson.related),\n created: lesson.created,\n confirmed: lesson.confirmed ? 1 : 0,\n deleted: lesson.deleted ? 1 : 0,\n retrieval_count: lesson.retrievalCount ?? 0,\n last_retrieved: null, // Reset on rebuild since we're rebuilding from source\n embedding: hasValidCache ? cached.embedding : null,\n content_hash: hasValidCache ? cached.contentHash : null,\n });\n }\n });\n\n insertMany(lessons);\n\n // Update last sync mtime\n const mtime = getJsonlMtime(repoRoot);\n if (mtime !== null) {\n setLastSyncMtime(database, mtime);\n }\n}\n\n/** Options for syncIfNeeded */\nexport interface SyncOptions {\n /** Force rebuild even if JSONL unchanged */\n force?: boolean;\n}\n\n/**\n * Sync the index if JSONL has changed since last sync.\n * Returns true if a rebuild was performed, false if skipped.\n */\nexport async function syncIfNeeded(\n repoRoot: string,\n options: SyncOptions = {}\n): Promise<boolean> {\n const { force = false } = options;\n\n // Check JSONL mtime\n const jsonlMtime = getJsonlMtime(repoRoot);\n if (jsonlMtime === null && !force) {\n // No JSONL file exists\n return false;\n }\n\n const database = openDb(repoRoot);\n const lastSyncMtime = getLastSyncMtime(database);\n\n // Rebuild if forced, no previous sync, or JSONL is newer\n const needsRebuild = force || lastSyncMtime === null || (jsonlMtime !== null && jsonlMtime > lastSyncMtime);\n\n if (needsRebuild) {\n await rebuildIndex(repoRoot);\n return true;\n }\n\n return false;\n}\n\n/**\n * Search lessons using FTS5 keyword search.\n * Returns matching lessons up to the specified limit.\n * Increments retrieval count for all returned lessons.\n */\nexport async function searchKeyword(\n repoRoot: string,\n query: string,\n limit: number\n): Promise<Lesson[]> {\n const database = openDb(repoRoot);\n\n // Check if there are any lessons\n const countResult = database.prepare('SELECT COUNT(*) as cnt FROM lessons').get() as {\n cnt: number;\n };\n if (countResult.cnt === 0) return [];\n\n // Use FTS5 MATCH for search\n const rows = database\n .prepare(\n `\n SELECT l.*\n FROM lessons l\n JOIN lessons_fts fts ON l.rowid = fts.rowid\n WHERE lessons_fts MATCH ?\n LIMIT ?\n `\n )\n .all(query, limit) as LessonRow[];\n\n // Increment retrieval count for matched lessons\n if (rows.length > 0) {\n incrementRetrievalCount(repoRoot, rows.map((r) => r.id));\n }\n\n return rows.map(rowToLesson);\n}\n\n/** Retrieval statistics for a lesson */\nexport interface RetrievalStat {\n id: string;\n count: number;\n lastRetrieved: string | null;\n}\n\n/**\n * Increment retrieval count for a list of lesson IDs.\n * Updates both count and last_retrieved timestamp.\n * Non-existent IDs are silently ignored.\n */\nexport function incrementRetrievalCount(repoRoot: string, lessonIds: string[]): void {\n if (lessonIds.length === 0) return;\n\n const database = openDb(repoRoot);\n const now = new Date().toISOString();\n\n const update = database.prepare(`\n UPDATE lessons\n SET retrieval_count = retrieval_count + 1,\n last_retrieved = ?\n WHERE id = ?\n `);\n\n const updateMany = database.transaction((ids: string[]) => {\n for (const id of ids) {\n update.run(now, id);\n }\n });\n\n updateMany(lessonIds);\n}\n\n/**\n * Get retrieval statistics for all lessons.\n * Returns id, retrieval count, and last retrieved timestamp for each lesson.\n */\nexport function getRetrievalStats(repoRoot: string): RetrievalStat[] {\n const database = openDb(repoRoot);\n\n const rows = database\n .prepare('SELECT id, retrieval_count, last_retrieved FROM lessons')\n .all() as Array<{ id: string; retrieval_count: number; last_retrieved: string | null }>;\n\n return rows.map((row) => ({\n id: row.id,\n count: row.retrieval_count,\n lastRetrieved: row.last_retrieved,\n }));\n}\n","/**\n * Embedding model resolution using node-llama-cpp's built-in resolver.\n *\n * Uses resolveModelFile for automatic download and caching.\n * Model is stored in ~/.node-llama-cpp/models/ by default.\n */\n\nimport { existsSync } from 'node:fs';\nimport { homedir } from 'node:os';\nimport { join } from 'node:path';\nimport { resolveModelFile } from 'node-llama-cpp';\n\n/**\n * HuggingFace URI for EmbeddingGemma-300M (Q4_0 quantization).\n *\n * - Size: ~278MB\n * - Dimensions: 768 (default), supports MRL truncation to 512/256/128\n * - Context: 2048 tokens\n */\nexport const MODEL_URI = 'hf:ggml-org/embeddinggemma-300M-qat-q4_0-GGUF/embeddinggemma-300M-qat-Q4_0.gguf';\n\n/**\n * Expected model filename after download.\n * node-llama-cpp uses format: hf_{org}_{filename}\n */\nexport const MODEL_FILENAME = 'hf_ggml-org_embeddinggemma-300M-qat-Q4_0.gguf';\n\n/** Default model directory used by node-llama-cpp */\nconst DEFAULT_MODEL_DIR = join(homedir(), '.node-llama-cpp', 'models');\n\n/**\n * Check if the embedding model is available locally.\n *\n * @returns true if model file exists\n */\nexport function isModelAvailable(): boolean {\n return existsSync(join(DEFAULT_MODEL_DIR, MODEL_FILENAME));\n}\n\n/**\n * Resolve the embedding model path, downloading if necessary.\n *\n * Uses node-llama-cpp's resolveModelFile for automatic download with progress.\n *\n * @param options - Optional configuration\n * @param options.cli - Show download progress in console (default: true)\n * @returns Path to the resolved model file\n *\n * @example\n * ```typescript\n * const modelPath = await resolveModel();\n * const llama = await getLlama();\n * const model = await llama.loadModel({ modelPath });\n * ```\n */\nexport async function resolveModel(options: { cli?: boolean } = {}): Promise<string> {\n const { cli = true } = options;\n return resolveModelFile(MODEL_URI, { cli });\n}\n","/**\n * Text embedding via node-llama-cpp with EmbeddingGemma model\n *\n * **Resource lifecycle:**\n * - Model is loaded lazily on first embedding call (~150MB in memory)\n * - Once loaded, the model remains in memory until `unloadEmbedding()` is called\n * - Loading is slow (~1-3s); keeping loaded improves subsequent call performance\n *\n * **Memory usage:**\n * - Embedding model: ~150MB RAM when loaded\n * - Embeddings themselves: ~3KB per embedding (768 dimensions x 4 bytes)\n *\n * @see {@link unloadEmbedding} for releasing memory\n * @see {@link getEmbedding} for the lazy-loading mechanism\n */\n\nimport { getLlama, LlamaEmbeddingContext } from 'node-llama-cpp';\n\nimport { isModelAvailable, resolveModel } from './model.js';\n\n/** Singleton embedding context */\nlet embeddingContext: LlamaEmbeddingContext | null = null;\n\n/**\n * Get the LlamaEmbeddingContext instance for generating embeddings.\n *\n * **Lazy loading behavior:**\n * - First call loads the embedding model (~150MB) into memory\n * - Loading takes ~1-3 seconds depending on hardware\n * - Subsequent calls return the cached instance immediately\n * - Downloads model automatically if not present\n *\n * **Resource lifecycle:**\n * - Once loaded, model stays in memory until `unloadEmbedding()` is called\n * - For CLI commands: typically load once, use, then unload on exit\n * - For long-running processes: keep loaded for performance\n *\n * @returns The singleton embedding context\n * @throws Error if model download fails\n *\n * @example\n * ```typescript\n * // Direct usage (prefer embedText for simple cases)\n * const ctx = await getEmbedding();\n * const result = await ctx.getEmbeddingFor('some text');\n *\n * // Ensure cleanup\n * process.on('exit', () => unloadEmbedding());\n * ```\n *\n * @see {@link embedText} for simpler text-to-vector conversion\n * @see {@link unloadEmbedding} for releasing memory\n */\nexport async function getEmbedding(): Promise<LlamaEmbeddingContext> {\n if (embeddingContext) return embeddingContext;\n\n // Resolve model path (downloads if needed)\n const modelPath = await resolveModel({ cli: true });\n\n // Load llama and model\n const llama = await getLlama();\n const model = await llama.loadModel({ modelPath });\n embeddingContext = await model.createEmbeddingContext();\n\n return embeddingContext;\n}\n\n/**\n * Unload the embedding context to free memory (~150MB).\n *\n * **Resource lifecycle:**\n * - Disposes the underlying LlamaEmbeddingContext\n * - Releases ~150MB of RAM used by the model\n * - After unloading, subsequent embedding calls will reload the model\n *\n * **When to call:**\n * - At the end of CLI commands to ensure clean process exit\n * - In memory-constrained environments after batch processing\n * - Before process exit in graceful shutdown handlers\n * - When switching to a different model (if supported in future)\n *\n * **Best practices:**\n * - For single-operation scripts: call before exit\n * - For daemon/server processes: call in shutdown handler\n * - Not needed between embedding calls in the same process\n *\n * @example\n * ```typescript\n * // CLI command pattern\n * try {\n * const embedding = await embedText('some text');\n * // ... use embedding\n * } finally {\n * unloadEmbedding();\n * closeDb();\n * }\n *\n * // Graceful shutdown pattern\n * process.on('SIGTERM', () => {\n * unloadEmbedding();\n * closeDb();\n * process.exit(0);\n * });\n * ```\n *\n * @see {@link getEmbedding} for loading the model\n * @see {@link closeDb} for database cleanup (often used together)\n */\nexport function unloadEmbedding(): void {\n if (embeddingContext) {\n embeddingContext.dispose();\n embeddingContext = null;\n }\n}\n\n/**\n * Embed a single text string into a vector.\n *\n * **Lazy loading:** First call loads the embedding model (~150MB, ~1-3s).\n * Subsequent calls use the cached model and complete in milliseconds.\n *\n * @param text - The text to embed\n * @returns A 768-dimensional vector (number[])\n * @throws Error if model download fails\n *\n * @example\n * ```typescript\n * const vector = await embedText('TypeScript error handling');\n * console.log(vector.length); // 768\n *\n * // Remember to clean up when done\n * unloadEmbedding();\n * ```\n *\n * @see {@link embedTexts} for batch embedding\n * @see {@link unloadEmbedding} for releasing memory\n */\nexport async function embedText(text: string): Promise<number[]> {\n const ctx = await getEmbedding();\n const result = await ctx.getEmbeddingFor(text);\n return Array.from(result.vector);\n}\n\n/**\n * Embed multiple texts into vectors.\n *\n * **Lazy loading:** First call loads the embedding model (~150MB, ~1-3s).\n * Subsequent calls use the cached model.\n *\n * **Performance:** More efficient than calling `embedText` in a loop\n * when processing multiple texts, as model loading happens only once.\n *\n * @param texts - Array of texts to embed\n * @returns Array of 768-dimensional vectors, same order as input\n * @throws Error if model download fails\n *\n * @example\n * ```typescript\n * const texts = ['first text', 'second text'];\n * const vectors = await embedTexts(texts);\n * console.log(vectors.length); // 2\n * console.log(vectors[0].length); // 768\n *\n * // Remember to clean up when done\n * unloadEmbedding();\n * ```\n *\n * @see {@link embedText} for single text embedding\n * @see {@link unloadEmbedding} for releasing memory\n */\nexport async function embedTexts(texts: string[]): Promise<number[][]> {\n if (texts.length === 0) return [];\n\n const ctx = await getEmbedding();\n const results: number[][] = [];\n\n for (const text of texts) {\n const result = await ctx.getEmbeddingFor(text);\n results.push(Array.from(result.vector));\n }\n\n return results;\n}\n\n// Re-export isModelAvailable for test utilities\nexport { isModelAvailable };\n","/**\n * Vector search with cosine similarity\n *\n * Embeds query text and ranks lessons by semantic similarity.\n * Uses SQLite cache to avoid recomputing embeddings.\n */\n\nimport { embedText } from '../embeddings/nomic.js';\nimport { contentHash, getCachedEmbedding, setCachedEmbedding } from '../storage/sqlite.js';\nimport { readLessons } from '../storage/jsonl.js';\nimport type { Lesson } from '../types.js';\n\n/**\n * Calculate cosine similarity between two vectors.\n * Returns value between -1 (opposite) and 1 (identical).\n */\nexport function cosineSimilarity(a: number[], b: number[]): number {\n if (a.length !== b.length) {\n throw new Error('Vectors must have same length');\n }\n\n let dotProduct = 0;\n let normA = 0;\n let normB = 0;\n\n for (let i = 0; i < a.length; i++) {\n dotProduct += a[i]! * b[i]!;\n normA += a[i]! * a[i]!;\n normB += b[i]! * b[i]!;\n }\n\n const magnitude = Math.sqrt(normA) * Math.sqrt(normB);\n if (magnitude === 0) return 0;\n\n return dotProduct / magnitude;\n}\n\n/** Lesson with similarity score */\nexport interface ScoredLesson {\n lesson: Lesson;\n score: number;\n}\n\n/** Options for vector search */\nexport interface SearchVectorOptions {\n /** Maximum number of results to return (default: 10) */\n limit?: number;\n}\n\n/** Default number of results to return */\nconst DEFAULT_LIMIT = 10;\n\n/**\n * Search lessons by vector similarity to query text.\n * Returns top N lessons sorted by similarity score (descending).\n * Uses embedding cache to avoid recomputing embeddings.\n */\nexport async function searchVector(\n repoRoot: string,\n query: string,\n options?: SearchVectorOptions\n): Promise<ScoredLesson[]> {\n const limit = options?.limit ?? DEFAULT_LIMIT;\n // Read all lessons\n const { lessons } = await readLessons(repoRoot);\n if (lessons.length === 0) return [];\n\n // Embed the query\n const queryVector = await embedText(query);\n\n // Score each lesson\n const scored: ScoredLesson[] = [];\n for (const lesson of lessons) {\n const lessonText = `${lesson.trigger} ${lesson.insight}`;\n const hash = contentHash(lesson.trigger, lesson.insight);\n\n // Try cache first\n let lessonVector = getCachedEmbedding(repoRoot, lesson.id, hash);\n\n if (!lessonVector) {\n // Cache miss - compute and store\n lessonVector = await embedText(lessonText);\n setCachedEmbedding(repoRoot, lesson.id, lessonVector, hash);\n }\n\n const score = cosineSimilarity(queryVector, lessonVector);\n scored.push({ lesson, score });\n }\n\n // Sort by score descending and take top N\n scored.sort((a, b) => b.score - a.score);\n return scored.slice(0, limit);\n}\n","/**\n * Multi-factor lesson ranking system\n *\n * Combines vector similarity with semantic boosts:\n * - Severity: high=1.5, medium=1.0, low=0.8\n * - Recency: 1.2 for lessons ≤30 days old\n * - Confirmation: 1.3 for confirmed lessons\n */\n\nimport type { Lesson } from '../types.js';\n\nimport type { ScoredLesson } from './vector.js';\n\n/** Lesson with final ranked score */\nexport interface RankedLesson extends ScoredLesson {\n finalScore?: number;\n}\n\nconst RECENCY_THRESHOLD_DAYS = 30;\nconst HIGH_SEVERITY_BOOST = 1.5;\nconst MEDIUM_SEVERITY_BOOST = 1.0;\nconst LOW_SEVERITY_BOOST = 0.8;\nconst RECENCY_BOOST = 1.2;\nconst CONFIRMATION_BOOST = 1.3;\n\n/**\n * Calculate severity boost based on lesson severity.\n * Lessons without severity get 1.0 (medium boost).\n */\nexport function severityBoost(lesson: Lesson): number {\n switch (lesson.severity) {\n case 'high':\n return HIGH_SEVERITY_BOOST;\n case 'medium':\n return MEDIUM_SEVERITY_BOOST;\n case 'low':\n return LOW_SEVERITY_BOOST;\n default:\n return MEDIUM_SEVERITY_BOOST;\n }\n}\n\n/**\n * Calculate recency boost based on lesson age.\n * Lessons ≤30 days old get 1.2, older get 1.0.\n */\nexport function recencyBoost(lesson: Lesson): number {\n const created = new Date(lesson.created);\n const now = new Date();\n const ageMs = now.getTime() - created.getTime();\n const ageDays = Math.floor(ageMs / (1000 * 60 * 60 * 24));\n\n return ageDays <= RECENCY_THRESHOLD_DAYS ? RECENCY_BOOST : 1.0;\n}\n\n/**\n * Calculate confirmation boost.\n * Confirmed lessons get 1.3, unconfirmed get 1.0.\n */\nexport function confirmationBoost(lesson: Lesson): number {\n return lesson.confirmed ? CONFIRMATION_BOOST : 1.0;\n}\n\n/**\n * Calculate combined score for a lesson.\n * score = vectorSimilarity * severity * recency * confirmation\n */\nexport function calculateScore(lesson: Lesson, vectorSimilarity: number): number {\n return (\n vectorSimilarity * severityBoost(lesson) * recencyBoost(lesson) * confirmationBoost(lesson)\n );\n}\n\n/**\n * Rank lessons by combined score.\n * Returns new array sorted by finalScore descending.\n */\nexport function rankLessons(lessons: ScoredLesson[]): RankedLesson[] {\n return lessons\n .map((scored) => ({\n ...scored,\n finalScore: calculateScore(scored.lesson, scored.score),\n }))\n .sort((a, b) => (b.finalScore ?? 0) - (a.finalScore ?? 0));\n}\n","/**\n * Quality filters for lesson capture\n *\n * Filters to ensure lessons are:\n * - Novel (not duplicate)\n * - Specific (not vague)\n * - Actionable (contains action words)\n */\n\nimport { searchKeyword, syncIfNeeded } from '../storage/sqlite.js';\n\n/** Default similarity threshold for duplicate detection */\nconst DEFAULT_SIMILARITY_THRESHOLD = 0.8;\n\n/** Result of novelty check */\nexport interface NoveltyResult {\n novel: boolean;\n reason?: string;\n existingId?: string;\n}\n\n/** Options for novelty check */\nexport interface NoveltyOptions {\n threshold?: number;\n}\n\n/**\n * Check if an insight is novel (not a duplicate of existing lessons).\n * Uses keyword search to find potentially similar lessons.\n */\nexport async function isNovel(\n repoRoot: string,\n insight: string,\n options: NoveltyOptions = {}\n): Promise<NoveltyResult> {\n const threshold = options.threshold ?? DEFAULT_SIMILARITY_THRESHOLD;\n\n // Sync index if JSONL has changed\n await syncIfNeeded(repoRoot);\n\n // Extract key words for search (take first 3 significant words)\n const words = insight\n .toLowerCase()\n .replace(/[^a-z0-9\\s]/g, '')\n .split(/\\s+/)\n .filter((w) => w.length > 3)\n .slice(0, 3);\n\n if (words.length === 0) {\n return { novel: true };\n }\n\n // Search for each word and collect results\n const searchQuery = words.join(' OR ');\n const results = await searchKeyword(repoRoot, searchQuery, 10);\n\n if (results.length === 0) {\n return { novel: true };\n }\n\n // Check similarity using simple word overlap (since we may not have embeddings)\n const insightWords = new Set(insight.toLowerCase().split(/\\s+/));\n\n for (const lesson of results) {\n const lessonWords = new Set(lesson.insight.toLowerCase().split(/\\s+/));\n\n // Calculate Jaccard similarity\n const intersection = [...insightWords].filter((w) => lessonWords.has(w)).length;\n const union = new Set([...insightWords, ...lessonWords]).size;\n const similarity = union > 0 ? intersection / union : 0;\n\n if (similarity >= threshold) {\n return {\n novel: false,\n reason: `Found similar existing lesson: \"${lesson.insight.slice(0, 50)}...\"`,\n existingId: lesson.id,\n };\n }\n\n // Also check exact match\n if (lesson.insight.toLowerCase() === insight.toLowerCase()) {\n return {\n novel: false,\n reason: `Exact duplicate found`,\n existingId: lesson.id,\n };\n }\n }\n\n return { novel: true };\n}\n\n/** Minimum word count for a specific insight */\nconst MIN_WORD_COUNT = 4;\n\n/** Vague patterns that indicate non-specific advice */\nconst VAGUE_PATTERNS = [\n /\\bwrite better\\b/i,\n /\\bbe careful\\b/i,\n /\\bremember to\\b/i,\n /\\bmake sure\\b/i,\n /\\btry to\\b/i,\n /\\bdouble check\\b/i,\n];\n\n/** Generic \"always/never\" phrases (short, lacking specificity) */\nconst GENERIC_IMPERATIVE_PATTERN = /^(always|never)\\s+\\w+(\\s+\\w+){0,2}$/i;\n\n/** Result of specificity check */\nexport interface SpecificityResult {\n specific: boolean;\n reason?: string;\n}\n\n/**\n * Check if an insight is specific enough to be useful.\n * Rejects vague, generic advice that doesn't provide actionable guidance.\n */\nexport function isSpecific(insight: string): SpecificityResult {\n // Check minimum length first\n const words = insight.trim().split(/\\s+/).filter((w) => w.length > 0);\n if (words.length < MIN_WORD_COUNT) {\n return { specific: false, reason: 'Insight is too short to be actionable' };\n }\n\n // Check for vague patterns\n for (const pattern of VAGUE_PATTERNS) {\n if (pattern.test(insight)) {\n return { specific: false, reason: 'Insight matches a vague pattern' };\n }\n }\n\n // Check for generic \"Always X\" or \"Never X\" phrases\n if (GENERIC_IMPERATIVE_PATTERN.test(insight)) {\n return { specific: false, reason: 'Insight matches a vague pattern' };\n }\n\n return { specific: true };\n}\n\n/** Action word patterns that indicate actionable guidance */\nconst ACTION_PATTERNS = [\n /\\buse\\s+.+\\s+instead\\s+of\\b/i, // \"use X instead of Y\"\n /\\bprefer\\s+.+\\s+(over|to)\\b/i, // \"prefer X over Y\" or \"prefer X to Y\"\n /\\balways\\s+.+\\s+when\\b/i, // \"always X when Y\"\n /\\bnever\\s+.+\\s+without\\b/i, // \"never X without Y\"\n /\\bavoid\\s+(using\\s+)?\\w+/i, // \"avoid X\" or \"avoid using X\"\n /\\bcheck\\s+.+\\s+before\\b/i, // \"check X before Y\"\n /^(run|use|add|remove|install|update|configure|set|enable|disable)\\s+/i, // Imperative commands at start\n];\n\n/** Result of actionability check */\nexport interface ActionabilityResult {\n actionable: boolean;\n reason?: string;\n}\n\n/**\n * Check if an insight contains actionable guidance.\n * Returns false for pure observations or questions.\n */\nexport function isActionable(insight: string): ActionabilityResult {\n // Check for action patterns\n for (const pattern of ACTION_PATTERNS) {\n if (pattern.test(insight)) {\n return { actionable: true };\n }\n }\n\n return { actionable: false, reason: 'Insight lacks clear action guidance' };\n}\n\n/** Result of combined quality check */\nexport interface ProposeResult {\n shouldPropose: boolean;\n reason?: string;\n}\n\n/**\n * Combined quality check for lesson proposals.\n * Returns true only if insight is novel, specific, AND actionable.\n */\nexport async function shouldPropose(\n repoRoot: string,\n insight: string\n): Promise<ProposeResult> {\n // Check specificity first (fast, no DB)\n const specificResult = isSpecific(insight);\n if (!specificResult.specific) {\n return { shouldPropose: false, reason: specificResult.reason };\n }\n\n // Check actionability (fast, no DB)\n const actionableResult = isActionable(insight);\n if (!actionableResult.actionable) {\n return { shouldPropose: false, reason: actionableResult.reason };\n }\n\n // Check novelty (requires DB lookup)\n const noveltyResult = await isNovel(repoRoot, insight);\n if (!noveltyResult.novel) {\n return { shouldPropose: false, reason: noveltyResult.reason };\n }\n\n return { shouldPropose: true };\n}\n","/**\n * Trigger detection for automatic lesson capture\n *\n * Detects patterns that indicate potential learning opportunities:\n * - User corrections\n * - Self-corrections\n * - Test failures\n */\n\nimport type { Context } from '../types.js';\n\n/** Signal data for correction detection */\nexport interface CorrectionSignal {\n messages: string[];\n context: Context;\n}\n\n/** Detected correction result */\nexport interface DetectedCorrection {\n trigger: string;\n correctionMessage: string;\n context: Context;\n}\n\n/** User correction patterns */\nconst USER_CORRECTION_PATTERNS = [\n /\\bno\\b[,.]?\\s/i, // \"no, ...\" or \"no ...\"\n /\\bwrong\\b/i, // \"wrong\"\n /\\bactually\\b/i, // \"actually...\"\n /\\bnot that\\b/i, // \"not that\"\n /\\bi meant\\b/i, // \"I meant\"\n];\n\n/**\n * Detect user correction signals in conversation.\n *\n * Looks for patterns that indicate the user is correcting Claude's\n * understanding or actions.\n *\n * @param signals - Messages and context to analyze\n * @returns Detected correction or null if none found\n */\nexport function detectUserCorrection(signals: CorrectionSignal): DetectedCorrection | null {\n const { messages, context } = signals;\n\n if (messages.length < 2) {\n return null;\n }\n\n // Check later messages for correction patterns\n for (let i = 1; i < messages.length; i++) {\n const message = messages[i];\n if (!message) continue;\n\n for (const pattern of USER_CORRECTION_PATTERNS) {\n if (pattern.test(message)) {\n return {\n trigger: `User correction during ${context.intent}`,\n correctionMessage: message,\n context,\n };\n }\n }\n }\n\n return null;\n}\n\n/** Edit history entry */\nexport interface EditEntry {\n file: string;\n success: boolean;\n timestamp: number;\n}\n\n/** Edit history for self-correction detection */\nexport interface EditHistory {\n edits: EditEntry[];\n}\n\n/** Detected self-correction */\nexport interface DetectedSelfCorrection {\n file: string;\n trigger: string;\n}\n\n/**\n * Detect self-correction patterns in edit history.\n *\n * Looks for edit→fail→re-edit patterns on the same file,\n * which indicate Claude had to correct its own work.\n *\n * @param history - Edit history to analyze\n * @returns Detected self-correction or null if none found\n */\nexport function detectSelfCorrection(history: EditHistory): DetectedSelfCorrection | null {\n const { edits } = history;\n\n if (edits.length < 3) {\n return null;\n }\n\n // Look for edit→fail→re-edit pattern on same file\n for (let i = 0; i <= edits.length - 3; i++) {\n const first = edits[i];\n const second = edits[i + 1];\n const third = edits[i + 2];\n\n if (!first || !second || !third) continue;\n\n // Pattern: success → fail → success on same file\n if (\n first.file === second.file &&\n second.file === third.file &&\n first.success &&\n !second.success &&\n third.success\n ) {\n return {\n file: first.file,\n trigger: `Self-correction on ${first.file}`,\n };\n }\n }\n\n return null;\n}\n\n/** Test result for failure detection */\nexport interface TestResult {\n passed: boolean;\n output: string;\n testFile: string;\n}\n\n/** Detected test failure */\nexport interface DetectedTestFailure {\n testFile: string;\n errorOutput: string;\n trigger: string;\n}\n\n/**\n * Detect test failure patterns.\n *\n * When tests fail, this creates a potential learning opportunity\n * if the failure is later fixed.\n *\n * @param testResult - Test result to analyze\n * @returns Detected test failure or null if tests passed\n */\nexport function detectTestFailure(testResult: TestResult): DetectedTestFailure | null {\n if (testResult.passed) {\n return null;\n }\n\n // Extract first meaningful error line for trigger\n const lines = testResult.output.split('\\n').filter((line) => line.trim().length > 0);\n const errorLine = lines.find((line) => /error|fail|assert/i.test(line)) ?? lines[0] ?? '';\n\n return {\n testFile: testResult.testFile,\n errorOutput: testResult.output,\n trigger: `Test failure in ${testResult.testFile}: ${errorLine.slice(0, 100)}`,\n };\n}\n","/**\n * Session-start lesson retrieval\n *\n * Loads high-severity lessons at the start of a session.\n * No vector search - just filter by severity and recency.\n */\n\nimport { readLessons } from '../storage/jsonl.js';\nimport type { Lesson, Severity } from '../types.js';\n\n/** Default number of lessons to load at session start */\nconst DEFAULT_LIMIT = 5;\n\n/** A full lesson with severity field present */\ntype FullLesson = Lesson & { type: 'full'; severity: Severity };\n\n/**\n * Type guard to check if a lesson is a full lesson with severity\n */\nfunction isFullLesson(lesson: Lesson): lesson is FullLesson {\n return lesson.type === 'full' && lesson.severity !== undefined;\n}\n\n/**\n * Load high-severity lessons for session start.\n *\n * Returns confirmed, high-severity lessons sorted by recency.\n * These are the most important lessons to surface at the start\n * of a coding session.\n *\n * @param repoRoot - Repository root directory\n * @param limit - Maximum number of lessons to return (default: 5)\n * @returns Array of high-severity lessons, most recent first\n */\nexport async function loadSessionLessons(\n repoRoot: string,\n limit: number = DEFAULT_LIMIT\n): Promise<FullLesson[]> {\n const { lessons: allLessons } = await readLessons(repoRoot);\n\n // Filter for high-severity, confirmed, full lessons\n const highSeverityLessons = allLessons.filter(\n (lesson): lesson is FullLesson =>\n isFullLesson(lesson) && lesson.severity === 'high' && lesson.confirmed\n );\n\n // Sort by recency (most recent first)\n highSeverityLessons.sort((a, b) => {\n const dateA = new Date(a.created).getTime();\n const dateB = new Date(b.created).getTime();\n return dateB - dateA;\n });\n\n // Return top N\n return highSeverityLessons.slice(0, limit);\n}\n","/**\n * Plan-time lesson retrieval\n *\n * Retrieves relevant lessons when planning an implementation.\n * Uses vector search to find semantically similar lessons.\n */\n\nimport { searchVector, type ScoredLesson } from '../search/vector.js';\nimport { rankLessons, type RankedLesson } from '../search/ranking.js';\n\n/** Default number of lessons to retrieve */\nconst DEFAULT_LIMIT = 5;\n\n/** Result of plan-time retrieval */\nexport interface PlanRetrievalResult {\n lessons: RankedLesson[];\n message: string;\n}\n\n/**\n * Retrieve relevant lessons for a plan.\n *\n * Uses vector search to find semantically similar lessons,\n * then applies ranking boosts for severity, recency, and confirmation.\n *\n * Hard-fails if embeddings are unavailable (propagates error from embedText).\n *\n * @param repoRoot - Repository root directory\n * @param planText - The plan text to search against\n * @param limit - Maximum number of lessons to return (default: 5)\n * @returns Ranked lessons and formatted message\n */\nexport async function retrieveForPlan(\n repoRoot: string,\n planText: string,\n limit: number = DEFAULT_LIMIT\n): Promise<PlanRetrievalResult> {\n // Get lessons by vector similarity (will throw if embeddings unavailable)\n const scored = await searchVector(repoRoot, planText, { limit: limit * 2 });\n\n // Apply ranking boosts\n const ranked = rankLessons(scored);\n\n // Take top N after ranking\n const topLessons = ranked.slice(0, limit);\n\n // Format the Lessons Check message\n const message = formatLessonsCheck(topLessons);\n\n return { lessons: topLessons, message };\n}\n\n/**\n * Format a \"Lessons Check\" message for display.\n *\n * This message is intended to be shown at plan-time to remind\n * the developer of relevant lessons before implementation.\n *\n * @param lessons - Ranked lessons to include in the message\n * @returns Formatted message string\n */\nexport function formatLessonsCheck(lessons: ScoredLesson[]): string {\n const header = '📚 Lessons Check\\n' + '─'.repeat(40);\n\n if (lessons.length === 0) {\n return `${header}\\nNo relevant lessons found for this plan.`;\n }\n\n const lessonLines = lessons.map((l, i) => {\n const bullet = `${i + 1}.`;\n const insight = l.lesson.insight;\n return `${bullet} ${insight}`;\n });\n\n return `${header}\\n${lessonLines.join('\\n')}`;\n}\n","/**\n * Learning Agent - Repository-scoped learning system for Claude Code\n *\n * This package helps Claude Code learn from mistakes and avoid repeating them.\n * It captures lessons during coding sessions and retrieves relevant lessons\n * when planning new work.\n *\n * ## Quick Start\n *\n * ```typescript\n * import { appendLesson, retrieveForPlan, loadSessionLessons } from 'learning-agent';\n *\n * // At session start, load high-severity lessons\n * const criticalLessons = await loadSessionLessons(repoRoot);\n *\n * // When planning, retrieve relevant lessons\n * const { lessons, message } = await retrieveForPlan(repoRoot, planText);\n *\n * // When capturing a lesson\n * await appendLesson(repoRoot, lesson);\n * ```\n *\n * ## Hook Integration\n *\n * Add to your `.claude/settings.json`:\n *\n * ```json\n * {\n * \"hooks\": {\n * \"session_start\": \"npx learning-agent load-session\",\n * \"pre_tool\": \"npx learning-agent check-plan\"\n * }\n * }\n * ```\n *\n * ## Resource Management\n *\n * This library manages two heavyweight resources that require cleanup:\n *\n * ### SQLite Database\n * - **Acquired:** Lazily on first database operation (search, rebuild, etc.)\n * - **Memory:** Minimal (~few KB for connection, index cached by OS)\n * - **Cleanup:** Call `closeDb()` before process exit\n *\n * ### Embedding Model\n * - **Acquired:** Lazily on first embedding call (embedText, embedTexts, searchVector)\n * - **Memory:** ~150MB RAM for the EmbeddingGemma model\n * - **Cleanup:** Call `unloadEmbedding()` before process exit\n *\n * ### Recommended Cleanup Pattern\n *\n * ```typescript\n * import { closeDb, unloadEmbedding } from 'learning-agent';\n *\n * // For CLI commands - use try/finally\n * async function main() {\n * try {\n * // ... your code that uses learning-agent\n * } finally {\n * unloadEmbedding();\n * closeDb();\n * }\n * }\n *\n * // For long-running processes - use shutdown handlers\n * process.on('SIGTERM', () => {\n * unloadEmbedding();\n * closeDb();\n * process.exit(0);\n * });\n * process.on('SIGINT', () => {\n * unloadEmbedding();\n * closeDb();\n * process.exit(0);\n * });\n * ```\n *\n * **Note:** Failing to clean up will not corrupt data, but may cause:\n * - Memory leaks in long-running processes\n * - Unclean process exits (warnings in some environments)\n *\n * @see {@link closeDb} for database cleanup\n * @see {@link unloadEmbedding} for embedding model cleanup\n * @module learning-agent\n */\n\nexport const VERSION = '0.1.0';\n\n// Storage API\nexport { appendLesson, readLessons, LESSONS_PATH } from './storage/jsonl.js';\nexport type { ReadLessonsOptions, ReadLessonsResult, ParseError } from './storage/jsonl.js';\nexport { rebuildIndex, searchKeyword, closeDb, DB_PATH } from './storage/sqlite.js';\n\n// Embeddings API\nexport { embedText, embedTexts, getEmbedding, isModelAvailable, unloadEmbedding } from './embeddings/nomic.js';\nexport { MODEL_FILENAME, MODEL_URI, resolveModel } from './embeddings/model.js';\n\n// Search API\nexport { searchVector, cosineSimilarity } from './search/vector.js';\nexport type { ScoredLesson, SearchVectorOptions } from './search/vector.js';\nexport { rankLessons, calculateScore, severityBoost, recencyBoost, confirmationBoost } from './search/ranking.js';\nexport type { RankedLesson } from './search/ranking.js';\n\n// Capture API - Quality filters\nexport { shouldPropose, isNovel, isSpecific, isActionable } from './capture/quality.js';\nexport type { NoveltyResult, NoveltyOptions, SpecificityResult, ActionabilityResult, ProposeResult } from './capture/quality.js';\n\n// Capture API - Triggers\nexport { detectUserCorrection, detectSelfCorrection, detectTestFailure } from './capture/triggers.js';\nexport type {\n CorrectionSignal,\n DetectedCorrection,\n EditHistory,\n EditEntry,\n DetectedSelfCorrection,\n TestResult,\n DetectedTestFailure,\n} from './capture/triggers.js';\n\n// Retrieval API\nexport { loadSessionLessons } from './retrieval/session.js';\nexport { retrieveForPlan, formatLessonsCheck } from './retrieval/plan.js';\nexport type { PlanRetrievalResult } from './retrieval/plan.js';\n\n// Types and schemas\nexport {\n generateId,\n LessonSchema,\n LessonTypeSchema,\n TombstoneSchema,\n} from './types.js';\nexport type {\n Lesson,\n LessonType,\n Tombstone,\n Source,\n Severity,\n Context,\n} from './types.js';\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/types.ts","../src/storage/jsonl.ts","../src/storage/sqlite/availability.ts","../src/storage/sqlite/schema.ts","../src/storage/sqlite/connection.ts","../src/storage/sqlite/cache.ts","../src/storage/sqlite/sync.ts","../src/storage/sqlite/search.ts","../src/utils.ts","../src/embeddings/model.ts","../src/embeddings/nomic.ts","../src/search/vector.ts","../src/search/ranking.ts","../src/capture/quality.ts","../src/capture/triggers.ts","../src/retrieval/session.ts","../src/retrieval/plan.ts","../src/index.ts"],"names":["require","join","dirname","createHash","mtime","DEFAULT_LIMIT"],"mappings":"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","file":"index.js","sourcesContent":["/**\n * Lesson type definitions using Zod schemas\n */\n\nimport { createHash } from 'node:crypto';\nimport { z } from 'zod';\n\n// Source of lesson capture\nexport const SourceSchema = z.enum([\n 'user_correction',\n 'self_correction',\n 'test_failure',\n 'manual',\n]);\n\n// Context about when lesson was learned\nexport const ContextSchema = z.object({\n tool: z.string(),\n intent: z.string(),\n});\n\n// Code pattern (bad -> good)\nexport const PatternSchema = z.object({\n bad: z.string(),\n good: z.string(),\n});\n\n// Citation for lesson provenance tracking\nexport const CitationSchema = z.object({\n file: z.string().min(1), // Source file path (required, non-empty)\n line: z.number().int().positive().optional(), // Line number (optional, must be positive)\n commit: z.string().optional(), // Git commit hash (optional)\n});\n\n// Severity levels for lessons\nexport const SeveritySchema = z.enum(['high', 'medium', 'low']);\n\n// Compaction levels for age-based validity\nexport const CompactionLevelSchema = z.union([\n z.literal(0), // Active\n z.literal(1), // Flagged (>90 days)\n z.literal(2), // Archived\n]);\n\n// Lesson type - semantic marker for lesson quality tier\nexport const LessonTypeSchema = z.enum(['quick', 'full']);\n\n/**\n * Unified Lesson schema.\n *\n * The `type` field is a semantic marker:\n * - 'quick': Minimal lesson for fast capture\n * - 'full': Important lesson (typically has evidence/severity)\n *\n * All fields except core identity are optional for flexibility.\n * Semantic meaning is preserved through convention, not schema enforcement.\n */\nexport const LessonSchema = z.object({\n // Core identity (required)\n id: z.string(),\n type: LessonTypeSchema,\n trigger: z.string(),\n insight: z.string(),\n\n // Metadata (required)\n tags: z.array(z.string()),\n source: SourceSchema,\n context: ContextSchema,\n created: z.string(), // ISO8601\n confirmed: z.boolean(),\n\n // Relationships (required, can be empty arrays)\n supersedes: z.array(z.string()),\n related: z.array(z.string()),\n\n // Extended fields (optional - typically present for 'full' type)\n evidence: z.string().optional(),\n severity: SeveritySchema.optional(),\n pattern: PatternSchema.optional(),\n\n // Lifecycle fields (optional)\n deleted: z.boolean().optional(),\n retrievalCount: z.number().optional(),\n\n // Provenance tracking (optional)\n citation: CitationSchema.optional(),\n\n // Age-based validity fields (optional)\n compactionLevel: CompactionLevelSchema.optional(), // 0=active, 1=flagged, 2=archived\n compactedAt: z.string().optional(), // ISO8601 when compaction happened\n lastRetrieved: z.string().optional(), // ISO8601 last retrieval time\n\n // Invalidation fields (optional - for marking lessons as wrong)\n invalidatedAt: z.string().optional(), // ISO8601\n invalidationReason: z.string().optional(),\n});\n\n// Tombstone for deletions (append-only delete marker)\nexport const TombstoneSchema = z.object({\n id: z.string(),\n deleted: z.literal(true),\n deletedAt: z.string(), // ISO8601\n});\n\n// Type exports\nexport type Lesson = z.infer<typeof LessonSchema>;\nexport type LessonType = z.infer<typeof LessonTypeSchema>;\nexport type Tombstone = z.infer<typeof TombstoneSchema>;\nexport type Source = z.infer<typeof SourceSchema>;\nexport type Severity = z.infer<typeof SeveritySchema>;\nexport type Context = z.infer<typeof ContextSchema>;\nexport type Pattern = z.infer<typeof PatternSchema>;\nexport type Citation = z.infer<typeof CitationSchema>;\nexport type CompactionLevel = z.infer<typeof CompactionLevelSchema>;\n\n/**\n * Generate deterministic lesson ID from insight text.\n * Format: L + 8 hex characters from SHA-256 hash\n */\nexport function generateId(insight: string): string {\n const hash = createHash('sha256').update(insight).digest('hex');\n return `L${hash.slice(0, 8)}`;\n}\n","/**\n * JSONL storage layer for lessons\n *\n * Append-only storage with last-write-wins deduplication.\n * Source of truth - git trackable.\n */\n\nimport { appendFile, mkdir, readFile } from 'node:fs/promises';\nimport { dirname, join } from 'node:path';\nimport { LessonSchema, type Lesson } from '../types.js';\n\n/** Relative path to lessons file from repo root */\nexport const LESSONS_PATH = '.claude/lessons/index.jsonl';\n\n/** Options for reading lessons */\nexport interface ReadLessonsOptions {\n /** If true, throw on first parse error. Default: false (skip errors) */\n strict?: boolean;\n /** Callback for each parse error in non-strict mode */\n onParseError?: (error: ParseError) => void;\n}\n\n/** Parse error details */\nexport interface ParseError {\n /** 1-based line number */\n line: number;\n /** Error message */\n message: string;\n /** Original error */\n cause: unknown;\n}\n\n/** Result of reading lessons */\nexport interface ReadLessonsResult {\n /** Successfully parsed lessons */\n lessons: Lesson[];\n /** Number of lines skipped due to errors */\n skippedCount: number;\n}\n\n/**\n * Append a lesson to the JSONL file.\n * Creates directory structure if missing.\n */\nexport async function appendLesson(repoRoot: string, lesson: Lesson): Promise<void> {\n const filePath = join(repoRoot, LESSONS_PATH);\n await mkdir(dirname(filePath), { recursive: true });\n\n const line = JSON.stringify(lesson) + '\\n';\n await appendFile(filePath, line, 'utf-8');\n}\n\n/**\n * Parse and validate a single JSON line.\n * @returns Parsed lesson or null if invalid\n */\nfunction parseJsonLine(\n line: string,\n lineNumber: number,\n strict: boolean,\n onParseError?: (error: ParseError) => void\n): Lesson | null {\n // Try to parse JSON\n let parsed: unknown;\n try {\n parsed = JSON.parse(line);\n } catch (err) {\n const parseError: ParseError = {\n line: lineNumber,\n message: `Invalid JSON: ${(err as Error).message}`,\n cause: err,\n };\n if (strict) {\n throw new Error(`Parse error on line ${lineNumber}: ${parseError.message}`);\n }\n onParseError?.(parseError);\n return null;\n }\n\n // Validate against schema\n const result = LessonSchema.safeParse(parsed);\n if (!result.success) {\n const parseError: ParseError = {\n line: lineNumber,\n message: `Schema validation failed: ${result.error.message}`,\n cause: result.error,\n };\n if (strict) {\n throw new Error(`Parse error on line ${lineNumber}: ${parseError.message}`);\n }\n onParseError?.(parseError);\n return null;\n }\n\n return result.data;\n}\n\n/**\n * Read all non-deleted lessons from the JSONL file.\n * Applies last-write-wins deduplication by ID.\n * Returns result object with lessons and skippedCount.\n *\n * @param repoRoot - Repository root directory\n * @param options - Optional settings for error handling\n * @returns Result with lessons array and count of skipped lines\n */\nexport async function readLessons(\n repoRoot: string,\n options: ReadLessonsOptions = {}\n): Promise<ReadLessonsResult> {\n const { strict = false, onParseError } = options;\n const filePath = join(repoRoot, LESSONS_PATH);\n\n let content: string;\n try {\n content = await readFile(filePath, 'utf-8');\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code === 'ENOENT') {\n return { lessons: [], skippedCount: 0 };\n }\n throw err;\n }\n\n const lessons = new Map<string, Lesson>();\n let skippedCount = 0;\n\n const lines = content.split('\\n');\n for (let i = 0; i < lines.length; i++) {\n const trimmed = lines[i]!.trim();\n if (!trimmed) continue;\n\n const lesson = parseJsonLine(trimmed, i + 1, strict, onParseError);\n if (!lesson) {\n skippedCount++;\n continue;\n }\n\n if (lesson.deleted) {\n lessons.delete(lesson.id);\n } else {\n lessons.set(lesson.id, lesson);\n }\n }\n\n return { lessons: Array.from(lessons.values()), skippedCount };\n}\n","/**\n * SQLite availability detection and graceful degradation.\n *\n * If better-sqlite3 fails to load (e.g., native binding compilation issues),\n * the module operates in JSONL-only mode. JSONL remains the source of truth;\n * SQLite is just a cache/index.\n */\n\nimport { createRequire } from 'node:module';\nimport type { Database as DatabaseType } from 'better-sqlite3';\n\n// Create require function for ESM compatibility\nconst require = createRequire(import.meta.url);\n\n/** SQLite availability state */\nlet sqliteAvailable: boolean | null = null;\nlet sqliteWarningLogged = false;\nlet DatabaseConstructor: (new (path: string) => DatabaseType) | null = null;\n\n/** Test-only flag to simulate SQLite unavailability */\nlet _forceUnavailable = false;\n\n/**\n * Check if SQLite is available and can be loaded.\n * @returns true if SQLite is available, false otherwise\n */\nexport function isSqliteAvailable(): boolean {\n // Test hook: force unavailability for degradation tests\n if (_forceUnavailable) {\n if (!sqliteWarningLogged) {\n console.warn('SQLite unavailable, running in JSONL-only mode');\n sqliteWarningLogged = true;\n }\n return false;\n }\n\n if (sqliteAvailable !== null) {\n return sqliteAvailable;\n }\n\n try {\n // eslint-disable-next-line @typescript-eslint/no-require-imports\n const module = require('better-sqlite3');\n const Constructor = module.default || module;\n const testDb = new Constructor(':memory:');\n testDb.close();\n DatabaseConstructor = Constructor;\n sqliteAvailable = true;\n } catch {\n sqliteAvailable = false;\n if (!sqliteWarningLogged) {\n console.warn('SQLite unavailable, running in JSONL-only mode');\n sqliteWarningLogged = true;\n }\n }\n\n return sqliteAvailable;\n}\n\n/**\n * Log degradation warning if not already logged.\n */\nexport function logDegradationWarning(): void {\n if (!sqliteAvailable && !sqliteWarningLogged) {\n console.warn('SQLite unavailable, running in JSONL-only mode');\n sqliteWarningLogged = true;\n }\n}\n\n/**\n * Check if SQLite is available and the module is operating in SQLite mode.\n * @returns true if SQLite loaded successfully, false if degraded to JSONL-only mode\n */\nexport function isSqliteMode(): boolean {\n return isSqliteAvailable();\n}\n\n/**\n * Get the SQLite Database constructor.\n * @returns Database constructor or null if unavailable\n */\nexport function getDatabaseConstructor(): (new (path: string) => DatabaseType) | null {\n if (!isSqliteAvailable()) {\n return null;\n }\n return DatabaseConstructor;\n}\n\n/**\n * Reset SQLite state. Used in tests to reset detection state.\n */\nexport function _resetSqliteState(): void {\n sqliteAvailable = null;\n sqliteWarningLogged = false;\n DatabaseConstructor = null;\n _forceUnavailable = false;\n}\n\n/**\n * Force SQLite to be unavailable. Used in tests to simulate degradation.\n * @internal Test-only API\n */\nexport function _setForceUnavailable(value: boolean): void {\n _forceUnavailable = value;\n if (value) {\n sqliteAvailable = null;\n DatabaseConstructor = null;\n }\n}\n","/**\n * SQLite schema definition for lessons database.\n */\n\nimport type { Database as DatabaseType } from 'better-sqlite3';\n\n/** SQL schema for lessons database with FTS5 full-text search */\nexport const SCHEMA_SQL = `\n CREATE TABLE IF NOT EXISTS lessons (\n id TEXT PRIMARY KEY,\n type TEXT NOT NULL,\n trigger TEXT NOT NULL,\n insight TEXT NOT NULL,\n evidence TEXT,\n severity TEXT,\n tags TEXT NOT NULL DEFAULT '',\n source TEXT NOT NULL,\n context TEXT NOT NULL DEFAULT '{}',\n supersedes TEXT NOT NULL DEFAULT '[]',\n related TEXT NOT NULL DEFAULT '[]',\n created TEXT NOT NULL,\n confirmed INTEGER NOT NULL DEFAULT 0,\n deleted INTEGER NOT NULL DEFAULT 0,\n retrieval_count INTEGER NOT NULL DEFAULT 0,\n last_retrieved TEXT,\n embedding BLOB,\n content_hash TEXT,\n invalidated_at TEXT,\n invalidation_reason TEXT,\n citation_file TEXT,\n citation_line INTEGER,\n citation_commit TEXT,\n compaction_level INTEGER DEFAULT 0,\n compacted_at TEXT\n );\n\n CREATE VIRTUAL TABLE IF NOT EXISTS lessons_fts USING fts5(\n id, trigger, insight, tags,\n content='lessons', content_rowid='rowid'\n );\n\n CREATE TRIGGER IF NOT EXISTS lessons_ai AFTER INSERT ON lessons BEGIN\n INSERT INTO lessons_fts(rowid, id, trigger, insight, tags)\n VALUES (new.rowid, new.id, new.trigger, new.insight, new.tags);\n END;\n\n CREATE TRIGGER IF NOT EXISTS lessons_ad AFTER DELETE ON lessons BEGIN\n INSERT INTO lessons_fts(lessons_fts, rowid, id, trigger, insight, tags)\n VALUES ('delete', old.rowid, old.id, old.trigger, old.insight, old.tags);\n END;\n\n CREATE TRIGGER IF NOT EXISTS lessons_au AFTER UPDATE ON lessons BEGIN\n INSERT INTO lessons_fts(lessons_fts, rowid, id, trigger, insight, tags)\n VALUES ('delete', old.rowid, old.id, old.trigger, old.insight, old.tags);\n INSERT INTO lessons_fts(rowid, id, trigger, insight, tags)\n VALUES (new.rowid, new.id, new.trigger, new.insight, new.tags);\n END;\n\n CREATE INDEX IF NOT EXISTS idx_lessons_created ON lessons(created);\n CREATE INDEX IF NOT EXISTS idx_lessons_confirmed ON lessons(confirmed);\n CREATE INDEX IF NOT EXISTS idx_lessons_severity ON lessons(severity);\n\n CREATE TABLE IF NOT EXISTS metadata (\n key TEXT PRIMARY KEY,\n value TEXT NOT NULL\n );\n`;\n\n/**\n * Create the database schema.\n * @param database - SQLite database instance\n */\nexport function createSchema(database: DatabaseType): void {\n database.exec(SCHEMA_SQL);\n}\n","/**\n * SQLite database connection management.\n */\n\nimport { mkdirSync } from 'node:fs';\nimport { dirname, join } from 'node:path';\nimport type { Database as DatabaseType } from 'better-sqlite3';\n\nimport type { DbOptions } from './types.js';\nimport { getDatabaseConstructor, isSqliteAvailable } from './availability.js';\nimport { createSchema } from './schema.js';\n\n/** Relative path to database file from repo root */\nexport const DB_PATH = '.claude/.cache/lessons.sqlite';\n\n/** Database singleton */\nlet db: DatabaseType | null = null;\nlet dbIsInMemory = false;\n\n/**\n * Open the SQLite database connection.\n * Gracefully degrades: returns null if SQLite unavailable.\n * @param repoRoot - Absolute path to repository root\n * @param options - Database options (e.g., inMemory for testing)\n * @returns Database instance or null if SQLite unavailable\n */\nexport function openDb(repoRoot: string, options: DbOptions = {}): DatabaseType | null {\n if (!isSqliteAvailable()) {\n return null;\n }\n\n const { inMemory = false } = options;\n\n if (db) {\n if (inMemory !== dbIsInMemory) {\n closeDb();\n } else {\n return db;\n }\n }\n\n const Database = getDatabaseConstructor()!;\n\n if (inMemory) {\n db = new Database(':memory:');\n dbIsInMemory = true;\n } else {\n const dbPath = join(repoRoot, DB_PATH);\n const dir = dirname(dbPath);\n mkdirSync(dir, { recursive: true });\n db = new Database(dbPath);\n dbIsInMemory = false;\n db.pragma('journal_mode = WAL');\n }\n\n createSchema(db);\n return db;\n}\n\n/**\n * Close the SQLite database connection.\n */\nexport function closeDb(): void {\n if (db) {\n db.close();\n db = null;\n dbIsInMemory = false;\n }\n}\n\n/**\n * Get the current database instance (for internal use).\n * @returns Current database instance or null\n */\nexport function getDb(): DatabaseType | null {\n return db;\n}\n","/**\n * Embedding cache operations for SQLite storage.\n */\n\nimport { createHash } from 'node:crypto';\nimport type { Database as DatabaseType } from 'better-sqlite3';\n\nimport type { CachedEmbeddingData } from './types.js';\nimport { logDegradationWarning } from './availability.js';\nimport { openDb } from './connection.js';\n\n/**\n * Compute content hash for a lesson's trigger and insight.\n * Used to detect content changes for embedding cache invalidation.\n * @param trigger - The lesson trigger text\n * @param insight - The lesson insight text\n * @returns SHA-256 hash of the combined content\n */\nexport function contentHash(trigger: string, insight: string): string {\n return createHash('sha256').update(`${trigger} ${insight}`).digest('hex');\n}\n\n/**\n * Get cached embedding for a lesson.\n * Gracefully degrades: returns null if SQLite unavailable.\n * @param repoRoot - Absolute path to repository root\n * @param lessonId - ID of the lesson\n * @param expectedHash - Optional content hash to validate cache freshness\n * @returns Embedding array or null if not cached/unavailable\n */\nexport function getCachedEmbedding(\n repoRoot: string,\n lessonId: string,\n expectedHash?: string\n): number[] | null {\n const database = openDb(repoRoot);\n if (!database) {\n logDegradationWarning();\n return null;\n }\n\n const row = database\n .prepare('SELECT embedding, content_hash FROM lessons WHERE id = ?')\n .get(lessonId) as { embedding: Buffer | null; content_hash: string | null } | undefined;\n\n if (!row || !row.embedding || !row.content_hash) {\n return null;\n }\n\n if (expectedHash && row.content_hash !== expectedHash) {\n return null;\n }\n\n const float32 = new Float32Array(\n row.embedding.buffer,\n row.embedding.byteOffset,\n row.embedding.byteLength / 4\n );\n return Array.from(float32);\n}\n\n/**\n * Cache embedding for a lesson in SQLite.\n * Gracefully degrades: no-op if SQLite unavailable.\n * @param repoRoot - Absolute path to repository root\n * @param lessonId - ID of the lesson\n * @param embedding - Embedding vector (Float32Array or number array)\n * @param hash - Content hash for cache validation\n */\nexport function setCachedEmbedding(\n repoRoot: string,\n lessonId: string,\n embedding: Float32Array | number[],\n hash: string\n): void {\n const database = openDb(repoRoot);\n if (!database) {\n logDegradationWarning();\n return;\n }\n\n const float32 = embedding instanceof Float32Array ? embedding : new Float32Array(embedding);\n const buffer = Buffer.from(float32.buffer, float32.byteOffset, float32.byteLength);\n\n database\n .prepare('UPDATE lessons SET embedding = ?, content_hash = ? WHERE id = ?')\n .run(buffer, hash, lessonId);\n}\n\n/**\n * Collect all cached embeddings from the database.\n * Used during index rebuild to preserve valid caches.\n * @param database - SQLite database instance\n * @returns Map of lesson ID to cached embedding data\n */\nexport function collectCachedEmbeddings(database: DatabaseType): Map<string, CachedEmbeddingData> {\n const cache = new Map<string, CachedEmbeddingData>();\n const rows = database\n .prepare('SELECT id, embedding, content_hash FROM lessons WHERE embedding IS NOT NULL')\n .all() as Array<{ id: string; embedding: Buffer; content_hash: string | null }>;\n\n for (const row of rows) {\n if (row.embedding && row.content_hash) {\n cache.set(row.id, { embedding: row.embedding, contentHash: row.content_hash });\n }\n }\n return cache;\n}\n","/**\n * SQLite index synchronization with JSONL source of truth.\n */\n\nimport { statSync } from 'node:fs';\nimport { join } from 'node:path';\nimport type { Database as DatabaseType } from 'better-sqlite3';\n\nimport type { Lesson } from '../../types.js';\nimport { LESSONS_PATH, readLessons } from '../jsonl.js';\n\nimport type { SyncOptions } from './types.js';\nimport { isSqliteAvailable, logDegradationWarning } from './availability.js';\nimport { openDb } from './connection.js';\nimport { collectCachedEmbeddings, contentHash } from './cache.js';\n\n/** SQL for inserting a lesson record */\nconst INSERT_LESSON_SQL = `\n INSERT INTO lessons (id, type, trigger, insight, evidence, severity, tags, source, context, supersedes, related, created, confirmed, deleted, retrieval_count, last_retrieved, embedding, content_hash, invalidated_at, invalidation_reason, citation_file, citation_line, citation_commit, compaction_level, compacted_at)\n VALUES (@id, @type, @trigger, @insight, @evidence, @severity, @tags, @source, @context, @supersedes, @related, @created, @confirmed, @deleted, @retrieval_count, @last_retrieved, @embedding, @content_hash, @invalidated_at, @invalidation_reason, @citation_file, @citation_line, @citation_commit, @compaction_level, @compacted_at)\n`;\n\n/**\n * Get the modification time of the JSONL file.\n * @param repoRoot - Absolute path to repository root\n * @returns Modification time in milliseconds or null if file doesn't exist\n */\nfunction getJsonlMtime(repoRoot: string): number | null {\n const jsonlPath = join(repoRoot, LESSONS_PATH);\n try {\n const stat = statSync(jsonlPath);\n return stat.mtimeMs;\n } catch {\n return null;\n }\n}\n\n/**\n * Get the last sync modification time from metadata.\n * @param database - SQLite database instance\n * @returns Last sync mtime or null if not set\n */\nfunction getLastSyncMtime(database: DatabaseType): number | null {\n const row = database\n .prepare('SELECT value FROM metadata WHERE key = ?')\n .get('last_sync_mtime') as { value: string } | undefined;\n return row ? parseFloat(row.value) : null;\n}\n\n/**\n * Set the last sync modification time in metadata.\n * @param database - SQLite database instance\n * @param mtime - Modification time to store\n */\nfunction setLastSyncMtime(database: DatabaseType, mtime: number): void {\n database\n .prepare('INSERT OR REPLACE INTO metadata (key, value) VALUES (?, ?)')\n .run('last_sync_mtime', mtime.toString());\n}\n\n/**\n * Rebuild the SQLite index from JSONL source of truth.\n * Gracefully degrades: no-op with warning if SQLite unavailable.\n * Preserves cached embeddings when lesson content hasn't changed.\n * @param repoRoot - Absolute path to repository root\n */\nexport async function rebuildIndex(repoRoot: string): Promise<void> {\n const database = openDb(repoRoot);\n if (!database) {\n logDegradationWarning();\n return;\n }\n\n const { lessons } = await readLessons(repoRoot);\n const cachedEmbeddings = collectCachedEmbeddings(database);\n database.exec('DELETE FROM lessons');\n\n if (lessons.length === 0) {\n const mtime = getJsonlMtime(repoRoot);\n if (mtime !== null) {\n setLastSyncMtime(database, mtime);\n }\n return;\n }\n\n const insert = database.prepare(INSERT_LESSON_SQL);\n const insertMany = database.transaction((items: Lesson[]) => {\n for (const lesson of items) {\n const newHash = contentHash(lesson.trigger, lesson.insight);\n const cached = cachedEmbeddings.get(lesson.id);\n const hasValidCache = cached && cached.contentHash === newHash;\n\n insert.run({\n id: lesson.id,\n type: lesson.type,\n trigger: lesson.trigger,\n insight: lesson.insight,\n evidence: lesson.evidence ?? null,\n severity: lesson.severity ?? null,\n tags: lesson.tags.join(','),\n source: lesson.source,\n context: JSON.stringify(lesson.context),\n supersedes: JSON.stringify(lesson.supersedes),\n related: JSON.stringify(lesson.related),\n created: lesson.created,\n confirmed: lesson.confirmed ? 1 : 0,\n deleted: lesson.deleted ? 1 : 0,\n retrieval_count: lesson.retrievalCount ?? 0,\n last_retrieved: lesson.lastRetrieved ?? null,\n embedding: hasValidCache ? cached.embedding : null,\n content_hash: hasValidCache ? cached.contentHash : null,\n invalidated_at: lesson.invalidatedAt ?? null,\n invalidation_reason: lesson.invalidationReason ?? null,\n citation_file: lesson.citation?.file ?? null,\n citation_line: lesson.citation?.line ?? null,\n citation_commit: lesson.citation?.commit ?? null,\n compaction_level: lesson.compactionLevel ?? 0,\n compacted_at: lesson.compactedAt ?? null,\n });\n }\n });\n\n insertMany(lessons);\n\n const mtime = getJsonlMtime(repoRoot);\n if (mtime !== null) {\n setLastSyncMtime(database, mtime);\n }\n}\n\n/**\n * Sync SQLite index if JSONL has changed.\n * Gracefully degrades: returns false immediately if SQLite unavailable.\n * @param repoRoot - Absolute path to repository root\n * @param options - Sync options\n * @returns true if sync was performed, false otherwise\n */\nexport async function syncIfNeeded(\n repoRoot: string,\n options: SyncOptions = {}\n): Promise<boolean> {\n if (!isSqliteAvailable()) {\n logDegradationWarning();\n return false;\n }\n\n const { force = false } = options;\n const jsonlMtime = getJsonlMtime(repoRoot);\n if (jsonlMtime === null && !force) {\n return false;\n }\n\n const database = openDb(repoRoot);\n if (!database) return false;\n\n const lastSyncMtime = getLastSyncMtime(database);\n const needsRebuild = force || lastSyncMtime === null || (jsonlMtime !== null && jsonlMtime > lastSyncMtime);\n\n if (needsRebuild) {\n await rebuildIndex(repoRoot);\n return true;\n }\n\n return false;\n}\n","/**\n * SQLite search operations using FTS5 full-text search.\n */\n\nimport type { Lesson } from '../../types.js';\n\nimport type { LessonRow, RetrievalStat } from './types.js';\nimport { logDegradationWarning } from './availability.js';\nimport { openDb } from './connection.js';\n\n/**\n * Convert a database row to a Lesson object.\n * @param row - Database row\n * @returns Lesson object\n */\nfunction rowToLesson(row: LessonRow): Lesson {\n const lesson: Lesson = {\n id: row.id,\n type: row.type as 'quick' | 'full',\n trigger: row.trigger,\n insight: row.insight,\n tags: row.tags ? row.tags.split(',').filter(Boolean) : [],\n source: row.source as Lesson['source'],\n context: JSON.parse(row.context) as Lesson['context'],\n supersedes: JSON.parse(row.supersedes) as string[],\n related: JSON.parse(row.related) as string[],\n created: row.created,\n confirmed: row.confirmed === 1,\n };\n\n if (row.evidence !== null) lesson.evidence = row.evidence;\n if (row.severity !== null) lesson.severity = row.severity as 'high' | 'medium' | 'low';\n if (row.deleted === 1) lesson.deleted = true;\n if (row.retrieval_count > 0) lesson.retrievalCount = row.retrieval_count;\n if (row.invalidated_at !== null) lesson.invalidatedAt = row.invalidated_at;\n if (row.invalidation_reason !== null) lesson.invalidationReason = row.invalidation_reason;\n if (row.citation_file !== null) {\n lesson.citation = {\n file: row.citation_file,\n ...(row.citation_line !== null && { line: row.citation_line }),\n ...(row.citation_commit !== null && { commit: row.citation_commit }),\n };\n }\n if (row.compaction_level !== null && row.compaction_level !== 0) {\n lesson.compactionLevel = row.compaction_level as 0 | 1 | 2;\n }\n if (row.compacted_at !== null) lesson.compactedAt = row.compacted_at;\n if (row.last_retrieved !== null) lesson.lastRetrieved = row.last_retrieved;\n\n return lesson;\n}\n\n/**\n * Increment retrieval count for lessons.\n * Gracefully degrades: no-op if SQLite unavailable.\n * @param repoRoot - Absolute path to repository root\n * @param lessonIds - IDs of retrieved lessons\n */\nexport function incrementRetrievalCount(repoRoot: string, lessonIds: string[]): void {\n if (lessonIds.length === 0) return;\n\n const database = openDb(repoRoot);\n if (!database) {\n logDegradationWarning();\n return;\n }\n\n const now = new Date().toISOString();\n\n const update = database.prepare(`\n UPDATE lessons\n SET retrieval_count = retrieval_count + 1,\n last_retrieved = ?\n WHERE id = ?\n `);\n\n const updateMany = database.transaction((ids: string[]) => {\n for (const id of ids) {\n update.run(now, id);\n }\n });\n\n updateMany(lessonIds);\n}\n\n/**\n * Search lessons using FTS5 full-text search.\n * Does NOT degrade gracefully: throws error if SQLite unavailable.\n * @param repoRoot - Absolute path to repository root\n * @param query - FTS5 query string\n * @param limit - Maximum number of results\n * @returns Matching lessons\n * @throws Error if SQLite unavailable (FTS5 required)\n */\nexport async function searchKeyword(\n repoRoot: string,\n query: string,\n limit: number\n): Promise<Lesson[]> {\n const database = openDb(repoRoot);\n if (!database) {\n throw new Error(\n 'Keyword search requires SQLite (FTS5 required). ' +\n 'Install native build tools or use vector search instead.'\n );\n }\n\n const countResult = database.prepare('SELECT COUNT(*) as cnt FROM lessons').get() as {\n cnt: number;\n };\n if (countResult.cnt === 0) return [];\n\n const rows = database\n .prepare(\n `\n SELECT l.*\n FROM lessons l\n JOIN lessons_fts fts ON l.rowid = fts.rowid\n WHERE lessons_fts MATCH ?\n AND l.invalidated_at IS NULL\n LIMIT ?\n `\n )\n .all(query, limit) as LessonRow[];\n\n if (rows.length > 0) {\n incrementRetrievalCount(repoRoot, rows.map((r) => r.id));\n }\n\n return rows.map(rowToLesson);\n}\n\n/**\n * Get retrieval statistics for all lessons.\n * Gracefully degrades: returns empty array if SQLite unavailable.\n * @param repoRoot - Absolute path to repository root\n * @returns Array of retrieval statistics\n */\nexport function getRetrievalStats(repoRoot: string): RetrievalStat[] {\n const database = openDb(repoRoot);\n if (!database) {\n logDegradationWarning();\n return [];\n }\n\n const rows = database\n .prepare('SELECT id, retrieval_count, last_retrieved FROM lessons')\n .all() as Array<{ id: string; retrieval_count: number; last_retrieved: string | null }>;\n\n return rows.map((row) => ({\n id: row.id,\n count: row.retrieval_count,\n lastRetrieved: row.last_retrieved,\n }));\n}\n","/**\n * Shared utility functions for the Learning Agent.\n */\n\n/** Milliseconds per day for time calculations */\nexport const MS_PER_DAY = 24 * 60 * 60 * 1000;\n\n/**\n * Calculate the age of a lesson in days from its created date.\n *\n * @param lesson - Object with a created field (ISO8601 string)\n * @returns Age in days (integer, rounded down)\n */\nexport function getLessonAgeDays(lesson: { created: string }): number {\n const created = new Date(lesson.created).getTime();\n const now = Date.now();\n return Math.floor((now - created) / MS_PER_DAY);\n}\n","/**\n * Embedding model resolution using node-llama-cpp's built-in resolver.\n *\n * Uses resolveModelFile for automatic download and caching.\n * Model is stored in ~/.node-llama-cpp/models/ by default.\n */\n\nimport { existsSync } from 'node:fs';\nimport { homedir } from 'node:os';\nimport { join } from 'node:path';\nimport { resolveModelFile } from 'node-llama-cpp';\n\n/**\n * HuggingFace URI for EmbeddingGemma-300M (Q4_0 quantization).\n *\n * - Size: ~278MB\n * - Dimensions: 768 (default), supports MRL truncation to 512/256/128\n * - Context: 2048 tokens\n */\nexport const MODEL_URI = 'hf:ggml-org/embeddinggemma-300M-qat-q4_0-GGUF/embeddinggemma-300M-qat-Q4_0.gguf';\n\n/**\n * Expected model filename after download.\n * node-llama-cpp uses format: hf_{org}_{filename}\n */\nexport const MODEL_FILENAME = 'hf_ggml-org_embeddinggemma-300M-qat-Q4_0.gguf';\n\n/** Default model directory used by node-llama-cpp */\nconst DEFAULT_MODEL_DIR = join(homedir(), '.node-llama-cpp', 'models');\n\n/**\n * Check if the embedding model is available locally.\n *\n * @returns true if model file exists\n */\nexport function isModelAvailable(): boolean {\n return existsSync(join(DEFAULT_MODEL_DIR, MODEL_FILENAME));\n}\n\n/**\n * Resolve the embedding model path, downloading if necessary.\n *\n * Uses node-llama-cpp's resolveModelFile for automatic download with progress.\n *\n * @param options - Optional configuration\n * @param options.cli - Show download progress in console (default: true)\n * @returns Path to the resolved model file\n *\n * @example\n * ```typescript\n * const modelPath = await resolveModel();\n * const llama = await getLlama();\n * const model = await llama.loadModel({ modelPath });\n * ```\n */\nexport async function resolveModel(options: { cli?: boolean } = {}): Promise<string> {\n const { cli = true } = options;\n return resolveModelFile(MODEL_URI, { cli });\n}\n","/**\n * Text embedding via node-llama-cpp with EmbeddingGemma model\n *\n * **Resource lifecycle:**\n * - Model is loaded lazily on first embedding call (~150MB in memory)\n * - Once loaded, the model remains in memory until `unloadEmbedding()` is called\n * - Loading is slow (~1-3s); keeping loaded improves subsequent call performance\n *\n * **Memory usage:**\n * - Embedding model: ~150MB RAM when loaded\n * - Embeddings themselves: ~3KB per embedding (768 dimensions x 4 bytes)\n *\n * @see {@link unloadEmbedding} for releasing memory\n * @see {@link getEmbedding} for the lazy-loading mechanism\n */\n\nimport { getLlama, LlamaEmbeddingContext } from 'node-llama-cpp';\n\nimport { isModelAvailable, resolveModel } from './model.js';\n\n/** Singleton embedding context */\nlet embeddingContext: LlamaEmbeddingContext | null = null;\n\n/**\n * Get the LlamaEmbeddingContext instance for generating embeddings.\n *\n * **Lazy loading behavior:**\n * - First call loads the embedding model (~150MB) into memory\n * - Loading takes ~1-3 seconds depending on hardware\n * - Subsequent calls return the cached instance immediately\n * - Downloads model automatically if not present\n *\n * **Resource lifecycle:**\n * - Once loaded, model stays in memory until `unloadEmbedding()` is called\n * - For CLI commands: typically load once, use, then unload on exit\n * - For long-running processes: keep loaded for performance\n *\n * @returns The singleton embedding context\n * @throws Error if model download fails\n *\n * @example\n * ```typescript\n * // Direct usage (prefer embedText for simple cases)\n * const ctx = await getEmbedding();\n * const result = await ctx.getEmbeddingFor('some text');\n *\n * // Ensure cleanup\n * process.on('exit', () => unloadEmbedding());\n * ```\n *\n * @see {@link embedText} for simpler text-to-vector conversion\n * @see {@link unloadEmbedding} for releasing memory\n */\nexport async function getEmbedding(): Promise<LlamaEmbeddingContext> {\n if (embeddingContext) return embeddingContext;\n\n // Resolve model path (downloads if needed)\n const modelPath = await resolveModel({ cli: true });\n\n // Load llama and model\n const llama = await getLlama();\n const model = await llama.loadModel({ modelPath });\n embeddingContext = await model.createEmbeddingContext();\n\n return embeddingContext;\n}\n\n/**\n * Unload the embedding context to free memory (~150MB).\n *\n * **Resource lifecycle:**\n * - Disposes the underlying LlamaEmbeddingContext\n * - Releases ~150MB of RAM used by the model\n * - After unloading, subsequent embedding calls will reload the model\n *\n * **When to call:**\n * - At the end of CLI commands to ensure clean process exit\n * - In memory-constrained environments after batch processing\n * - Before process exit in graceful shutdown handlers\n * - When switching to a different model (if supported in future)\n *\n * **Best practices:**\n * - For single-operation scripts: call before exit\n * - For daemon/server processes: call in shutdown handler\n * - Not needed between embedding calls in the same process\n *\n * @example\n * ```typescript\n * // CLI command pattern\n * try {\n * const embedding = await embedText('some text');\n * // ... use embedding\n * } finally {\n * unloadEmbedding();\n * closeDb();\n * }\n *\n * // Graceful shutdown pattern\n * process.on('SIGTERM', () => {\n * unloadEmbedding();\n * closeDb();\n * process.exit(0);\n * });\n * ```\n *\n * @see {@link getEmbedding} for loading the model\n * @see {@link closeDb} for database cleanup (often used together)\n */\nexport function unloadEmbedding(): void {\n if (embeddingContext) {\n embeddingContext.dispose();\n embeddingContext = null;\n }\n}\n\n/**\n * Embed a single text string into a vector.\n *\n * **Lazy loading:** First call loads the embedding model (~150MB, ~1-3s).\n * Subsequent calls use the cached model and complete in milliseconds.\n *\n * @param text - The text to embed\n * @returns A 768-dimensional vector (number[])\n * @throws Error if model download fails\n *\n * @example\n * ```typescript\n * const vector = await embedText('TypeScript error handling');\n * console.log(vector.length); // 768\n *\n * // Remember to clean up when done\n * unloadEmbedding();\n * ```\n *\n * @see {@link embedTexts} for batch embedding\n * @see {@link unloadEmbedding} for releasing memory\n */\nexport async function embedText(text: string): Promise<number[]> {\n const ctx = await getEmbedding();\n const result = await ctx.getEmbeddingFor(text);\n return Array.from(result.vector);\n}\n\n/**\n * Embed multiple texts into vectors.\n *\n * **Lazy loading:** First call loads the embedding model (~150MB, ~1-3s).\n * Subsequent calls use the cached model.\n *\n * **Performance:** More efficient than calling `embedText` in a loop\n * when processing multiple texts, as model loading happens only once.\n *\n * @param texts - Array of texts to embed\n * @returns Array of 768-dimensional vectors, same order as input\n * @throws Error if model download fails\n *\n * @example\n * ```typescript\n * const texts = ['first text', 'second text'];\n * const vectors = await embedTexts(texts);\n * console.log(vectors.length); // 2\n * console.log(vectors[0].length); // 768\n *\n * // Remember to clean up when done\n * unloadEmbedding();\n * ```\n *\n * @see {@link embedText} for single text embedding\n * @see {@link unloadEmbedding} for releasing memory\n */\nexport async function embedTexts(texts: string[]): Promise<number[][]> {\n if (texts.length === 0) return [];\n\n const ctx = await getEmbedding();\n const results: number[][] = [];\n\n for (const text of texts) {\n const result = await ctx.getEmbeddingFor(text);\n results.push(Array.from(result.vector));\n }\n\n return results;\n}\n\n// Re-export isModelAvailable for test utilities\nexport { isModelAvailable };\n","/**\n * Vector search with cosine similarity\n *\n * Embeds query text and ranks lessons by semantic similarity.\n * Uses SQLite cache to avoid recomputing embeddings.\n */\n\nimport { embedText } from '../embeddings/index.js';\nimport { contentHash, getCachedEmbedding, readLessons, setCachedEmbedding } from '../storage/index.js';\nimport type { Lesson } from '../types.js';\n\n/**\n * Calculate cosine similarity between two vectors.\n * Returns value between -1 (opposite) and 1 (identical).\n */\nexport function cosineSimilarity(a: number[], b: number[]): number {\n if (a.length !== b.length) {\n throw new Error('Vectors must have same length');\n }\n\n let dotProduct = 0;\n let normA = 0;\n let normB = 0;\n\n for (let i = 0; i < a.length; i++) {\n dotProduct += a[i]! * b[i]!;\n normA += a[i]! * a[i]!;\n normB += b[i]! * b[i]!;\n }\n\n const magnitude = Math.sqrt(normA) * Math.sqrt(normB);\n if (magnitude === 0) return 0;\n\n return dotProduct / magnitude;\n}\n\n/** Lesson with similarity score */\nexport interface ScoredLesson {\n lesson: Lesson;\n score: number;\n}\n\n/** Options for vector search */\nexport interface SearchVectorOptions {\n /** Maximum number of results to return (default: 10) */\n limit?: number;\n}\n\n/** Default number of results to return */\nconst DEFAULT_LIMIT = 10;\n\n/**\n * Search lessons by vector similarity to query text.\n * Returns top N lessons sorted by similarity score (descending).\n * Uses embedding cache to avoid recomputing embeddings.\n */\nexport async function searchVector(\n repoRoot: string,\n query: string,\n options?: SearchVectorOptions\n): Promise<ScoredLesson[]> {\n const limit = options?.limit ?? DEFAULT_LIMIT;\n // Read all lessons\n const { lessons } = await readLessons(repoRoot);\n if (lessons.length === 0) return [];\n\n // Embed the query\n const queryVector = await embedText(query);\n\n // Score each lesson, skipping invalidated ones\n const scored: ScoredLesson[] = [];\n for (const lesson of lessons) {\n // Skip invalidated lessons\n if (lesson.invalidatedAt) continue;\n\n const lessonText = `${lesson.trigger} ${lesson.insight}`;\n const hash = contentHash(lesson.trigger, lesson.insight);\n\n // Try cache first\n let lessonVector = getCachedEmbedding(repoRoot, lesson.id, hash);\n\n if (!lessonVector) {\n // Cache miss - compute and store\n lessonVector = await embedText(lessonText);\n setCachedEmbedding(repoRoot, lesson.id, lessonVector, hash);\n }\n\n const score = cosineSimilarity(queryVector, lessonVector);\n scored.push({ lesson, score });\n }\n\n // Sort by score descending and take top N\n scored.sort((a, b) => b.score - a.score);\n return scored.slice(0, limit);\n}\n","/**\n * Multi-factor lesson ranking system\n *\n * Combines vector similarity with semantic boosts:\n * - Severity: high=1.5, medium=1.0, low=0.8\n * - Recency: 1.2 for lessons ≤30 days old\n * - Confirmation: 1.3 for confirmed lessons\n */\n\nimport type { Lesson } from '../types.js';\nimport { getLessonAgeDays } from '../utils.js';\n\nimport type { ScoredLesson } from './vector.js';\n\n/** Lesson with final ranked score */\nexport interface RankedLesson extends ScoredLesson {\n finalScore?: number;\n}\n\nconst RECENCY_THRESHOLD_DAYS = 30;\nconst HIGH_SEVERITY_BOOST = 1.5;\nconst MEDIUM_SEVERITY_BOOST = 1.0;\nconst LOW_SEVERITY_BOOST = 0.8;\nconst RECENCY_BOOST = 1.2;\nconst CONFIRMATION_BOOST = 1.3;\n\n/**\n * Calculate severity boost based on lesson severity.\n * Lessons without severity get 1.0 (medium boost).\n */\nexport function severityBoost(lesson: Lesson): number {\n switch (lesson.severity) {\n case 'high':\n return HIGH_SEVERITY_BOOST;\n case 'medium':\n return MEDIUM_SEVERITY_BOOST;\n case 'low':\n return LOW_SEVERITY_BOOST;\n default:\n return MEDIUM_SEVERITY_BOOST;\n }\n}\n\n/**\n * Calculate recency boost based on lesson age.\n * Lessons ≤30 days old get 1.2, older get 1.0.\n */\nexport function recencyBoost(lesson: Lesson): number {\n const ageDays = getLessonAgeDays(lesson);\n return ageDays <= RECENCY_THRESHOLD_DAYS ? RECENCY_BOOST : 1.0;\n}\n\n/**\n * Calculate confirmation boost.\n * Confirmed lessons get 1.3, unconfirmed get 1.0.\n */\nexport function confirmationBoost(lesson: Lesson): number {\n return lesson.confirmed ? CONFIRMATION_BOOST : 1.0;\n}\n\n/**\n * Calculate combined score for a lesson.\n * score = vectorSimilarity * severity * recency * confirmation\n */\nexport function calculateScore(lesson: Lesson, vectorSimilarity: number): number {\n return (\n vectorSimilarity * severityBoost(lesson) * recencyBoost(lesson) * confirmationBoost(lesson)\n );\n}\n\n/**\n * Rank lessons by combined score.\n * Returns new array sorted by finalScore descending.\n */\nexport function rankLessons(lessons: ScoredLesson[]): RankedLesson[] {\n return lessons\n .map((scored) => ({\n ...scored,\n finalScore: calculateScore(scored.lesson, scored.score),\n }))\n .sort((a, b) => (b.finalScore ?? 0) - (a.finalScore ?? 0));\n}\n","/**\n * Quality filters for lesson capture\n *\n * Filters to ensure lessons are:\n * - Novel (not duplicate)\n * - Specific (not vague)\n * - Actionable (contains action words)\n */\n\nimport { searchKeyword, syncIfNeeded } from '../storage/index.js';\n\n/** Default similarity threshold for duplicate detection */\nconst DEFAULT_SIMILARITY_THRESHOLD = 0.8;\n\n/** Result of novelty check */\nexport interface NoveltyResult {\n novel: boolean;\n reason?: string;\n existingId?: string;\n}\n\n/** Options for novelty check */\nexport interface NoveltyOptions {\n threshold?: number;\n}\n\n/**\n * Check if an insight is novel (not a duplicate of existing lessons).\n * Uses keyword search to find potentially similar lessons.\n */\nexport async function isNovel(\n repoRoot: string,\n insight: string,\n options: NoveltyOptions = {}\n): Promise<NoveltyResult> {\n const threshold = options.threshold ?? DEFAULT_SIMILARITY_THRESHOLD;\n\n // Sync index if JSONL has changed\n await syncIfNeeded(repoRoot);\n\n // Extract key words for search (take first 3 significant words)\n const words = insight\n .toLowerCase()\n .replace(/[^a-z0-9\\s]/g, '')\n .split(/\\s+/)\n .filter((w) => w.length > 3)\n .slice(0, 3);\n\n if (words.length === 0) {\n return { novel: true };\n }\n\n // Search for each word and collect results\n const searchQuery = words.join(' OR ');\n const results = await searchKeyword(repoRoot, searchQuery, 10);\n\n if (results.length === 0) {\n return { novel: true };\n }\n\n // Check similarity using simple word overlap (since we may not have embeddings)\n const insightWords = new Set(insight.toLowerCase().split(/\\s+/));\n\n for (const lesson of results) {\n const lessonWords = new Set(lesson.insight.toLowerCase().split(/\\s+/));\n\n // Calculate Jaccard similarity\n const intersection = [...insightWords].filter((w) => lessonWords.has(w)).length;\n const union = new Set([...insightWords, ...lessonWords]).size;\n const similarity = union > 0 ? intersection / union : 0;\n\n if (similarity >= threshold) {\n return {\n novel: false,\n reason: `Found similar existing lesson: \"${lesson.insight.slice(0, 50)}...\"`,\n existingId: lesson.id,\n };\n }\n\n // Also check exact match\n if (lesson.insight.toLowerCase() === insight.toLowerCase()) {\n return {\n novel: false,\n reason: `Exact duplicate found`,\n existingId: lesson.id,\n };\n }\n }\n\n return { novel: true };\n}\n\n/** Minimum word count for a specific insight */\nconst MIN_WORD_COUNT = 4;\n\n/** Vague patterns that indicate non-specific advice */\nconst VAGUE_PATTERNS = [\n /\\bwrite better\\b/i,\n /\\bbe careful\\b/i,\n /\\bremember to\\b/i,\n /\\bmake sure\\b/i,\n /\\btry to\\b/i,\n /\\bdouble check\\b/i,\n];\n\n/** Generic \"always/never\" phrases (short, lacking specificity) */\nconst GENERIC_IMPERATIVE_PATTERN = /^(always|never)\\s+\\w+(\\s+\\w+){0,2}$/i;\n\n/** Result of specificity check */\nexport interface SpecificityResult {\n specific: boolean;\n reason?: string;\n}\n\n/**\n * Check if an insight is specific enough to be useful.\n * Rejects vague, generic advice that doesn't provide actionable guidance.\n */\nexport function isSpecific(insight: string): SpecificityResult {\n // Check minimum length first\n const words = insight.trim().split(/\\s+/).filter((w) => w.length > 0);\n if (words.length < MIN_WORD_COUNT) {\n return { specific: false, reason: 'Insight is too short to be actionable' };\n }\n\n // Check for vague patterns\n for (const pattern of VAGUE_PATTERNS) {\n if (pattern.test(insight)) {\n return { specific: false, reason: 'Insight matches a vague pattern' };\n }\n }\n\n // Check for generic \"Always X\" or \"Never X\" phrases\n if (GENERIC_IMPERATIVE_PATTERN.test(insight)) {\n return { specific: false, reason: 'Insight matches a vague pattern' };\n }\n\n return { specific: true };\n}\n\n/** Action word patterns that indicate actionable guidance */\nconst ACTION_PATTERNS = [\n /\\buse\\s+.+\\s+instead\\s+of\\b/i, // \"use X instead of Y\"\n /\\bprefer\\s+.+\\s+(over|to)\\b/i, // \"prefer X over Y\" or \"prefer X to Y\"\n /\\balways\\s+.+\\s+when\\b/i, // \"always X when Y\"\n /\\bnever\\s+.+\\s+without\\b/i, // \"never X without Y\"\n /\\bavoid\\s+(using\\s+)?\\w+/i, // \"avoid X\" or \"avoid using X\"\n /\\bcheck\\s+.+\\s+before\\b/i, // \"check X before Y\"\n /^(run|use|add|remove|install|update|configure|set|enable|disable)\\s+/i, // Imperative commands at start\n];\n\n/** Result of actionability check */\nexport interface ActionabilityResult {\n actionable: boolean;\n reason?: string;\n}\n\n/**\n * Check if an insight contains actionable guidance.\n * Returns false for pure observations or questions.\n */\nexport function isActionable(insight: string): ActionabilityResult {\n // Check for action patterns\n for (const pattern of ACTION_PATTERNS) {\n if (pattern.test(insight)) {\n return { actionable: true };\n }\n }\n\n return { actionable: false, reason: 'Insight lacks clear action guidance' };\n}\n\n/** Result of combined quality check */\nexport interface ProposeResult {\n shouldPropose: boolean;\n reason?: string;\n}\n\n/**\n * Combined quality check for lesson proposals.\n * Returns true only if insight is novel, specific, AND actionable.\n */\nexport async function shouldPropose(\n repoRoot: string,\n insight: string\n): Promise<ProposeResult> {\n // Check specificity first (fast, no DB)\n const specificResult = isSpecific(insight);\n if (!specificResult.specific) {\n return { shouldPropose: false, reason: specificResult.reason };\n }\n\n // Check actionability (fast, no DB)\n const actionableResult = isActionable(insight);\n if (!actionableResult.actionable) {\n return { shouldPropose: false, reason: actionableResult.reason };\n }\n\n // Check novelty (requires DB lookup)\n const noveltyResult = await isNovel(repoRoot, insight);\n if (!noveltyResult.novel) {\n return { shouldPropose: false, reason: noveltyResult.reason };\n }\n\n return { shouldPropose: true };\n}\n","/**\n * Trigger detection for automatic lesson capture\n *\n * Detects patterns that indicate potential learning opportunities:\n * - User corrections\n * - Self-corrections\n * - Test failures\n */\n\nimport type { Context } from '../types.js';\n\n/** Signal data for correction detection */\nexport interface CorrectionSignal {\n messages: string[];\n context: Context;\n}\n\n/** Detected correction result */\nexport interface DetectedCorrection {\n trigger: string;\n correctionMessage: string;\n context: Context;\n}\n\n/** User correction patterns */\nconst USER_CORRECTION_PATTERNS = [\n /\\bno\\b[,.]?\\s/i, // \"no, ...\" or \"no ...\"\n /\\bwrong\\b/i, // \"wrong\"\n /\\bactually\\b/i, // \"actually...\"\n /\\bnot that\\b/i, // \"not that\"\n /\\bi meant\\b/i, // \"I meant\"\n];\n\n/**\n * Detect user correction signals in conversation.\n *\n * Looks for patterns that indicate the user is correcting Claude's\n * understanding or actions.\n *\n * @param signals - Messages and context to analyze\n * @returns Detected correction or null if none found\n */\nexport function detectUserCorrection(signals: CorrectionSignal): DetectedCorrection | null {\n const { messages, context } = signals;\n\n if (messages.length < 2) {\n return null;\n }\n\n // Check later messages for correction patterns\n for (let i = 1; i < messages.length; i++) {\n const message = messages[i];\n if (!message) continue;\n\n for (const pattern of USER_CORRECTION_PATTERNS) {\n if (pattern.test(message)) {\n return {\n trigger: `User correction during ${context.intent}`,\n correctionMessage: message,\n context,\n };\n }\n }\n }\n\n return null;\n}\n\n/** Edit history entry */\nexport interface EditEntry {\n file: string;\n success: boolean;\n timestamp: number;\n}\n\n/** Edit history for self-correction detection */\nexport interface EditHistory {\n edits: EditEntry[];\n}\n\n/** Detected self-correction */\nexport interface DetectedSelfCorrection {\n file: string;\n trigger: string;\n}\n\n/**\n * Detect self-correction patterns in edit history.\n *\n * Looks for edit→fail→re-edit patterns on the same file,\n * which indicate Claude had to correct its own work.\n *\n * @param history - Edit history to analyze\n * @returns Detected self-correction or null if none found\n */\nexport function detectSelfCorrection(history: EditHistory): DetectedSelfCorrection | null {\n const { edits } = history;\n\n if (edits.length < 3) {\n return null;\n }\n\n // Look for edit→fail→re-edit pattern on same file\n for (let i = 0; i <= edits.length - 3; i++) {\n const first = edits[i];\n const second = edits[i + 1];\n const third = edits[i + 2];\n\n if (!first || !second || !third) continue;\n\n // Pattern: success → fail → success on same file\n if (\n first.file === second.file &&\n second.file === third.file &&\n first.success &&\n !second.success &&\n third.success\n ) {\n return {\n file: first.file,\n trigger: `Self-correction on ${first.file}`,\n };\n }\n }\n\n return null;\n}\n\n/** Test result for failure detection */\nexport interface TestResult {\n passed: boolean;\n output: string;\n testFile: string;\n}\n\n/** Detected test failure */\nexport interface DetectedTestFailure {\n testFile: string;\n errorOutput: string;\n trigger: string;\n}\n\n/**\n * Detect test failure patterns.\n *\n * When tests fail, this creates a potential learning opportunity\n * if the failure is later fixed.\n *\n * @param testResult - Test result to analyze\n * @returns Detected test failure or null if tests passed\n */\nexport function detectTestFailure(testResult: TestResult): DetectedTestFailure | null {\n if (testResult.passed) {\n return null;\n }\n\n // Extract first meaningful error line for trigger\n const lines = testResult.output.split('\\n').filter((line) => line.trim().length > 0);\n const errorLine = lines.find((line) => /error|fail|assert/i.test(line)) ?? lines[0] ?? '';\n\n return {\n testFile: testResult.testFile,\n errorOutput: testResult.output,\n trigger: `Test failure in ${testResult.testFile}: ${errorLine.slice(0, 100)}`,\n };\n}\n","/**\n * Session-start lesson retrieval\n *\n * Loads high-severity lessons at the start of a session.\n * No vector search - just filter by severity and recency.\n */\n\nimport { readLessons } from '../storage/index.js';\nimport type { Lesson, Severity } from '../types.js';\n\n/** Default number of lessons to load at session start */\nconst DEFAULT_LIMIT = 5;\n\n/** A full lesson with severity field present */\ntype FullLesson = Lesson & { type: 'full'; severity: Severity };\n\n/**\n * Type guard to check if a lesson is a full lesson with severity\n */\nfunction isFullLesson(lesson: Lesson): lesson is FullLesson {\n return lesson.type === 'full' && lesson.severity !== undefined;\n}\n\n/**\n * Load high-severity lessons for session start.\n *\n * Returns confirmed, high-severity lessons sorted by recency.\n * These are the most important lessons to surface at the start\n * of a coding session.\n *\n * @param repoRoot - Repository root directory\n * @param limit - Maximum number of lessons to return (default: 5)\n * @returns Array of high-severity lessons, most recent first\n */\nexport async function loadSessionLessons(\n repoRoot: string,\n limit: number = DEFAULT_LIMIT\n): Promise<FullLesson[]> {\n const { lessons: allLessons } = await readLessons(repoRoot);\n\n // Filter for high-severity, confirmed, full lessons (excluding invalidated)\n const highSeverityLessons = allLessons.filter(\n (lesson): lesson is FullLesson =>\n isFullLesson(lesson) &&\n lesson.severity === 'high' &&\n lesson.confirmed &&\n !lesson.invalidatedAt\n );\n\n // Sort by recency (most recent first)\n highSeverityLessons.sort((a, b) => {\n const dateA = new Date(a.created).getTime();\n const dateB = new Date(b.created).getTime();\n return dateB - dateA;\n });\n\n // Return top N\n return highSeverityLessons.slice(0, limit);\n}\n","/**\n * Plan-time lesson retrieval\n *\n * Retrieves relevant lessons when planning an implementation.\n * Uses vector search to find semantically similar lessons.\n */\n\nimport { rankLessons, searchVector, type RankedLesson, type ScoredLesson } from '../search/index.js';\n\n/** Default number of lessons to retrieve */\nconst DEFAULT_LIMIT = 5;\n\n/** Result of plan-time retrieval */\nexport interface PlanRetrievalResult {\n lessons: RankedLesson[];\n message: string;\n}\n\n/**\n * Retrieve relevant lessons for a plan.\n *\n * Uses vector search to find semantically similar lessons,\n * then applies ranking boosts for severity, recency, and confirmation.\n *\n * Hard-fails if embeddings are unavailable (propagates error from embedText).\n *\n * @param repoRoot - Repository root directory\n * @param planText - The plan text to search against\n * @param limit - Maximum number of lessons to return (default: 5)\n * @returns Ranked lessons and formatted message\n */\nexport async function retrieveForPlan(\n repoRoot: string,\n planText: string,\n limit: number = DEFAULT_LIMIT\n): Promise<PlanRetrievalResult> {\n // Get lessons by vector similarity (will throw if embeddings unavailable)\n const scored = await searchVector(repoRoot, planText, { limit: limit * 2 });\n\n // Apply ranking boosts\n const ranked = rankLessons(scored);\n\n // Take top N after ranking\n const topLessons = ranked.slice(0, limit);\n\n // Format the Lessons Check message\n const message = formatLessonsCheck(topLessons);\n\n return { lessons: topLessons, message };\n}\n\n/**\n * Format a \"Lessons Check\" message for display.\n *\n * This message is intended to be shown at plan-time to remind\n * the developer of relevant lessons before implementation.\n *\n * @param lessons - Ranked lessons to include in the message\n * @returns Formatted message string\n */\nexport function formatLessonsCheck(lessons: ScoredLesson[]): string {\n const header = 'Lessons Check\\n' + '─'.repeat(40);\n\n if (lessons.length === 0) {\n return `${header}\\nNo relevant lessons found for this plan.`;\n }\n\n const lessonLines = lessons.map((l, i) => {\n const bullet = `${i + 1}.`;\n const insight = l.lesson.insight;\n return `${bullet} ${insight}`;\n });\n\n return `${header}\\n${lessonLines.join('\\n')}`;\n}\n","/**\n * Learning Agent - Repository-scoped learning system for Claude Code\n *\n * This package helps Claude Code learn from mistakes and avoid repeating them.\n * It captures lessons during coding sessions and retrieves relevant lessons\n * when planning new work.\n *\n * ## Quick Start\n *\n * ```typescript\n * import { appendLesson, retrieveForPlan, loadSessionLessons } from 'learning-agent';\n *\n * // At session start, load high-severity lessons\n * const criticalLessons = await loadSessionLessons(repoRoot);\n *\n * // When planning, retrieve relevant lessons\n * const { lessons, message } = await retrieveForPlan(repoRoot, planText);\n *\n * // When capturing a lesson\n * await appendLesson(repoRoot, lesson);\n * ```\n *\n * ## Hook Integration\n *\n * Add to your `.claude/settings.json`:\n *\n * ```json\n * {\n * \"hooks\": {\n * \"session_start\": \"npx learning-agent load-session\",\n * \"pre_tool\": \"npx learning-agent check-plan\"\n * }\n * }\n * ```\n *\n * ## Resource Management\n *\n * This library manages two heavyweight resources that require cleanup:\n *\n * ### SQLite Database\n * - **Acquired:** Lazily on first database operation (search, rebuild, etc.)\n * - **Memory:** Minimal (~few KB for connection, index cached by OS)\n * - **Cleanup:** Call `closeDb()` before process exit\n *\n * ### Embedding Model\n * - **Acquired:** Lazily on first embedding call (embedText, embedTexts, searchVector)\n * - **Memory:** ~150MB RAM for the EmbeddingGemma model\n * - **Cleanup:** Call `unloadEmbedding()` before process exit\n *\n * ### Recommended Cleanup Pattern\n *\n * ```typescript\n * import { closeDb, unloadEmbedding } from 'learning-agent';\n *\n * // For CLI commands - use try/finally\n * async function main() {\n * try {\n * // ... your code that uses learning-agent\n * } finally {\n * unloadEmbedding();\n * closeDb();\n * }\n * }\n *\n * // For long-running processes - use shutdown handlers\n * process.on('SIGTERM', () => {\n * unloadEmbedding();\n * closeDb();\n * process.exit(0);\n * });\n * process.on('SIGINT', () => {\n * unloadEmbedding();\n * closeDb();\n * process.exit(0);\n * });\n * ```\n *\n * **Note:** Failing to clean up will not corrupt data, but may cause:\n * - Memory leaks in long-running processes\n * - Unclean process exits (warnings in some environments)\n *\n * @see {@link closeDb} for database cleanup\n * @see {@link unloadEmbedding} for embedding model cleanup\n * @module learning-agent\n */\n\n/**\n * Package version - must match package.json.\n * Update this when releasing a new version.\n */\nexport const VERSION = '0.2.3';\n\n// Storage API (JSONL source of truth + SQLite index)\nexport {\n appendLesson,\n closeDb,\n DB_PATH,\n LESSONS_PATH,\n readLessons,\n rebuildIndex,\n searchKeyword,\n} from './storage/index.js';\nexport type { ParseError, ReadLessonsOptions, ReadLessonsResult } from './storage/index.js';\n\n// Embeddings API\nexport {\n embedText,\n embedTexts,\n getEmbedding,\n isModelAvailable,\n MODEL_FILENAME,\n MODEL_URI,\n resolveModel,\n unloadEmbedding,\n} from './embeddings/index.js';\n\n// Search API (vector similarity + ranking)\nexport {\n calculateScore,\n confirmationBoost,\n cosineSimilarity,\n rankLessons,\n recencyBoost,\n searchVector,\n severityBoost,\n} from './search/index.js';\nexport type { RankedLesson, ScoredLesson, SearchVectorOptions } from './search/index.js';\n\n// Capture API (quality filters + trigger detection)\nexport {\n detectSelfCorrection,\n detectTestFailure,\n detectUserCorrection,\n isActionable,\n isNovel,\n isSpecific,\n shouldPropose,\n} from './capture/index.js';\nexport type {\n ActionabilityResult,\n CorrectionSignal,\n DetectedCorrection,\n DetectedSelfCorrection,\n DetectedTestFailure,\n EditEntry,\n EditHistory,\n NoveltyOptions,\n NoveltyResult,\n ProposeResult,\n SpecificityResult,\n TestResult,\n} from './capture/index.js';\n\n// Retrieval API (session + plan time)\nexport { formatLessonsCheck, loadSessionLessons, retrieveForPlan } from './retrieval/index.js';\nexport type { PlanRetrievalResult } from './retrieval/index.js';\n\n// Types and schemas\nexport {\n generateId,\n LessonSchema,\n LessonTypeSchema,\n TombstoneSchema,\n} from './types.js';\nexport type {\n Lesson,\n LessonType,\n Tombstone,\n Source,\n Severity,\n Context,\n} from './types.js';\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "learning-agent",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.3",
|
|
4
4
|
"description": "Repository-scoped learning system for Claude Code",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -27,6 +27,18 @@
|
|
|
27
27
|
"url": "https://github.com/Nathandela/learning_agent/issues"
|
|
28
28
|
},
|
|
29
29
|
"homepage": "https://github.com/Nathandela/learning_agent#readme",
|
|
30
|
+
"scripts": {
|
|
31
|
+
"build": "tsup",
|
|
32
|
+
"dev": "tsup --watch",
|
|
33
|
+
"test": "vitest run",
|
|
34
|
+
"test:fast": "vitest run --exclude 'src/cli/**'",
|
|
35
|
+
"test:watch": "vitest",
|
|
36
|
+
"test:changed": "vitest run --changed HEAD~1",
|
|
37
|
+
"test:all": "pnpm download-model && vitest run",
|
|
38
|
+
"lint": "tsc --noEmit",
|
|
39
|
+
"download-model": "node ./dist/cli.js download-model",
|
|
40
|
+
"prepublishOnly": "pnpm build"
|
|
41
|
+
},
|
|
30
42
|
"keywords": [
|
|
31
43
|
"claude",
|
|
32
44
|
"learning",
|
|
@@ -35,6 +47,7 @@
|
|
|
35
47
|
],
|
|
36
48
|
"author": "Nathan Delacrétaz",
|
|
37
49
|
"license": "MIT",
|
|
50
|
+
"packageManager": "pnpm@10.28.2",
|
|
38
51
|
"engines": {
|
|
39
52
|
"node": ">=20"
|
|
40
53
|
},
|
|
@@ -45,7 +58,6 @@
|
|
|
45
58
|
"@vitest/coverage-v8": "2.1.9",
|
|
46
59
|
"fast-check": "4.5.3",
|
|
47
60
|
"tsup": "^8.0.0",
|
|
48
|
-
"tsx": "4.21.0",
|
|
49
61
|
"typescript": "^5.3.0",
|
|
50
62
|
"vitest": "^2.0.0"
|
|
51
63
|
},
|
|
@@ -56,13 +68,11 @@
|
|
|
56
68
|
"node-llama-cpp": "^3.0.0",
|
|
57
69
|
"zod": "^3.22.0"
|
|
58
70
|
},
|
|
59
|
-
"
|
|
60
|
-
"
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
"lint": "tsc --noEmit",
|
|
66
|
-
"download-model": "node ./dist/cli.js download-model"
|
|
71
|
+
"pnpm": {
|
|
72
|
+
"onlyBuiltDependencies": [
|
|
73
|
+
"better-sqlite3",
|
|
74
|
+
"node-llama-cpp",
|
|
75
|
+
"esbuild"
|
|
76
|
+
]
|
|
67
77
|
}
|
|
68
|
-
}
|
|
78
|
+
}
|