@lh8ppl/claude-memory-kit 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (81) hide show
  1. package/bin/cmk-compress-lazy.mjs +59 -0
  2. package/bin/cmk-daily-distill.mjs +67 -0
  3. package/bin/cmk-weekly-curate.mjs +56 -0
  4. package/bin/cmk.mjs +12 -0
  5. package/package.json +50 -0
  6. package/src/audit-log.mjs +103 -0
  7. package/src/auto-extract.mjs +742 -0
  8. package/src/capture-prompt.mjs +61 -0
  9. package/src/capture-turn.mjs +273 -0
  10. package/src/claude-md.mjs +212 -0
  11. package/src/compress-session.mjs +349 -0
  12. package/src/compressor.mjs +376 -0
  13. package/src/conflict-queue.mjs +796 -0
  14. package/src/cooldown.mjs +61 -0
  15. package/src/daily-distill.mjs +252 -0
  16. package/src/doctor.mjs +528 -0
  17. package/src/forget.mjs +335 -0
  18. package/src/frontmatter.mjs +73 -0
  19. package/src/import-anthropic-memory.mjs +266 -0
  20. package/src/index-db.mjs +154 -0
  21. package/src/index-rebuild.mjs +597 -0
  22. package/src/index.mjs +90 -0
  23. package/src/inject-context.mjs +484 -0
  24. package/src/install.mjs +327 -0
  25. package/src/lazy-compress.mjs +326 -0
  26. package/src/lock-discipline.mjs +166 -0
  27. package/src/mcp-server.mjs +498 -0
  28. package/src/memory-write.mjs +565 -0
  29. package/src/merge-facts.mjs +213 -0
  30. package/src/observe-edit.mjs +87 -0
  31. package/src/platform-commands.mjs +138 -0
  32. package/src/poison-guard.mjs +245 -0
  33. package/src/privacy.mjs +21 -0
  34. package/src/provenance.mjs +217 -0
  35. package/src/register-crons.mjs +354 -0
  36. package/src/reindex.mjs +134 -0
  37. package/src/repair.mjs +316 -0
  38. package/src/result-shapes.mjs +155 -0
  39. package/src/review-queue.mjs +345 -0
  40. package/src/roll.mjs +115 -0
  41. package/src/scratchpad.mjs +335 -0
  42. package/src/search.mjs +311 -0
  43. package/src/subcommands.mjs +1252 -0
  44. package/src/tier-paths.mjs +74 -0
  45. package/src/transcripts.mjs +234 -0
  46. package/src/trust.mjs +226 -0
  47. package/src/weekly-curate.mjs +454 -0
  48. package/src/write-fact.mjs +205 -0
  49. package/template/.claude/hooks/pre-tool-memory.js +78 -0
  50. package/template/.claude/hooks/transcript-capture.js +69 -0
  51. package/template/.claude/settings.json +27 -0
  52. package/template/.claude/skills/memory-write/SKILL.md +117 -0
  53. package/template/.gitignore.fragment +12 -0
  54. package/template/CLAUDE.md.template +49 -0
  55. package/template/docs/journey/journey-log.md.template +292 -0
  56. package/template/local/machine-paths.md.template +37 -0
  57. package/template/local/overrides.md.template +36 -0
  58. package/template/project/.index/.gitkeep +0 -0
  59. package/template/project/MEMORY.md.template +47 -0
  60. package/template/project/SOUL.md.template +35 -0
  61. package/template/project/memory/INDEX.md.template +47 -0
  62. package/template/project/memory/archive/superseded/.gitkeep +0 -0
  63. package/template/project/memory/archive/tombstones/.gitkeep +0 -0
  64. package/template/project/queues/.gitkeep +0 -0
  65. package/template/project/sessions/.gitkeep +0 -0
  66. package/template/project/transcripts/.gitkeep +0 -0
  67. package/template/support/cron-jobs/daily-memory-distill.md +15 -0
  68. package/template/support/cron-jobs/nightly-memsearch-index.md +17 -0
  69. package/template/support/cron-jobs/weekly-memory-curator.md +15 -0
  70. package/template/support/milvus-deploy/README.md +57 -0
  71. package/template/support/milvus-deploy/docker-compose.yml +66 -0
  72. package/template/support/scripts/auto-extract-memory.sh +102 -0
  73. package/template/support/scripts/memsearch-index-with-flush.sh +59 -0
  74. package/template/support/scripts/refresh-distill-timestamp.py +35 -0
  75. package/template/support/scripts/register-crons.py +242 -0
  76. package/template/support/scripts/run-daily-distill.sh +67 -0
  77. package/template/support/scripts/run-weekly-curate.sh +58 -0
  78. package/template/user/HABITS.md.template +18 -0
  79. package/template/user/LESSONS.md.template +18 -0
  80. package/template/user/USER.md.template +18 -0
  81. package/template/user/fragments/INDEX.md.template +23 -0
@@ -0,0 +1,154 @@
1
+ // SQLite index-DB schema + open function (Task 28, T-024).
2
+ //
3
+ // The kit's search layer (Layer 5, design §9) is a regenerable
4
+ // read-cache at `<projectRoot>/context/.index/memory.db`. Source of
5
+ // truth is always the markdown files under `context/`, `context.local/`,
6
+ // and the user-tier directory — the DB is rebuilt from those any time
7
+ // it diverges (T1 architectural rule).
8
+ //
9
+ // Schema follows design §9.1 verbatim:
10
+ // - `observations` table mirrors every fact bullet from the markdown
11
+ // tier files (id, tier, source_file, source_line, source_sha1,
12
+ // heading_path, body, write_source, trust, created_at, superseded_by,
13
+ // deleted_at).
14
+ // - `observations_fts` FTS5 virtual table provides BM25 keyword search
15
+ // over the body / heading_path / write_source columns. Three sync
16
+ // triggers (after insert / update / delete) keep the FTS5 mirror
17
+ // consistent with the base table without requiring the caller to
18
+ // manage two write paths.
19
+ // - `files` checkpoint table tracks markdown file mtime + sha1, used
20
+ // by Task 29's reindex strategy (boot-time diff, runtime watcher)
21
+ // to skip files that haven't changed since the last index pass.
22
+ //
23
+ // Pragma posture:
24
+ // - `journal_mode=WAL` enables many readers + one writer concurrently
25
+ // (the kit reads from the index during `cmk search` while auto-extract
26
+ // or `cmk reindex` may be writing).
27
+ // - `synchronous=NORMAL` is the sqlite-recommended tradeoff for WAL
28
+ // mode — durability is preserved across checkpoint events but a
29
+ // crash between checkpoints can lose the last few transactions.
30
+ // For a regenerable read-cache this is correct: the user can always
31
+ // `cmk reindex --full` to rebuild.
32
+ //
33
+ // Public boundary (this module):
34
+ // - openIndexDb({projectRoot}) → Database
35
+ // - getIndexDbPath(projectRoot) → string
36
+ // - INDEX_DB_SCHEMA (exported for tests / migration tooling)
37
+ //
38
+ // Not yet in this task:
39
+ // - reindex orchestration (Task 29)
40
+ // - `cmk search` CLI (Task 30)
41
+ // - MCP server (Task 31)
42
+ //
43
+ // Task 28 ships ONLY the schema + open function. Callers in Tasks 29-31
44
+ // will compose on top.
45
+
46
+ import Database from 'better-sqlite3';
47
+ import { mkdirSync } from 'node:fs';
48
+ import { dirname, join } from 'node:path';
49
+
50
+ const INDEX_DB_RELATIVE = ['context', '.index', 'memory.db'];
51
+
52
+ /**
53
+ * Full DDL applied at open time. Idempotent — every CREATE has
54
+ * `IF NOT EXISTS` so reopening an existing DB is a no-op.
55
+ *
56
+ * Kept as a single export so tests can assert that the production
57
+ * open path applied the documented schema (no drift between code
58
+ * and design §9.1).
59
+ */
60
+ export const INDEX_DB_SCHEMA = `
61
+ CREATE TABLE IF NOT EXISTS observations (
62
+ id TEXT PRIMARY KEY,
63
+ tier TEXT NOT NULL CHECK(tier IN ('U','P','L')),
64
+ source_file TEXT NOT NULL,
65
+ source_line INTEGER NOT NULL,
66
+ source_sha1 TEXT NOT NULL,
67
+ heading_path TEXT,
68
+ body TEXT NOT NULL,
69
+ write_source TEXT NOT NULL,
70
+ trust TEXT NOT NULL,
71
+ created_at INTEGER NOT NULL,
72
+ superseded_by TEXT REFERENCES observations(id),
73
+ deleted_at INTEGER
74
+ );
75
+
76
+ CREATE INDEX IF NOT EXISTS idx_observations_tier ON observations(tier);
77
+ CREATE INDEX IF NOT EXISTS idx_observations_trust ON observations(trust);
78
+ CREATE INDEX IF NOT EXISTS idx_observations_created_at ON observations(created_at);
79
+ CREATE INDEX IF NOT EXISTS idx_observations_deleted ON observations(deleted_at) WHERE deleted_at IS NOT NULL;
80
+
81
+ CREATE VIRTUAL TABLE IF NOT EXISTS observations_fts USING fts5(
82
+ body, heading_path, write_source,
83
+ content='observations',
84
+ content_rowid='rowid',
85
+ tokenize='porter unicode61'
86
+ );
87
+
88
+ -- FTS5 external-content sync triggers (sqlite.org/fts5 §4.4.3).
89
+ -- The standard "DELETE FROM fts WHERE rowid = old.rowid" trigger pattern
90
+ -- does NOT work for external-content FTS5 (content='observations') —
91
+ -- FTS5 needs to read the deleted content to remove it from the index,
92
+ -- but the row is gone by the time an AFTER DELETE trigger runs. The
93
+ -- documented escape hatch is the 'delete' / 'delete-all' command — a
94
+ -- sentinel INSERT into the FTS5 table that takes rowid + column values
95
+ -- so FTS5 can compute the delete without re-reading the source row.
96
+ CREATE TRIGGER IF NOT EXISTS obs_after_insert AFTER INSERT ON observations BEGIN
97
+ INSERT INTO observations_fts(rowid, body, heading_path, write_source)
98
+ VALUES (new.rowid, new.body, new.heading_path, new.write_source);
99
+ END;
100
+
101
+ CREATE TRIGGER IF NOT EXISTS obs_after_update AFTER UPDATE ON observations BEGIN
102
+ INSERT INTO observations_fts(observations_fts, rowid, body, heading_path, write_source)
103
+ VALUES ('delete', old.rowid, old.body, old.heading_path, old.write_source);
104
+ INSERT INTO observations_fts(rowid, body, heading_path, write_source)
105
+ VALUES (new.rowid, new.body, new.heading_path, new.write_source);
106
+ END;
107
+
108
+ CREATE TRIGGER IF NOT EXISTS obs_after_delete AFTER DELETE ON observations BEGIN
109
+ INSERT INTO observations_fts(observations_fts, rowid, body, heading_path, write_source)
110
+ VALUES ('delete', old.rowid, old.body, old.heading_path, old.write_source);
111
+ END;
112
+
113
+ CREATE TABLE IF NOT EXISTS files (
114
+ path TEXT PRIMARY KEY,
115
+ mtime INTEGER NOT NULL,
116
+ sha1 TEXT NOT NULL,
117
+ indexed_at INTEGER NOT NULL
118
+ );
119
+ `;
120
+
121
+ /**
122
+ * @param {string} projectRoot
123
+ * @returns {string} absolute path to the kit's index DB for the given project
124
+ */
125
+ export function getIndexDbPath(projectRoot) {
126
+ return join(projectRoot, ...INDEX_DB_RELATIVE);
127
+ }
128
+
129
+ /**
130
+ * Opens (or creates) the index DB for the given project, applies the
131
+ * schema, and sets the documented PRAGMA posture. Idempotent — calling
132
+ * twice against the same projectRoot returns a second handle to the
133
+ * same on-disk database; the schema CREATE IF NOT EXISTS statements
134
+ * are no-ops on the second call.
135
+ *
136
+ * @param {object} opts
137
+ * @param {string} opts.projectRoot
138
+ * @param {string} [opts.dbPath] override path (test fixture injection)
139
+ * @returns {import('better-sqlite3').Database}
140
+ */
141
+ export function openIndexDb({ projectRoot, dbPath } = {}) {
142
+ const path = dbPath ?? getIndexDbPath(projectRoot);
143
+ mkdirSync(dirname(path), { recursive: true });
144
+ const db = new Database(path);
145
+ // WAL + NORMAL synchronous: design §9.1 posture. WAL allows many
146
+ // readers + one writer concurrently; NORMAL trades a small durability
147
+ // window (between WAL checkpoints) for write throughput, acceptable
148
+ // for a regenerable read-cache.
149
+ db.pragma('journal_mode = WAL');
150
+ db.pragma('synchronous = NORMAL');
151
+ // Apply schema (idempotent CREATE IF NOT EXISTS).
152
+ db.exec(INDEX_DB_SCHEMA);
153
+ return db;
154
+ }