@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.
- package/bin/cmk-compress-lazy.mjs +59 -0
- package/bin/cmk-daily-distill.mjs +67 -0
- package/bin/cmk-weekly-curate.mjs +56 -0
- package/bin/cmk.mjs +12 -0
- package/package.json +50 -0
- package/src/audit-log.mjs +103 -0
- package/src/auto-extract.mjs +742 -0
- package/src/capture-prompt.mjs +61 -0
- package/src/capture-turn.mjs +273 -0
- package/src/claude-md.mjs +212 -0
- package/src/compress-session.mjs +349 -0
- package/src/compressor.mjs +376 -0
- package/src/conflict-queue.mjs +796 -0
- package/src/cooldown.mjs +61 -0
- package/src/daily-distill.mjs +252 -0
- package/src/doctor.mjs +528 -0
- package/src/forget.mjs +335 -0
- package/src/frontmatter.mjs +73 -0
- package/src/import-anthropic-memory.mjs +266 -0
- package/src/index-db.mjs +154 -0
- package/src/index-rebuild.mjs +597 -0
- package/src/index.mjs +90 -0
- package/src/inject-context.mjs +484 -0
- package/src/install.mjs +327 -0
- package/src/lazy-compress.mjs +326 -0
- package/src/lock-discipline.mjs +166 -0
- package/src/mcp-server.mjs +498 -0
- package/src/memory-write.mjs +565 -0
- package/src/merge-facts.mjs +213 -0
- package/src/observe-edit.mjs +87 -0
- package/src/platform-commands.mjs +138 -0
- package/src/poison-guard.mjs +245 -0
- package/src/privacy.mjs +21 -0
- package/src/provenance.mjs +217 -0
- package/src/register-crons.mjs +354 -0
- package/src/reindex.mjs +134 -0
- package/src/repair.mjs +316 -0
- package/src/result-shapes.mjs +155 -0
- package/src/review-queue.mjs +345 -0
- package/src/roll.mjs +115 -0
- package/src/scratchpad.mjs +335 -0
- package/src/search.mjs +311 -0
- package/src/subcommands.mjs +1252 -0
- package/src/tier-paths.mjs +74 -0
- package/src/transcripts.mjs +234 -0
- package/src/trust.mjs +226 -0
- package/src/weekly-curate.mjs +454 -0
- package/src/write-fact.mjs +205 -0
- package/template/.claude/hooks/pre-tool-memory.js +78 -0
- package/template/.claude/hooks/transcript-capture.js +69 -0
- package/template/.claude/settings.json +27 -0
- package/template/.claude/skills/memory-write/SKILL.md +117 -0
- package/template/.gitignore.fragment +12 -0
- package/template/CLAUDE.md.template +49 -0
- package/template/docs/journey/journey-log.md.template +292 -0
- package/template/local/machine-paths.md.template +37 -0
- package/template/local/overrides.md.template +36 -0
- package/template/project/.index/.gitkeep +0 -0
- package/template/project/MEMORY.md.template +47 -0
- package/template/project/SOUL.md.template +35 -0
- package/template/project/memory/INDEX.md.template +47 -0
- package/template/project/memory/archive/superseded/.gitkeep +0 -0
- package/template/project/memory/archive/tombstones/.gitkeep +0 -0
- package/template/project/queues/.gitkeep +0 -0
- package/template/project/sessions/.gitkeep +0 -0
- package/template/project/transcripts/.gitkeep +0 -0
- package/template/support/cron-jobs/daily-memory-distill.md +15 -0
- package/template/support/cron-jobs/nightly-memsearch-index.md +17 -0
- package/template/support/cron-jobs/weekly-memory-curator.md +15 -0
- package/template/support/milvus-deploy/README.md +57 -0
- package/template/support/milvus-deploy/docker-compose.yml +66 -0
- package/template/support/scripts/auto-extract-memory.sh +102 -0
- package/template/support/scripts/memsearch-index-with-flush.sh +59 -0
- package/template/support/scripts/refresh-distill-timestamp.py +35 -0
- package/template/support/scripts/register-crons.py +242 -0
- package/template/support/scripts/run-daily-distill.sh +67 -0
- package/template/support/scripts/run-weekly-curate.sh +58 -0
- package/template/user/HABITS.md.template +18 -0
- package/template/user/LESSONS.md.template +18 -0
- package/template/user/USER.md.template +18 -0
- package/template/user/fragments/INDEX.md.template +23 -0
package/src/index-db.mjs
ADDED
|
@@ -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
|
+
}
|