mindlore 0.3.5 → 0.4.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 (37) hide show
  1. package/README.md +9 -6
  2. package/dist/scripts/init.js +9 -1
  3. package/dist/scripts/init.js.map +1 -1
  4. package/dist/scripts/lib/episodes.d.ts +66 -0
  5. package/dist/scripts/lib/episodes.d.ts.map +1 -0
  6. package/dist/scripts/lib/episodes.js +180 -0
  7. package/dist/scripts/lib/episodes.js.map +1 -0
  8. package/dist/scripts/mindlore-episodes.d.ts +12 -0
  9. package/dist/scripts/mindlore-episodes.d.ts.map +1 -0
  10. package/dist/scripts/mindlore-episodes.js +193 -0
  11. package/dist/scripts/mindlore-episodes.js.map +1 -0
  12. package/dist/tests/diary.test.d.ts +6 -0
  13. package/dist/tests/diary.test.d.ts.map +1 -0
  14. package/dist/tests/diary.test.js +169 -0
  15. package/dist/tests/diary.test.js.map +1 -0
  16. package/dist/tests/episodes-inject.test.d.ts +6 -0
  17. package/dist/tests/episodes-inject.test.d.ts.map +1 -0
  18. package/dist/tests/episodes-inject.test.js +161 -0
  19. package/dist/tests/episodes-inject.test.js.map +1 -0
  20. package/dist/tests/episodes.test.d.ts +5 -0
  21. package/dist/tests/episodes.test.d.ts.map +1 -0
  22. package/dist/tests/episodes.test.js +254 -0
  23. package/dist/tests/episodes.test.js.map +1 -0
  24. package/dist/tests/helpers/db.d.ts +7 -0
  25. package/dist/tests/helpers/db.d.ts.map +1 -1
  26. package/dist/tests/helpers/db.js +20 -1
  27. package/dist/tests/helpers/db.js.map +1 -1
  28. package/hooks/lib/mindlore-common.cjs +119 -0
  29. package/hooks/mindlore-post-read.cjs +11 -2
  30. package/hooks/mindlore-read-guard.cjs +12 -4
  31. package/hooks/mindlore-search.cjs +36 -0
  32. package/hooks/mindlore-session-end.cjs +105 -22
  33. package/hooks/mindlore-session-focus.cjs +28 -1
  34. package/package.json +1 -1
  35. package/skills/mindlore-log/SKILL.md +74 -14
  36. package/skills/mindlore-query/SKILL.md +8 -5
  37. package/templates/config.json +4 -1
@@ -0,0 +1,254 @@
1
+ "use strict";
2
+ /**
3
+ * Episodes CRUD tests — v0.4.0 episodic memory.
4
+ */
5
+ var __importDefault = (this && this.__importDefault) || function (mod) {
6
+ return (mod && mod.__esModule) ? mod : { "default": mod };
7
+ };
8
+ Object.defineProperty(exports, "__esModule", { value: true });
9
+ const path_1 = __importDefault(require("path"));
10
+ const episodes_js_1 = require("../scripts/lib/episodes.js");
11
+ const db_js_1 = require("./helpers/db.js");
12
+ const better_sqlite3_1 = __importDefault(require("better-sqlite3"));
13
+ let env;
14
+ let db;
15
+ beforeEach(() => {
16
+ env = (0, db_js_1.createEpisodesTestEnv)('episodes');
17
+ db = env.db;
18
+ });
19
+ afterEach(() => {
20
+ (0, db_js_1.destroyEpisodesTestEnv)(env);
21
+ });
22
+ describe('episodes table', () => {
23
+ test('hasEpisodesTable returns true after creation', () => {
24
+ expect((0, episodes_js_1.hasEpisodesTable)(db)).toBe(true);
25
+ });
26
+ test('hasEpisodesTable returns false on fresh db', () => {
27
+ const freshPath = path_1.default.join(env.tmpDir, 'fresh.db');
28
+ const freshDb = new better_sqlite3_1.default(freshPath);
29
+ expect((0, episodes_js_1.hasEpisodesTable)(freshDb)).toBe(false);
30
+ freshDb.close();
31
+ });
32
+ test('ensureEpisodesTable is idempotent', () => {
33
+ (0, episodes_js_1.ensureEpisodesTable)(db);
34
+ (0, episodes_js_1.ensureEpisodesTable)(db);
35
+ expect((0, episodes_js_1.hasEpisodesTable)(db)).toBe(true);
36
+ });
37
+ });
38
+ describe('createEpisode', () => {
39
+ test('creates episode with minimal input', () => {
40
+ const ep = (0, episodes_js_1.createEpisode)(db, {
41
+ kind: 'session',
42
+ summary: 'Test session episode',
43
+ });
44
+ expect(ep.id).toMatch(/^ep-/);
45
+ expect(ep.kind).toBe('session');
46
+ expect(ep.scope).toBe('project');
47
+ expect(ep.summary).toBe('Test session episode');
48
+ expect(ep.status).toBe('active');
49
+ expect(ep.source).toBe('manual');
50
+ expect(ep.created_at).toBeTruthy();
51
+ });
52
+ test('creates episode with all fields', () => {
53
+ const ep = (0, episodes_js_1.createEpisode)(db, {
54
+ kind: 'decision',
55
+ summary: 'Chose SQLite over JSONL',
56
+ scope: 'global',
57
+ project: 'mindlore',
58
+ body: '## Reason\nBetter query support.',
59
+ tags: 'architecture, storage',
60
+ entities: ['scripts/lib/episodes.ts', 'mindlore.db'],
61
+ parent_id: null,
62
+ source: 'diary',
63
+ });
64
+ expect(ep.kind).toBe('decision');
65
+ expect(ep.scope).toBe('global');
66
+ expect(ep.project).toBe('mindlore');
67
+ expect(ep.body).toContain('Reason');
68
+ expect(ep.tags).toBe('architecture, storage');
69
+ expect(JSON.parse(ep.entities)).toEqual(['scripts/lib/episodes.ts', 'mindlore.db']);
70
+ expect(ep.source).toBe('diary');
71
+ });
72
+ test('generates unique IDs', () => {
73
+ const ep1 = (0, episodes_js_1.createEpisode)(db, { kind: 'event', summary: 'Event 1' });
74
+ const ep2 = (0, episodes_js_1.createEpisode)(db, { kind: 'event', summary: 'Event 2' });
75
+ expect(ep1.id).not.toBe(ep2.id);
76
+ });
77
+ });
78
+ describe('getEpisode', () => {
79
+ test('retrieves existing episode', () => {
80
+ const created = (0, episodes_js_1.createEpisode)(db, { kind: 'learning', summary: 'Learned X' });
81
+ const fetched = (0, episodes_js_1.getEpisode)(db, created.id);
82
+ expect(fetched).toBeDefined();
83
+ expect(fetched.summary).toBe('Learned X');
84
+ });
85
+ test('returns undefined for non-existent ID', () => {
86
+ expect((0, episodes_js_1.getEpisode)(db, 'ep-nonexistent')).toBeUndefined();
87
+ });
88
+ });
89
+ describe('queryEpisodes', () => {
90
+ beforeEach(() => {
91
+ (0, episodes_js_1.createEpisode)(db, { kind: 'session', summary: 'Session 1', project: 'mindlore', source: 'hook' });
92
+ (0, episodes_js_1.createEpisode)(db, { kind: 'decision', summary: 'Decision 1', project: 'mindlore', source: 'diary' });
93
+ (0, episodes_js_1.createEpisode)(db, { kind: 'session', summary: 'Session 2', project: 'kastell', source: 'hook' });
94
+ (0, episodes_js_1.createEpisode)(db, { kind: 'learning', summary: 'Learning 1', scope: 'global', source: 'reflect' });
95
+ });
96
+ test('returns all active episodes by default', () => {
97
+ const results = (0, episodes_js_1.queryEpisodes)(db);
98
+ expect(results).toHaveLength(4);
99
+ });
100
+ test('filters by kind', () => {
101
+ const sessions = (0, episodes_js_1.queryEpisodes)(db, { kind: 'session' });
102
+ expect(sessions).toHaveLength(2);
103
+ expect(sessions.every(e => e.kind === 'session')).toBe(true);
104
+ });
105
+ test('filters by project', () => {
106
+ const mindlore = (0, episodes_js_1.queryEpisodes)(db, { project: 'mindlore' });
107
+ expect(mindlore).toHaveLength(2);
108
+ });
109
+ test('filters by scope', () => {
110
+ const globals = (0, episodes_js_1.queryEpisodes)(db, { scope: 'global' });
111
+ expect(globals).toHaveLength(1);
112
+ expect(globals[0].summary).toBe('Learning 1');
113
+ });
114
+ test('filters by source', () => {
115
+ const hooks = (0, episodes_js_1.queryEpisodes)(db, { source: 'hook' });
116
+ expect(hooks).toHaveLength(2);
117
+ });
118
+ test('respects limit', () => {
119
+ const limited = (0, episodes_js_1.queryEpisodes)(db, { limit: 2 });
120
+ expect(limited).toHaveLength(2);
121
+ });
122
+ test('orders by created_at DESC', () => {
123
+ const all = (0, episodes_js_1.queryEpisodes)(db);
124
+ for (let i = 1; i < all.length; i++) {
125
+ expect(all[i - 1].created_at >= all[i].created_at).toBe(true);
126
+ }
127
+ });
128
+ test('filters by since date', () => {
129
+ const future = (0, episodes_js_1.queryEpisodes)(db, { since: '2099-01-01T00:00:00.000Z' });
130
+ expect(future).toHaveLength(0);
131
+ const past = (0, episodes_js_1.queryEpisodes)(db, { since: '2020-01-01T00:00:00.000Z' });
132
+ expect(past).toHaveLength(4);
133
+ });
134
+ });
135
+ describe('supersede', () => {
136
+ test('marks old episode as superseded and links new one', () => {
137
+ const old = (0, episodes_js_1.createEpisode)(db, { kind: 'decision', summary: 'Use JSONL' });
138
+ const newer = (0, episodes_js_1.supersede)(db, old.id, { kind: 'decision', summary: 'Use SQLite' });
139
+ expect(newer.supersedes).toBe(old.id);
140
+ expect(newer.status).toBe('active');
141
+ const oldRefresh = (0, episodes_js_1.getEpisode)(db, old.id);
142
+ expect(oldRefresh.status).toBe('superseded');
143
+ });
144
+ test('superseded episodes excluded from default query', () => {
145
+ const old = (0, episodes_js_1.createEpisode)(db, { kind: 'decision', summary: 'Old decision' });
146
+ (0, episodes_js_1.supersede)(db, old.id, { kind: 'decision', summary: 'New decision' });
147
+ const active = (0, episodes_js_1.queryEpisodes)(db, { kind: 'decision' });
148
+ expect(active).toHaveLength(1);
149
+ expect(active[0].summary).toBe('New decision');
150
+ });
151
+ });
152
+ describe('deleteEpisode', () => {
153
+ test('soft-deletes an episode', () => {
154
+ const ep = (0, episodes_js_1.createEpisode)(db, { kind: 'event', summary: 'To delete' });
155
+ const result = (0, episodes_js_1.deleteEpisode)(db, ep.id);
156
+ expect(result).toBe(true);
157
+ const deleted = (0, episodes_js_1.getEpisode)(db, ep.id);
158
+ expect(deleted.status).toBe('deleted');
159
+ });
160
+ test('returns false for already deleted', () => {
161
+ const ep = (0, episodes_js_1.createEpisode)(db, { kind: 'event', summary: 'Double delete' });
162
+ (0, episodes_js_1.deleteEpisode)(db, ep.id);
163
+ expect((0, episodes_js_1.deleteEpisode)(db, ep.id)).toBe(false);
164
+ });
165
+ test('deleted episodes excluded from default query', () => {
166
+ const ep = (0, episodes_js_1.createEpisode)(db, { kind: 'event', summary: 'Deleted event' });
167
+ (0, episodes_js_1.deleteEpisode)(db, ep.id);
168
+ const results = (0, episodes_js_1.queryEpisodes)(db);
169
+ expect(results).toHaveLength(0);
170
+ });
171
+ });
172
+ describe('countEpisodes', () => {
173
+ test('counts all active episodes', () => {
174
+ (0, episodes_js_1.createEpisode)(db, { kind: 'session', summary: 'S1' });
175
+ (0, episodes_js_1.createEpisode)(db, { kind: 'session', summary: 'S2' });
176
+ expect((0, episodes_js_1.countEpisodes)(db)).toBe(2);
177
+ });
178
+ test('counts by project', () => {
179
+ (0, episodes_js_1.createEpisode)(db, { kind: 'session', summary: 'S1', project: 'mindlore' });
180
+ (0, episodes_js_1.createEpisode)(db, { kind: 'session', summary: 'S2', project: 'kastell' });
181
+ expect((0, episodes_js_1.countEpisodes)(db, 'mindlore')).toBe(1);
182
+ });
183
+ test('excludes deleted episodes', () => {
184
+ const ep = (0, episodes_js_1.createEpisode)(db, { kind: 'session', summary: 'S1' });
185
+ (0, episodes_js_1.createEpisode)(db, { kind: 'session', summary: 'S2' });
186
+ (0, episodes_js_1.deleteEpisode)(db, ep.id);
187
+ expect((0, episodes_js_1.countEpisodes)(db)).toBe(1);
188
+ });
189
+ });
190
+ describe('mirrorToFts', () => {
191
+ test('calls insertFn with correct FTS5 entry shape', () => {
192
+ const ep = (0, episodes_js_1.createEpisode)(db, {
193
+ kind: 'decision',
194
+ summary: 'Use SQLite',
195
+ body: 'Better queries',
196
+ tags: 'architecture',
197
+ project: 'mindlore',
198
+ });
199
+ const calls = [];
200
+ const mockInsert = (_db, entry) => {
201
+ calls.push(entry);
202
+ };
203
+ (0, episodes_js_1.mirrorToFts)(db, ep, mockInsert);
204
+ expect(calls).toHaveLength(1);
205
+ const entry = calls[0];
206
+ expect(entry.type).toBe('episode');
207
+ expect(entry.category).toBe('episodes');
208
+ expect(entry.slug).toBe(`ep-${ep.id}`);
209
+ expect(entry.title).toBe('Use SQLite');
210
+ expect(entry.content).toContain('Use SQLite');
211
+ expect(entry.content).toContain('Better queries');
212
+ expect(entry.tags).toContain('decision');
213
+ expect(entry.tags).toContain('architecture');
214
+ expect(entry.project).toBe('mindlore');
215
+ });
216
+ });
217
+ describe('hook helpers (CJS)', () => {
218
+ // Test the CJS module functions used by hooks
219
+ const { insertBareEpisode, queryRecentEpisodes, generateEpisodeId, } = require('../hooks/lib/mindlore-common.cjs');
220
+ test('generateEpisodeId returns ep- prefixed string', () => {
221
+ const id = generateEpisodeId();
222
+ expect(id).toMatch(/^ep-[a-z0-9]+-[a-f0-9]{12}$/);
223
+ });
224
+ test('insertBareEpisode writes to DB', () => {
225
+ const id = insertBareEpisode(db, {
226
+ kind: 'session',
227
+ summary: 'Bare session from hook',
228
+ project: 'mindlore',
229
+ source: 'hook',
230
+ });
231
+ expect(id).toMatch(/^ep-/);
232
+ const ep = (0, episodes_js_1.getEpisode)(db, id);
233
+ expect(ep).toBeDefined();
234
+ expect(ep.summary).toBe('Bare session from hook');
235
+ expect(ep.source).toBe('hook');
236
+ });
237
+ test('queryRecentEpisodes returns latest N episodes', () => {
238
+ insertBareEpisode(db, { kind: 'session', summary: 'S1', project: 'mindlore' });
239
+ insertBareEpisode(db, { kind: 'decision', summary: 'D1', project: 'mindlore' });
240
+ insertBareEpisode(db, { kind: 'session', summary: 'S2', project: 'kastell' });
241
+ const recent = queryRecentEpisodes(db, { project: 'mindlore', limit: 2 });
242
+ expect(recent).toHaveLength(2);
243
+ expect(recent[0]).toHaveProperty('kind');
244
+ expect(recent[0]).toHaveProperty('summary');
245
+ expect(recent[0]).toHaveProperty('created_at');
246
+ });
247
+ test('queryRecentEpisodes respects project filter', () => {
248
+ insertBareEpisode(db, { kind: 'session', summary: 'S1', project: 'mindlore' });
249
+ insertBareEpisode(db, { kind: 'session', summary: 'S2', project: 'kastell' });
250
+ const mindlore = queryRecentEpisodes(db, { project: 'mindlore' });
251
+ expect(mindlore).toHaveLength(1);
252
+ });
253
+ });
254
+ //# sourceMappingURL=episodes.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"episodes.test.js","sourceRoot":"","sources":["../../tests/episodes.test.ts"],"names":[],"mappings":"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"}
@@ -16,6 +16,13 @@ export interface FtsEntry {
16
16
  project?: string | null;
17
17
  }
18
18
  export declare function insertFts(db: Database.Database, entry: FtsEntry): void;
19
+ export declare function createTestDbWithEpisodes(dbPath: string): Database.Database;
19
20
  export declare function setupTestDir(testDir: string, subdirs?: string[]): void;
20
21
  export declare function teardownTestDir(testDir: string): void;
22
+ export interface EpisodesTestEnv {
23
+ db: Database.Database;
24
+ tmpDir: string;
25
+ }
26
+ export declare function createEpisodesTestEnv(prefix: string): EpisodesTestEnv;
27
+ export declare function destroyEpisodesTestEnv(env: EpisodesTestEnv): void;
21
28
  //# sourceMappingURL=db.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"db.d.ts","sourceRoot":"","sources":["../../../tests/helpers/db.ts"],"names":[],"mappings":"AAGA,OAAO,QAAQ,MAAM,gBAAgB,CAAC;AAQtC,wBAAgB,MAAM,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAE9C;AAED,wBAAgB,YAAY,CAAC,MAAM,EAAE,MAAM,GAAG,QAAQ,CAAC,QAAQ,CAY9D;AAED,MAAM,WAAW,QAAQ;IACvB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC;IACzC,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CACzB;AAED,wBAAgB,SAAS,CAAC,EAAE,EAAE,QAAQ,CAAC,QAAQ,EAAE,KAAK,EAAE,QAAQ,GAAG,IAAI,CAEtE;AAED,wBAAgB,YAAY,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,EAAE,GAAG,IAAI,CAMtE;AAED,wBAAgB,eAAe,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAErD"}
1
+ {"version":3,"file":"db.d.ts","sourceRoot":"","sources":["../../../tests/helpers/db.ts"],"names":[],"mappings":"AAIA,OAAO,QAAQ,MAAM,gBAAgB,CAAC;AAStC,wBAAgB,MAAM,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAE9C;AAED,wBAAgB,YAAY,CAAC,MAAM,EAAE,MAAM,GAAG,QAAQ,CAAC,QAAQ,CAY9D;AAED,MAAM,WAAW,QAAQ;IACvB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC;IACzC,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CACzB;AAED,wBAAgB,SAAS,CAAC,EAAE,EAAE,QAAQ,CAAC,QAAQ,EAAE,KAAK,EAAE,QAAQ,GAAG,IAAI,CAEtE;AAED,wBAAgB,wBAAwB,CAAC,MAAM,EAAE,MAAM,GAAG,QAAQ,CAAC,QAAQ,CAI1E;AAED,wBAAgB,YAAY,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,EAAE,GAAG,IAAI,CAMtE;AAED,wBAAgB,eAAe,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAErD;AAED,MAAM,WAAW,eAAe;IAC9B,EAAE,EAAE,QAAQ,CAAC,QAAQ,CAAC;IACtB,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,MAAM,GAAG,eAAe,CAKrE;AAED,wBAAgB,sBAAsB,CAAC,GAAG,EAAE,eAAe,GAAG,IAAI,CAGjE"}
@@ -6,14 +6,18 @@ Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.sha256 = sha256;
7
7
  exports.createTestDb = createTestDb;
8
8
  exports.insertFts = insertFts;
9
+ exports.createTestDbWithEpisodes = createTestDbWithEpisodes;
9
10
  exports.setupTestDir = setupTestDir;
10
11
  exports.teardownTestDir = teardownTestDir;
12
+ exports.createEpisodesTestEnv = createEpisodesTestEnv;
13
+ exports.destroyEpisodesTestEnv = destroyEpisodesTestEnv;
11
14
  const fs_1 = __importDefault(require("fs"));
12
15
  const path_1 = __importDefault(require("path"));
16
+ const os_1 = __importDefault(require("os"));
13
17
  const crypto_1 = __importDefault(require("crypto"));
14
18
  const better_sqlite3_1 = __importDefault(require("better-sqlite3"));
15
19
  // Hook'lar .cjs kalıyor — SQL constants'ları oradan import ediyoruz
16
- const { SQL_FTS_CREATE, insertFtsRow } = require('../../hooks/lib/mindlore-common.cjs');
20
+ const { SQL_FTS_CREATE, insertFtsRow, ensureEpisodesTable: ensureEpisodesTableCjs } = require('../../hooks/lib/mindlore-common.cjs');
17
21
  function sha256(content) {
18
22
  return crypto_1.default.createHash('sha256').update(content, 'utf8').digest('hex');
19
23
  }
@@ -33,6 +37,11 @@ function createTestDb(dbPath) {
33
37
  function insertFts(db, entry) {
34
38
  insertFtsRow(db, entry);
35
39
  }
40
+ function createTestDbWithEpisodes(dbPath) {
41
+ const db = createTestDb(dbPath);
42
+ ensureEpisodesTableCjs(db);
43
+ return db;
44
+ }
36
45
  function setupTestDir(testDir, subdirs) {
37
46
  const dirs = subdirs ?? [];
38
47
  fs_1.default.mkdirSync(testDir, { recursive: true });
@@ -43,4 +52,14 @@ function setupTestDir(testDir, subdirs) {
43
52
  function teardownTestDir(testDir) {
44
53
  fs_1.default.rmSync(testDir, { recursive: true, force: true });
45
54
  }
55
+ function createEpisodesTestEnv(prefix) {
56
+ const tmpDir = fs_1.default.mkdtempSync(path_1.default.join(os_1.default.tmpdir(), `mindlore-${prefix}-`));
57
+ const dbPath = path_1.default.join(tmpDir, 'test.db');
58
+ const db = createTestDbWithEpisodes(dbPath);
59
+ return { db, tmpDir };
60
+ }
61
+ function destroyEpisodesTestEnv(env) {
62
+ env.db.close();
63
+ fs_1.default.rmSync(env.tmpDir, { recursive: true, force: true });
64
+ }
46
65
  //# sourceMappingURL=db.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"db.js","sourceRoot":"","sources":["../../../tests/helpers/db.ts"],"names":[],"mappings":";;;;;AAWA,wBAEC;AAED,oCAYC;AAiBD,8BAEC;AAED,oCAMC;AAED,0CAEC;AA1DD,4CAAoB;AACpB,gDAAwB;AACxB,oDAA4B;AAC5B,oEAAsC;AAEtC,oEAAoE;AACpE,MAAM,EAAE,cAAc,EAAE,YAAY,EAAE,GAGlC,OAAO,CAAC,qCAAqC,CAAC,CAAC;AAEnD,SAAgB,MAAM,CAAC,OAAe;IACpC,OAAO,gBAAM,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;AAC3E,CAAC;AAED,SAAgB,YAAY,CAAC,MAAc;IACzC,MAAM,EAAE,GAAG,IAAI,wBAAQ,CAAC,MAAM,CAAC,CAAC;IAChC,EAAE,CAAC,MAAM,CAAC,oBAAoB,CAAC,CAAC;IAChC,EAAE,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC;IACxB,EAAE,CAAC,IAAI,CAAC;;;;;;GAMP,CAAC,CAAC;IACH,OAAO,EAAE,CAAC;AACZ,CAAC;AAiBD,SAAgB,SAAS,CAAC,EAAqB,EAAE,KAAe;IAC9D,YAAY,CAAC,EAAE,EAAE,KAAK,CAAC,CAAC;AAC1B,CAAC;AAED,SAAgB,YAAY,CAAC,OAAe,EAAE,OAAkB;IAC9D,MAAM,IAAI,GAAG,OAAO,IAAI,EAAE,CAAC;IAC3B,YAAE,CAAC,SAAS,CAAC,OAAO,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAC3C,KAAK,MAAM,GAAG,IAAI,IAAI,EAAE,CAAC;QACvB,YAAE,CAAC,SAAS,CAAC,cAAI,CAAC,IAAI,CAAC,OAAO,EAAE,GAAG,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAC7D,CAAC;AACH,CAAC;AAED,SAAgB,eAAe,CAAC,OAAe;IAC7C,YAAE,CAAC,MAAM,CAAC,OAAO,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;AACvD,CAAC"}
1
+ {"version":3,"file":"db.js","sourceRoot":"","sources":["../../../tests/helpers/db.ts"],"names":[],"mappings":";;;;;AAaA,wBAEC;AAED,oCAYC;AAiBD,8BAEC;AAED,4DAIC;AAED,oCAMC;AAED,0CAEC;AAOD,sDAKC;AAED,wDAGC;AAnFD,4CAAoB;AACpB,gDAAwB;AACxB,4CAAoB;AACpB,oDAA4B;AAC5B,oEAAsC;AAEtC,oEAAoE;AACpE,MAAM,EAAE,cAAc,EAAE,YAAY,EAAE,mBAAmB,EAAE,sBAAsB,EAAE,GAI/E,OAAO,CAAC,qCAAqC,CAAC,CAAC;AAEnD,SAAgB,MAAM,CAAC,OAAe;IACpC,OAAO,gBAAM,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;AAC3E,CAAC;AAED,SAAgB,YAAY,CAAC,MAAc;IACzC,MAAM,EAAE,GAAG,IAAI,wBAAQ,CAAC,MAAM,CAAC,CAAC;IAChC,EAAE,CAAC,MAAM,CAAC,oBAAoB,CAAC,CAAC;IAChC,EAAE,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC;IACxB,EAAE,CAAC,IAAI,CAAC;;;;;;GAMP,CAAC,CAAC;IACH,OAAO,EAAE,CAAC;AACZ,CAAC;AAiBD,SAAgB,SAAS,CAAC,EAAqB,EAAE,KAAe;IAC9D,YAAY,CAAC,EAAE,EAAE,KAAK,CAAC,CAAC;AAC1B,CAAC;AAED,SAAgB,wBAAwB,CAAC,MAAc;IACrD,MAAM,EAAE,GAAG,YAAY,CAAC,MAAM,CAAC,CAAC;IAChC,sBAAsB,CAAC,EAAE,CAAC,CAAC;IAC3B,OAAO,EAAE,CAAC;AACZ,CAAC;AAED,SAAgB,YAAY,CAAC,OAAe,EAAE,OAAkB;IAC9D,MAAM,IAAI,GAAG,OAAO,IAAI,EAAE,CAAC;IAC3B,YAAE,CAAC,SAAS,CAAC,OAAO,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAC3C,KAAK,MAAM,GAAG,IAAI,IAAI,EAAE,CAAC;QACvB,YAAE,CAAC,SAAS,CAAC,cAAI,CAAC,IAAI,CAAC,OAAO,EAAE,GAAG,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAC7D,CAAC;AACH,CAAC;AAED,SAAgB,eAAe,CAAC,OAAe;IAC7C,YAAE,CAAC,MAAM,CAAC,OAAO,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;AACvD,CAAC;AAOD,SAAgB,qBAAqB,CAAC,MAAc;IAClD,MAAM,MAAM,GAAG,YAAE,CAAC,WAAW,CAAC,cAAI,CAAC,IAAI,CAAC,YAAE,CAAC,MAAM,EAAE,EAAE,YAAY,MAAM,GAAG,CAAC,CAAC,CAAC;IAC7E,MAAM,MAAM,GAAG,cAAI,CAAC,IAAI,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC;IAC5C,MAAM,EAAE,GAAG,wBAAwB,CAAC,MAAM,CAAC,CAAC;IAC5C,OAAO,EAAE,EAAE,EAAE,MAAM,EAAE,CAAC;AACxB,CAAC;AAED,SAAgB,sBAAsB,CAAC,GAAoB;IACzD,GAAG,CAAC,EAAE,CAAC,KAAK,EAAE,CAAC;IACf,YAAE,CAAC,MAAM,CAAC,GAAG,CAAC,MAAM,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;AAC1D,CAAC"}
@@ -287,6 +287,117 @@ const DEFAULT_MODELS = {
287
287
  default: 'haiku',
288
288
  };
289
289
 
290
+ // ── Episodes (v0.4.0) ─────────────────────────────────────────────
291
+
292
+ const SQL_EPISODES_CREATE = `
293
+ CREATE TABLE IF NOT EXISTS episodes (
294
+ id TEXT PRIMARY KEY,
295
+ kind TEXT NOT NULL,
296
+ scope TEXT NOT NULL DEFAULT 'project',
297
+ project TEXT,
298
+ summary TEXT NOT NULL,
299
+ body TEXT,
300
+ tags TEXT,
301
+ entities TEXT,
302
+ parent_id TEXT,
303
+ status TEXT NOT NULL DEFAULT 'active',
304
+ supersedes TEXT,
305
+ source TEXT,
306
+ created_at TEXT NOT NULL
307
+ )`;
308
+
309
+ const SQL_EPISODES_INDEXES = [
310
+ 'CREATE INDEX IF NOT EXISTS idx_episodes_kind ON episodes(kind, status)',
311
+ 'CREATE INDEX IF NOT EXISTS idx_episodes_project ON episodes(project, status)',
312
+ 'CREATE INDEX IF NOT EXISTS idx_episodes_created ON episodes(created_at DESC)',
313
+ ];
314
+
315
+ /**
316
+ * Ensure episodes table + indexes exist. Idempotent.
317
+ * @param {import('better-sqlite3').Database} db
318
+ */
319
+ function ensureEpisodesTable(db) {
320
+ db.exec(SQL_EPISODES_CREATE);
321
+ for (const idx of SQL_EPISODES_INDEXES) {
322
+ db.exec(idx);
323
+ }
324
+ }
325
+
326
+ /**
327
+ * Check if episodes table exists in the database.
328
+ * @param {import('better-sqlite3').Database} db
329
+ * @returns {boolean}
330
+ */
331
+ function hasEpisodesTable(db) {
332
+ const row = db.prepare("SELECT name FROM sqlite_master WHERE type='table' AND name='episodes'").get();
333
+ return row !== undefined;
334
+ }
335
+
336
+ /**
337
+ * Generate a time-sortable episode ID.
338
+ * Format: ep-{base36-timestamp}-{12-hex-random}
339
+ */
340
+ function generateEpisodeId() {
341
+ const timestamp = Date.now().toString(36);
342
+ const random = crypto.randomBytes(6).toString('hex');
343
+ return `ep-${timestamp}-${random}`;
344
+ }
345
+
346
+ /**
347
+ * Insert a bare episode from hook context (no LLM needed).
348
+ * @param {import('better-sqlite3').Database} db
349
+ * @param {object} entry
350
+ * @returns {string} episode id
351
+ */
352
+ function insertBareEpisode(db, entry) {
353
+ const id = entry.id || generateEpisodeId();
354
+ const now = new Date().toISOString();
355
+
356
+ db.prepare(`
357
+ INSERT INTO episodes (id, kind, scope, project, summary, body, tags, entities, parent_id, status, supersedes, source, created_at)
358
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, 'active', ?, ?, ?)
359
+ `).run(
360
+ id,
361
+ entry.kind || 'session',
362
+ entry.scope || 'project',
363
+ entry.project || null,
364
+ entry.summary || '',
365
+ entry.body || null,
366
+ entry.tags || null,
367
+ entry.entities ? JSON.stringify(entry.entities) : null,
368
+ entry.parent_id || null,
369
+ entry.supersedes || null,
370
+ entry.source || 'hook',
371
+ now,
372
+ );
373
+
374
+ return id;
375
+ }
376
+
377
+ /**
378
+ * Query recent episodes for session-focus injection.
379
+ * @param {import('better-sqlite3').Database} db
380
+ * @param {object} opts - { project, limit, maxChars }
381
+ * @returns {Array<{kind: string, summary: string, created_at: string}>}
382
+ */
383
+ function queryRecentEpisodes(db, opts) {
384
+ const project = opts.project || null;
385
+ const limit = opts.limit || 3;
386
+
387
+ let sql = "SELECT kind, summary, created_at FROM episodes WHERE status = 'active'";
388
+ const params = [];
389
+
390
+ if (project) {
391
+ sql += ' AND project = ?';
392
+ params.push(project);
393
+ }
394
+
395
+ sql += ' ORDER BY created_at DESC LIMIT ?';
396
+ params.push(limit);
397
+
398
+ return db.prepare(sql).all(...params);
399
+ }
400
+
290
401
  module.exports = {
291
402
  MINDLORE_DIR,
292
403
  GLOBAL_MINDLORE_DIR,
@@ -312,4 +423,12 @@ module.exports = {
312
423
  detectSchemaVersion,
313
424
  getProjectName,
314
425
  DEFAULT_MODELS,
426
+ // Episodes (v0.4.0)
427
+ SQL_EPISODES_CREATE,
428
+ SQL_EPISODES_INDEXES,
429
+ ensureEpisodesTable,
430
+ hasEpisodesTable,
431
+ generateEpisodeId,
432
+ insertBareEpisode,
433
+ queryRecentEpisodes,
315
434
  };
@@ -7,8 +7,8 @@
7
7
  * After a file is read, estimate its token count
8
8
  * and store in _session-reads.json for the read-guard to reference.
9
9
  *
10
- * Does NOT output anything (pure bookkeeping).
11
- * PostToolUse stdout goes to debug log only — no inject needed.
10
+ * Outputs token estimate via additionalContext JSON.
11
+ * Also stores token info in _session-reads.json for read-guard.
12
12
  */
13
13
 
14
14
  const fs = require('fs');
@@ -87,6 +87,15 @@ function main() {
87
87
  }
88
88
 
89
89
  fs.writeFileSync(readsPath, JSON.stringify(reads, null, 2), 'utf8');
90
+
91
+ // Output token estimate to Claude via additionalContext
92
+ const basename = path.basename(filePath);
93
+ process.stdout.write(JSON.stringify({
94
+ hookSpecificOutput: {
95
+ hookEventName: 'PostToolUse',
96
+ additionalContext: `[Mindlore: ${basename} — ~${tokens} token (${charCount} char). Edit etmeyeceksen ctx_execute_file kullan.]`
97
+ }
98
+ }));
90
99
  } catch {
91
100
  // Silent fail
92
101
  }
@@ -68,15 +68,23 @@ function main() {
68
68
  // Write updated reads
69
69
  fs.writeFileSync(readsPath, JSON.stringify(reads, null, 2), 'utf8');
70
70
 
71
- // Warn on repeated reads (2nd+ time)
71
+ const basename = path.basename(filePath);
72
+ const tokenInfo = tokens > 0 ? ` (~${tokens} token)` : '';
73
+
74
+ // Block on 3+ repeated reads (exit 2 = block tool call)
75
+ if (count >= 3) {
76
+ const totalWaste = tokens > 0 ? ` Toplam israf: ~${tokens * (count - 1)} token.` : '';
77
+ process.stderr.write(`[Mindlore BLOCK] ${basename}${tokenInfo} bu session'da ${count}. kez okunuyor.${totalWaste} Edit icin gerekiyorsa once degisikligini yap, sonra tekrar oku. Analiz icin ctx_execute_file kullan.`);
78
+ process.exit(2);
79
+ }
80
+
81
+ // Warn on 2nd read (exit 0 = allow but warn)
72
82
  if (count > 1) {
73
- const basename = path.basename(filePath);
74
- const tokenInfo = tokens > 0 ? ` (~${tokens} token)` : '';
75
83
  const totalWaste = tokens > 0 ? ` Toplam tekrar: ~${tokens * (count - 1)} token.` : '';
76
84
  process.stdout.write(JSON.stringify({
77
85
  hookSpecificOutput: {
78
86
  hookEventName: 'PreToolUse',
79
- additionalContext: `[Mindlore: ${basename}${tokenInfo} bu session'da ${count}. kez okunuyor.${totalWaste} Değişiklik yoksa tekrar okumayı atlayabilirsin.]`
87
+ additionalContext: `[Mindlore: ${basename}${tokenInfo} bu session'da ${count}. kez okunuyor.${totalWaste} Bir sonraki okuma engellenecek — Edit gerekiyorsa simdi yap.]`
80
88
  }
81
89
  }));
82
90
  }
@@ -104,6 +104,27 @@ function searchDb(dbPath, keywords, Database) {
104
104
  return results;
105
105
  }
106
106
 
107
+ /**
108
+ * Search episodes via FTS5 mirror (type = 'episode').
109
+ * Reuses an already-open DB handle — no extra sqlite3_open.
110
+ */
111
+ function searchEpisodesFts(db, keywords) {
112
+ try {
113
+ const ftsQuery = keywords.map(kw => '"' + kw + '"').join(' OR ');
114
+ const rows = db.prepare(
115
+ "SELECT title, category, slug, tags FROM mindlore_fts WHERE type = 'episode' AND mindlore_fts MATCH ? LIMIT 2"
116
+ ).all(ftsQuery);
117
+
118
+ return rows.map(r => {
119
+ const tags = r.tags || '';
120
+ const kind = tags.split(',')[0]?.trim() || 'episode';
121
+ return `[episode] ${kind}: ${r.title || r.slug}`;
122
+ });
123
+ } catch (_err) {
124
+ return [];
125
+ }
126
+ }
127
+
107
128
  function main() {
108
129
  const userMessage = readHookStdin(['prompt', 'content', 'message', 'query']);
109
130
  if (!userMessage || userMessage.length < MIN_QUERY_WORDS) return;
@@ -162,6 +183,21 @@ function main() {
162
183
  );
163
184
  }
164
185
 
186
+ // v0.4.0: Search episode mirrors in FTS5 (reuses searchDb's DB path, no extra open)
187
+ if (relevant.length < MAX_RESULTS) {
188
+ for (const dbPath of dbPaths) {
189
+ try {
190
+ const db = new Database(dbPath, { readonly: true });
191
+ const episodeResults = searchEpisodesFts(db, keywords);
192
+ db.close();
193
+ if (episodeResults.length > 0) {
194
+ output.push(`[Mindlore Episodes]\n${episodeResults.join('\n')}`);
195
+ break;
196
+ }
197
+ } catch (_err) { /* skip */ }
198
+ }
199
+ }
200
+
165
201
  if (output.length > 0) {
166
202
  process.stdout.write(output.join('\n\n') + '\n');
167
203
  }