akm-cli 0.7.1 → 0.7.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.
Files changed (38) hide show
  1. package/CHANGELOG.md +35 -0
  2. package/dist/cli.js +62 -16
  3. package/dist/commands/history.js +2 -7
  4. package/dist/commands/info.js +2 -2
  5. package/dist/commands/installed-stashes.js +45 -1
  6. package/dist/commands/search.js +2 -2
  7. package/dist/commands/show.js +4 -19
  8. package/dist/commands/source-add.js +1 -1
  9. package/dist/core/common.js +16 -1
  10. package/dist/core/config.js +18 -3
  11. package/dist/indexer/db-search.js +33 -39
  12. package/dist/indexer/db.js +51 -1
  13. package/dist/indexer/graph-extraction.js +5 -3
  14. package/dist/indexer/indexer.js +334 -121
  15. package/dist/indexer/manifest.js +18 -23
  16. package/dist/indexer/memory-inference.js +47 -58
  17. package/dist/indexer/metadata.js +253 -21
  18. package/dist/indexer/search-source.js +11 -5
  19. package/dist/llm/client.js +61 -1
  20. package/dist/llm/embedder.js +8 -5
  21. package/dist/llm/embedders/local.js +8 -2
  22. package/dist/llm/embedders/remote.js +4 -2
  23. package/dist/llm/graph-extract.js +4 -4
  24. package/dist/llm/memory-infer.js +61 -33
  25. package/dist/llm/metadata-enhance.js +2 -2
  26. package/dist/output/cli-hints.js +5 -2
  27. package/dist/output/renderers.js +22 -49
  28. package/dist/registry/build-index.js +13 -18
  29. package/dist/setup/setup.js +238 -96
  30. package/dist/sources/providers/git.js +14 -2
  31. package/dist/sources/providers/website.js +4 -460
  32. package/dist/sources/website-ingest.js +470 -0
  33. package/dist/wiki/wiki.js +11 -1
  34. package/dist/workflows/parser.js +19 -4
  35. package/dist/workflows/runs.js +3 -3
  36. package/docs/README.md +10 -3
  37. package/docs/migration/release-notes/0.7.0.md +22 -0
  38. package/package.json +5 -2
@@ -9,7 +9,7 @@ import { cosineSimilarity } from "../llm/embedders/types";
9
9
  import { buildSearchFields } from "./search-fields";
10
10
  import { ensureUsageEventsSchema } from "./usage-events";
11
11
  // ── Constants ───────────────────────────────────────────────────────────────
12
- export const DB_VERSION = 9;
12
+ export const DB_VERSION = 10;
13
13
  export const EMBEDDING_DIM = 384;
14
14
  // ── Database lifecycle ──────────────────────────────────────────────────────
15
15
  export function openDatabase(dbPath, options) {
@@ -29,6 +29,17 @@ export function openDatabase(dbPath, options) {
29
29
  warnIfVecMissing(db, { once: true });
30
30
  return db;
31
31
  }
32
+ export function openExistingDatabase(dbPath) {
33
+ const resolvedPath = dbPath ?? getDbPath();
34
+ const db = new Database(resolvedPath);
35
+ db.exec("PRAGMA journal_mode = WAL");
36
+ db.exec("PRAGMA busy_timeout = 5000");
37
+ db.exec("PRAGMA foreign_keys = ON");
38
+ // Existing-DB callers must not mutate schema or embedding metadata on open,
39
+ // but some paths still need write access to usage_events and other tables.
40
+ loadVecExtension(db);
41
+ return db;
42
+ }
32
43
  export function closeDatabase(db) {
33
44
  db.close();
34
45
  }
@@ -163,6 +174,15 @@ function ensureSchema(db, embeddingDim) {
163
174
  updated_at TEXT NOT NULL DEFAULT (datetime('now')),
164
175
  FOREIGN KEY (entry_id) REFERENCES entries(id) ON DELETE CASCADE
165
176
  );
177
+ `);
178
+ db.exec(`
179
+ CREATE TABLE IF NOT EXISTS index_dir_state (
180
+ dir_path TEXT PRIMARY KEY,
181
+ file_set_hash TEXT NOT NULL,
182
+ file_mtime_max_ms REAL NOT NULL,
183
+ reason TEXT NOT NULL,
184
+ updated_at TEXT NOT NULL
185
+ );
166
186
  `);
167
187
  // FTS-dirty queue. Created here (not lazily on first upsert) so the
168
188
  // per-entry write path doesn't issue a CREATE TABLE IF NOT EXISTS on
@@ -261,6 +281,7 @@ function handleVersionUpgrade(db) {
261
281
  db.exec("DROP TABLE IF EXISTS embeddings");
262
282
  db.exec("DROP TABLE IF EXISTS entries_vec");
263
283
  db.exec("DROP TABLE IF EXISTS entries_fts");
284
+ db.exec("DROP TABLE IF EXISTS index_dir_state");
264
285
  db.exec("DROP INDEX IF EXISTS idx_entries_dir");
265
286
  db.exec("DROP INDEX IF EXISTS idx_entries_type");
266
287
  db.exec("DROP TABLE IF EXISTS entries");
@@ -332,6 +353,35 @@ export function getMeta(db, key) {
332
353
  export function setMeta(db, key, value) {
333
354
  db.prepare("INSERT OR REPLACE INTO index_meta (key, value) VALUES (?, ?)").run(key, value);
334
355
  }
356
+ export function getIndexDirState(db, dirPath) {
357
+ const row = db
358
+ .prepare("SELECT dir_path, file_set_hash, file_mtime_max_ms, reason, updated_at FROM index_dir_state WHERE dir_path = ?")
359
+ .get(dirPath);
360
+ if (!row)
361
+ return undefined;
362
+ return {
363
+ dirPath: row.dir_path,
364
+ fileSetHash: row.file_set_hash,
365
+ fileMtimeMaxMs: row.file_mtime_max_ms,
366
+ reason: row.reason,
367
+ updatedAt: row.updated_at,
368
+ };
369
+ }
370
+ export function upsertIndexDirState(db, state) {
371
+ db.prepare(`INSERT INTO index_dir_state (dir_path, file_set_hash, file_mtime_max_ms, reason, updated_at)
372
+ VALUES (?, ?, ?, ?, ?)
373
+ ON CONFLICT(dir_path) DO UPDATE SET
374
+ file_set_hash = excluded.file_set_hash,
375
+ file_mtime_max_ms = excluded.file_mtime_max_ms,
376
+ reason = excluded.reason,
377
+ updated_at = excluded.updated_at`).run(state.dirPath, state.fileSetHash, state.fileMtimeMaxMs, state.reason, new Date().toISOString());
378
+ }
379
+ export function deleteIndexDirState(db, dirPath) {
380
+ db.prepare("DELETE FROM index_dir_state WHERE dir_path = ?").run(dirPath);
381
+ }
382
+ export function deleteIndexDirStatesByStashDir(db, stashDir) {
383
+ db.prepare("DELETE FROM index_dir_state WHERE dir_path = ? OR dir_path LIKE ?").run(stashDir, `${stashDir}${path.sep}%`);
384
+ }
335
385
  // ── Entry operations ────────────────────────────────────────────────────────
336
386
  /**
337
387
  * Insert or update an entry in the `entries` table. Returns the row id.
@@ -70,7 +70,7 @@ const EMPTY_RESULT = {
70
70
  * to an empty no-op result, leaving any existing `graph.json` untouched on
71
71
  * disk.
72
72
  */
73
- export async function runGraphExtractionPass(config, sources) {
73
+ export async function runGraphExtractionPass(config, sources, signal) {
74
74
  // Gate 1 — locked feature flag (§14). Defaults to enabled; only an
75
75
  // explicit `false` disables the pass entirely.
76
76
  if (config.llm?.features?.graph_extraction === false)
@@ -94,7 +94,9 @@ export async function runGraphExtractionPass(config, sources) {
94
94
  let totalEntities = 0;
95
95
  let totalRelations = 0;
96
96
  for (const candidate of eligible) {
97
- const extraction = await extractGraphFromBody(llmConfig, candidate.body);
97
+ if (signal?.aborted)
98
+ break;
99
+ const extraction = await extractGraphFromBody(llmConfig, candidate.body, signal);
98
100
  if (extraction.entities.length === 0)
99
101
  continue;
100
102
  nodes.push({
@@ -134,7 +136,7 @@ export async function runGraphExtractionPass(config, sources) {
134
136
  * same one the rest of the indexer uses: `<stashRoot>/<type>/...`.
135
137
  *
136
138
  * Inferred-child memories (frontmatter `inferred: true`) are skipped — they
137
- * are atomic facts already, with no internal graph structure worth
139
+ * are already derived summaries, with no additional internal graph structure worth
138
140
  * extracting.
139
141
  *
140
142
  * Exported for direct unit testing.