mindlore 0.6.6 → 0.6.8

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 (184) hide show
  1. package/README.md +259 -259
  2. package/SCHEMA.md +292 -292
  3. package/dist/scripts/cc-memory-bulk-sync.d.ts.map +1 -1
  4. package/dist/scripts/cc-memory-bulk-sync.js +47 -42
  5. package/dist/scripts/cc-memory-bulk-sync.js.map +1 -1
  6. package/dist/scripts/cc-session-sync.d.ts.map +1 -1
  7. package/dist/scripts/cc-session-sync.js +58 -48
  8. package/dist/scripts/cc-session-sync.js.map +1 -1
  9. package/dist/scripts/init.js +8 -8
  10. package/dist/scripts/init.js.map +1 -1
  11. package/dist/scripts/lib/all-migrations.d.ts.map +1 -1
  12. package/dist/scripts/lib/all-migrations.js +7 -1
  13. package/dist/scripts/lib/all-migrations.js.map +1 -1
  14. package/dist/scripts/lib/consolidation.d.ts +4 -3
  15. package/dist/scripts/lib/consolidation.d.ts.map +1 -1
  16. package/dist/scripts/lib/consolidation.js +10 -10
  17. package/dist/scripts/lib/consolidation.js.map +1 -1
  18. package/dist/scripts/lib/constants.d.ts +1 -7
  19. package/dist/scripts/lib/constants.d.ts.map +1 -1
  20. package/dist/scripts/lib/constants.js +2 -9
  21. package/dist/scripts/lib/constants.js.map +1 -1
  22. package/dist/scripts/lib/db-helpers.d.ts +0 -15
  23. package/dist/scripts/lib/db-helpers.d.ts.map +1 -1
  24. package/dist/scripts/lib/db-helpers.js +1 -51
  25. package/dist/scripts/lib/db-helpers.js.map +1 -1
  26. package/dist/scripts/lib/decay.d.ts.map +1 -1
  27. package/dist/scripts/lib/decay.js +9 -9
  28. package/dist/scripts/lib/decay.js.map +1 -1
  29. package/dist/scripts/lib/episodes.js +23 -23
  30. package/dist/scripts/lib/migrations-v061.js +21 -21
  31. package/dist/scripts/lib/migrations-v062.js +11 -11
  32. package/dist/scripts/lib/migrations-v063.js +14 -14
  33. package/dist/scripts/lib/migrations-v067.d.ts +7 -0
  34. package/dist/scripts/lib/migrations-v067.d.ts.map +1 -0
  35. package/dist/scripts/lib/migrations-v067.js +50 -0
  36. package/dist/scripts/lib/migrations-v067.js.map +1 -0
  37. package/dist/scripts/lib/migrations-v068.d.ts +3 -0
  38. package/dist/scripts/lib/migrations-v068.d.ts.map +1 -0
  39. package/dist/scripts/lib/migrations-v068.js +37 -0
  40. package/dist/scripts/lib/migrations-v068.js.map +1 -0
  41. package/dist/scripts/lib/migrations.d.ts.map +1 -1
  42. package/dist/scripts/lib/migrations.js +0 -15
  43. package/dist/scripts/lib/migrations.js.map +1 -1
  44. package/dist/scripts/lib/schema-version.js +6 -6
  45. package/dist/scripts/lib/search-cache.js +11 -11
  46. package/dist/scripts/lib/session-payload.d.ts +9 -1
  47. package/dist/scripts/lib/session-payload.d.ts.map +1 -1
  48. package/dist/scripts/lib/session-payload.js +11 -10
  49. package/dist/scripts/lib/session-payload.js.map +1 -1
  50. package/dist/scripts/lib/triage.js +3 -3
  51. package/dist/scripts/mindlore-backup.js +9 -9
  52. package/dist/scripts/mindlore-fts5-index.d.ts +1 -2
  53. package/dist/scripts/mindlore-fts5-index.d.ts.map +1 -1
  54. package/dist/scripts/mindlore-fts5-index.js +12 -64
  55. package/dist/scripts/mindlore-fts5-index.js.map +1 -1
  56. package/dist/scripts/mindlore-health-check.d.ts.map +1 -1
  57. package/dist/scripts/mindlore-health-check.js +0 -11
  58. package/dist/scripts/mindlore-health-check.js.map +1 -1
  59. package/dist/tests/cc-memory-bulk-sync.test.js +23 -0
  60. package/dist/tests/cc-memory-bulk-sync.test.js.map +1 -1
  61. package/dist/tests/cc-session-sync.test.js +25 -0
  62. package/dist/tests/cc-session-sync.test.js.map +1 -1
  63. package/dist/tests/compaction-snapshot.test.js +2 -2
  64. package/dist/tests/consolidation.test.js +5 -5
  65. package/dist/tests/consolidation.test.js.map +1 -1
  66. package/dist/tests/decay.test.js +9 -9
  67. package/dist/tests/diary.test.js +4 -4
  68. package/dist/tests/episode-kind-constant.test.d.ts +2 -0
  69. package/dist/tests/episode-kind-constant.test.d.ts.map +1 -0
  70. package/dist/tests/episode-kind-constant.test.js +28 -0
  71. package/dist/tests/episode-kind-constant.test.js.map +1 -0
  72. package/dist/tests/episodes-inject.test.js +14 -14
  73. package/dist/tests/episodes-inject.test.js.map +1 -1
  74. package/dist/tests/fts5.test.js +66 -125
  75. package/dist/tests/fts5.test.js.map +1 -1
  76. package/dist/tests/globalSetup.d.ts +2 -0
  77. package/dist/tests/globalSetup.d.ts.map +1 -0
  78. package/dist/tests/globalSetup.js +36 -0
  79. package/dist/tests/globalSetup.js.map +1 -0
  80. package/dist/tests/helpers/db.d.ts +13 -5
  81. package/dist/tests/helpers/db.d.ts.map +1 -1
  82. package/dist/tests/helpers/db.js +61 -33
  83. package/dist/tests/helpers/db.js.map +1 -1
  84. package/dist/tests/lesson-graduation.test.d.ts +2 -0
  85. package/dist/tests/lesson-graduation.test.d.ts.map +1 -0
  86. package/dist/tests/lesson-graduation.test.js +83 -0
  87. package/dist/tests/lesson-graduation.test.js.map +1 -0
  88. package/dist/tests/migrations-v053.test.js +16 -16
  89. package/dist/tests/migrations-v061.test.js +10 -10
  90. package/dist/tests/migrations-v063.test.js +2 -2
  91. package/dist/tests/migrations-v067.test.d.ts +2 -0
  92. package/dist/tests/migrations-v067.test.d.ts.map +1 -0
  93. package/dist/tests/migrations-v067.test.js +115 -0
  94. package/dist/tests/migrations-v067.test.js.map +1 -0
  95. package/dist/tests/migrations-v068.test.d.ts +2 -0
  96. package/dist/tests/migrations-v068.test.d.ts.map +1 -0
  97. package/dist/tests/migrations-v068.test.js +53 -0
  98. package/dist/tests/migrations-v068.test.js.map +1 -0
  99. package/dist/tests/nomination-counts.test.d.ts +2 -0
  100. package/dist/tests/nomination-counts.test.d.ts.map +1 -0
  101. package/dist/tests/nomination-counts.test.js +51 -0
  102. package/dist/tests/nomination-counts.test.js.map +1 -0
  103. package/dist/tests/recall-telemetry.test.js +8 -8
  104. package/dist/tests/schema-version.test.js +3 -7
  105. package/dist/tests/schema-version.test.js.map +1 -1
  106. package/dist/tests/search-hook.test.js +2 -2
  107. package/dist/tests/sec-regression.test.js +0 -50
  108. package/dist/tests/sec-regression.test.js.map +1 -1
  109. package/dist/tests/session-end-cleanup.test.d.ts +2 -0
  110. package/dist/tests/session-end-cleanup.test.d.ts.map +1 -0
  111. package/dist/tests/session-end-cleanup.test.js +59 -0
  112. package/dist/tests/session-end-cleanup.test.js.map +1 -0
  113. package/dist/tests/session-focus.test.js +69 -10
  114. package/dist/tests/session-focus.test.js.map +1 -1
  115. package/dist/tests/session-payload.test.js +11 -11
  116. package/dist/tests/session-payload.test.js.map +1 -1
  117. package/dist/tests/session-summary.test.js +2 -2
  118. package/dist/tests/session-summary.test.js.map +1 -1
  119. package/hooks/lib/constants.cjs +15 -0
  120. package/hooks/lib/mindlore-common.cjs +974 -1004
  121. package/hooks/mindlore-cwd-changed.cjs +57 -57
  122. package/hooks/mindlore-decision-detector.cjs +54 -54
  123. package/hooks/mindlore-dont-repeat.cjs +222 -222
  124. package/hooks/mindlore-fts5-sync.cjs +97 -88
  125. package/hooks/mindlore-index.cjs +229 -229
  126. package/hooks/mindlore-model-router.cjs +54 -54
  127. package/hooks/mindlore-post-compact.cjs +69 -69
  128. package/hooks/mindlore-post-read.cjs +106 -106
  129. package/hooks/mindlore-pre-compact.cjs +154 -154
  130. package/hooks/mindlore-read-guard.cjs +105 -105
  131. package/hooks/mindlore-research-guard.cjs +176 -176
  132. package/hooks/mindlore-search.cjs +200 -200
  133. package/hooks/mindlore-session-end.cjs +509 -523
  134. package/hooks/mindlore-session-focus.cjs +256 -245
  135. package/package.json +75 -78
  136. package/plugin.json +1 -1
  137. package/skills/mindlore-diary/SKILL.md +85 -85
  138. package/skills/mindlore-evolve/SKILL.md +126 -126
  139. package/skills/mindlore-explore/SKILL.md +109 -109
  140. package/skills/mindlore-ingest/SKILL.md +195 -195
  141. package/skills/mindlore-maintain/SKILL.md +125 -125
  142. package/skills/mindlore-query/SKILL.md +151 -151
  143. package/skills/mindlore-reflect/SKILL.md +141 -131
  144. package/skills/mindlore-stats/SKILL.md +106 -106
  145. package/templates/INDEX.md +14 -14
  146. package/templates/SCHEMA.md +292 -292
  147. package/templates/config.json +1 -1
  148. package/templates/extraction/article.md +15 -15
  149. package/templates/extraction/changelog.md +15 -15
  150. package/templates/extraction/default.md +15 -15
  151. package/templates/extraction/docs.md +15 -15
  152. package/templates/extraction/github-repo.md +17 -17
  153. package/dist/scripts/lib/daemon.d.ts +0 -16
  154. package/dist/scripts/lib/daemon.d.ts.map +0 -1
  155. package/dist/scripts/lib/daemon.js +0 -133
  156. package/dist/scripts/lib/daemon.js.map +0 -1
  157. package/dist/scripts/lib/embedding.d.ts +0 -5
  158. package/dist/scripts/lib/embedding.d.ts.map +0 -1
  159. package/dist/scripts/lib/embedding.js +0 -44
  160. package/dist/scripts/lib/embedding.js.map +0 -1
  161. package/dist/scripts/mindlore-daemon.d.ts +0 -2
  162. package/dist/scripts/mindlore-daemon.d.ts.map +0 -1
  163. package/dist/scripts/mindlore-daemon.js +0 -117
  164. package/dist/scripts/mindlore-daemon.js.map +0 -1
  165. package/dist/tests/daemon-integration.test.d.ts +0 -2
  166. package/dist/tests/daemon-integration.test.d.ts.map +0 -1
  167. package/dist/tests/daemon-integration.test.js +0 -37
  168. package/dist/tests/daemon-integration.test.js.map +0 -1
  169. package/dist/tests/daemon.test.d.ts +0 -2
  170. package/dist/tests/daemon.test.d.ts.map +0 -1
  171. package/dist/tests/daemon.test.js +0 -187
  172. package/dist/tests/daemon.test.js.map +0 -1
  173. package/dist/tests/embedding-hf-integration.test.d.ts +0 -2
  174. package/dist/tests/embedding-hf-integration.test.d.ts.map +0 -1
  175. package/dist/tests/embedding-hf-integration.test.js +0 -52
  176. package/dist/tests/embedding-hf-integration.test.js.map +0 -1
  177. package/dist/tests/embedding.test.d.ts +0 -6
  178. package/dist/tests/embedding.test.d.ts.map +0 -1
  179. package/dist/tests/embedding.test.js +0 -71
  180. package/dist/tests/embedding.test.js.map +0 -1
  181. package/dist/tests/sqlite-vec-v12.test.d.ts +0 -2
  182. package/dist/tests/sqlite-vec-v12.test.d.ts.map +0 -1
  183. package/dist/tests/sqlite-vec-v12.test.js +0 -72
  184. package/dist/tests/sqlite-vec-v12.test.js.map +0 -1
@@ -1,245 +1,256 @@
1
- #!/usr/bin/env node
2
- 'use strict';
3
-
4
- /**
5
- * mindlore-session-focus — SessionStart hook
6
- *
7
- * Injects last delta file content + INDEX.md into session context.
8
- * Fires once at session start via stdout additionalContext.
9
- */
10
-
11
- const fs = require('fs');
12
- const path = require('path');
13
- const { findMindloreDir, readConfig, openDatabase, hasEpisodesTable, querySupersededChains, formatSupersededChains, hookLog, getProjectName, parseFrontmatter, withTelemetry, withTimeoutDb, listSnapshots, isCorruptionError, recoverCorruptDb } = require('./lib/mindlore-common.cjs');
14
-
15
- function truncateSection(content, sectionRegex, keepCount, label) {
16
- const match = content.match(sectionRegex);
17
- if (!match) return content;
18
- const lines = match[2].trim().split('\n');
19
- if (lines.length <= keepCount) return content;
20
- const kept = lines.slice(0, keepCount).join('\n');
21
- return content.replace(match[2].trim(), kept + `\n- ...ve ${lines.length - keepCount} ${label} daha`);
22
- }
23
-
24
- function truncateCommits(content) {
25
- return truncateSection(content, /(## Commits\n)((?:- [^\n]+\n?)+)/, 5, 'commit');
26
- }
27
-
28
- function truncateChangedFiles(content) {
29
- return truncateSection(content, /(## Changed Files\n)((?:- [^\n]+\n?)+)/, 10, 'dosya');
30
- }
31
-
32
- function tryOpenDb(dbPath) {
33
- return openDatabase(dbPath, { readonly: true });
34
- }
35
-
36
- function getEpisodeStats(db, config, project) {
37
- const chains = querySupersededChains(db, { project, days: 7, limit: 5 });
38
- let consolidationMsg = null;
39
- try {
40
- const rawCount = withTimeoutDb(db,
41
- "SELECT COUNT(*) as cnt FROM episodes WHERE consolidation_status = 'raw' OR consolidation_status IS NULL",
42
- [], { mode: 'get' });
43
- const cnt = rawCount?.cnt ?? 0;
44
- const consolThreshold = config?.consolidation?.threshold ?? 50;
45
- if (cnt >= consolThreshold) {
46
- consolidationMsg = `[Mindlore] ${cnt} raw episode birikti — \`/mindlore-maintain consolidate\` ile birleştirmeyi düşün.`;
47
- }
48
- } catch (_err) { /* consolidation_status column may not exist yet */ }
49
- return { chains, consolidationMsg };
50
- }
51
-
52
- function checkStaleContent(db) {
53
- try {
54
- const thirtyDaysAgo = new Date(Date.now() - (30 * 24 * 60 * 60 * 1000)).toISOString();
55
- const row = withTimeoutDb(db, 'SELECT COUNT(*) as cnt FROM file_hashes WHERE last_indexed < ?', [thirtyDaysAgo], { mode: 'get' });
56
- const staleCount = row?.cnt ?? 0;
57
- if (staleCount > 3) {
58
- return `[Mindlore: ${staleCount} dosya 30+ gundur guncellenmemis — \`/mindlore-evolve\` dusun]`;
59
- }
60
- } catch (_staleErr) { /* file_hashes may not exist */ }
61
- return null;
62
- }
63
-
64
- function loadDbContent(db, baseDir, config, output, timings, latestDeltaContent, sessionId) {
65
- const project = path.basename(process.cwd());
66
- // Session payload: Session summary, Decisions, Friction, Learnings
67
- const tPayload = Date.now();
68
- try {
69
- const { buildSessionPayload } = require('../dist/scripts/lib/session-payload.js');
70
- const payloadBudget = config?.tokenBudget?.sessionInject ?? 2000;
71
- const payload = buildSessionPayload(db, baseDir, project, payloadBudget, latestDeltaContent, sessionId);
72
- for (const section of payload.sections) {
73
- output.push(`[Mindlore ${section.label}]\n${section.content}`);
74
- }
75
- } catch (_payloadErr) {
76
- // Session payload is optional — don't break session start
77
- }
78
- timings.db_payload = Date.now() - tPayload;
79
-
80
- // Supersedes chain display + episode consolidation reminder
81
- const tSuperseded = Date.now();
82
- if (hasEpisodesTable(db)) {
83
- const { chains, consolidationMsg } = getEpisodeStats(db, config, project);
84
- if (chains.length > 0) {
85
- output.push(`[Mindlore Supersedes]\n${formatSupersededChains(chains)}`);
86
- }
87
- if (consolidationMsg) {
88
- output.push(consolidationMsg);
89
- }
90
- }
91
- timings.db_episodes = Date.now() - tSuperseded;
92
-
93
- // Stale content check
94
- const tStale = Date.now();
95
- const staleMsg = checkStaleContent(db);
96
- if (staleMsg) {
97
- output.push(staleMsg);
98
- }
99
- timings.db_stale = Date.now() - tStale;
100
- }
101
-
102
- function main() {
103
- const t0 = Date.now();
104
- const baseDir = findMindloreDir();
105
- if (!baseDir) return; // No .mindlore/ found, silently skip
106
-
107
- // Read session_id from stdin (Claude Code passes { session_id } to SessionStart hooks)
108
- let sessionId;
109
- try {
110
- const stdinData = JSON.parse(fs.readFileSync(0, 'utf8') || '{}');
111
- sessionId = stdinData.session_id || undefined;
112
- } catch { sessionId = undefined; }
113
-
114
- const output = [];
115
- const config = readConfig(baseDir);
116
- const timings = {};
117
- let sourceChars = 0;
118
-
119
- // Inject INDEX.md
120
- const tIndex = Date.now();
121
- const indexPath = path.join(baseDir, 'INDEX.md');
122
- if (fs.existsSync(indexPath)) {
123
- sourceChars += fs.statSync(indexPath).size;
124
- const content = fs.readFileSync(indexPath, 'utf8').trim();
125
- output.push(`[Mindlore INDEX]\n${content}`);
126
- }
127
- timings.index_read = Date.now() - tIndex;
128
-
129
- // Inject latest delta + reflect trigger (single readdirSync)
130
- const tDiary = Date.now();
131
- const diaryDir = path.join(baseDir, 'diary');
132
- let latestDeltaContent = undefined;
133
- if (fs.existsSync(diaryDir)) {
134
- try {
135
- const diaryFiles = listSnapshots(diaryDir).filter(f => f.startsWith('delta-'));
136
-
137
- if (diaryFiles.length > 0) {
138
- const latestName = diaryFiles[diaryFiles.length - 1];
139
- const latestPath = path.join(diaryDir, latestName);
140
- sourceChars += fs.statSync(latestPath).size;
141
- const deltaContent = fs.readFileSync(latestPath, 'utf8').trim();
142
- latestDeltaContent = deltaContent;
143
- const { meta } = parseFrontmatter(deltaContent);
144
- const deltaProject = meta.project || null;
145
- const currentProject = getProjectName();
146
- if (!deltaProject || deltaProject.toLowerCase() === currentProject.toLowerCase()) {
147
- output.push(`[Mindlore Delta: ${latestName}]\n${truncateChangedFiles(truncateCommits(deltaContent))}`);
148
- }
149
- }
150
-
151
- // Reflect trigger
152
- const threshold = config?.reflect?.threshold ?? 5;
153
- if (diaryFiles.length >= threshold) {
154
- output.push(`[Mindlore] ${diaryFiles.length} diary entry birikti — \`/mindlore-log reflect\` calistirmayi dusun.`);
155
- }
156
- } catch (_err) { /* skip */ }
157
- }
158
- timings.diary_walk = Date.now() - tDiary;
159
-
160
- // Version check: compare .version (installed) vs .pkg-version (package)
161
- const tVersion = Date.now();
162
- // Both are flat strings written by init — no JSON parse needed on session start
163
- const versionPath = path.join(baseDir, '.version');
164
- const pkgVersionPath = path.join(baseDir, '.pkg-version');
165
- try {
166
- if (fs.existsSync(versionPath) && fs.existsSync(pkgVersionPath)) {
167
- const installed = fs.readFileSync(versionPath, 'utf8').trim();
168
- const pkgVersion = fs.readFileSync(pkgVersionPath, 'utf8').trim();
169
- if (pkgVersion && pkgVersion !== installed) {
170
- output.push(`[Mindlore: Guncelleme mevcut (${installed} → ${pkgVersion}). \`npx mindlore init\` calistirin.]`);
171
- }
172
- }
173
- } catch (_err) { /* skip */ }
174
- timings.version_check = Date.now() - tVersion;
175
-
176
- // v0.5.4: Consolidated session payload (replaces scattered episodes/activity/alerts injection)
177
- const tDb = Date.now();
178
- const outputLenBeforeDb = output.reduce((s, o) => s + o.length, 0);
179
- try {
180
- const dbPath = path.join(baseDir, 'mindlore.db');
181
- const tDbOpen = Date.now();
182
- const db = tryOpenDb(dbPath);
183
- timings.db_open = Date.now() - tDbOpen;
184
- timings.db_integrity = 0;
185
-
186
- if (db) {
187
- try {
188
- // Schema version check: warn if DB is behind expected version
189
- const tSchema = Date.now();
190
- try {
191
- const { EXPECTED_SCHEMA_VERSION } = require('../dist/scripts/lib/all-migrations.js');
192
- const row = db.prepare('SELECT MAX(version) as v FROM schema_versions').get();
193
- const current = row?.v ?? 0;
194
- if (current < EXPECTED_SCHEMA_VERSION) {
195
- output.push(`[Mindlore: schema güncel değil (v${current} → v${EXPECTED_SCHEMA_VERSION}). \`npx mindlore upgrade\` çalıştır.]`);
196
- }
197
- } catch (_schemaErr) { /* schema_versions may not exist yet */ }
198
- timings.schema_check = Date.now() - tSchema;
199
-
200
- loadDbContent(db, baseDir, config, output, timings, latestDeltaContent, sessionId);
201
- } catch (err) {
202
- if (isCorruptionError(err)) {
203
- recoverCorruptDb(db, dbPath, 'session-focus');
204
- }
205
- } finally {
206
- try { db.close(); } catch { /* already closed by recovery */ }
207
- }
208
- }
209
- } catch (_err) { /* graceful skip */ }
210
- const outputLenAfterDb = output.reduce((s, o) => s + o.length, 0);
211
- sourceChars += (outputLenAfterDb - outputLenBeforeDb);
212
- timings.db_total = Date.now() - tDb;
213
-
214
- timings.total = Date.now() - t0;
215
- hookLog('session-focus', 'info', `timings: ${JSON.stringify(timings)}`);
216
-
217
- // Token budget for session inject
218
- // Defaults match DEFAULT_TOKEN_BUDGET in scripts/lib/constants.ts
219
- const budgetConfig = config?.tokenBudget ?? {};
220
- const maxInjectChars = (budgetConfig.sessionInject || 2000) * 4;
221
-
222
- let joined = output.join('\n\n');
223
- if (joined.length > maxInjectChars) {
224
- joined = joined.slice(0, maxInjectChars) + '\n[...truncated by token budget]';
225
- }
226
-
227
- // v0.6.1: Daemon auto-start removed (daemon deprecated — MCP Server in v0.7)
228
-
229
- if (joined.length > 0) {
230
- process.stdout.write(joined + '\n');
231
- }
232
-
233
- const inject_tokens = Math.ceil(joined.length / 4);
234
- const source_tokens = Math.ceil(sourceChars / 4);
235
- return { inject_tokens, source_tokens };
236
- }
237
-
238
- withTelemetry('mindlore-session-focus', main).catch(err => {
239
- hookLog('mindlore-session-focus', 'error', err?.message ?? String(err));
240
- process.exit(0);
241
- });
242
-
243
- if (typeof module !== 'undefined') {
244
- module.exports = { truncateCommits, truncateChangedFiles, getEpisodeStats, checkStaleContent };
245
- }
1
+ #!/usr/bin/env node
2
+ 'use strict';
3
+
4
+ /**
5
+ * mindlore-session-focus — SessionStart hook
6
+ *
7
+ * Injects last delta file content + INDEX.md into session context.
8
+ * Fires once at session start via stdout additionalContext.
9
+ */
10
+
11
+ const fs = require('fs');
12
+ const path = require('path');
13
+ const { findMindloreDir, readConfig, openDatabase, hasEpisodesTable, querySupersededChains, formatSupersededChains, hookLog, getProjectName, parseFrontmatter, withTelemetry, withTimeoutDb, listSnapshots, isCorruptionError, recoverCorruptDb, getNominationCounts } = require('./lib/mindlore-common.cjs');
14
+
15
+ function truncateSection(content, sectionRegex, keepCount, label) {
16
+ const match = content.match(sectionRegex);
17
+ if (!match) return content;
18
+ const lines = match[2].trim().split('\n');
19
+ if (lines.length <= keepCount) return content;
20
+ const kept = lines.slice(0, keepCount).join('\n');
21
+ return content.replace(match[2].trim(), kept + `\n- ...ve ${lines.length - keepCount} ${label} daha`);
22
+ }
23
+
24
+ function truncateCommits(content) {
25
+ return truncateSection(content, /(## Commits\n)((?:- [^\n]+\n?)+)/, 5, 'commit');
26
+ }
27
+
28
+ function truncateChangedFiles(content) {
29
+ return truncateSection(content, /(## Changed Files\n)((?:- [^\n]+\n?)+)/, 10, 'dosya');
30
+ }
31
+
32
+ function tryOpenDb(dbPath) {
33
+ return openDatabase(dbPath, { readonly: true });
34
+ }
35
+
36
+ function getEpisodeStats(db, config, project) {
37
+ const chains = querySupersededChains(db, { project, days: 7, limit: 5 });
38
+ let consolidationMsg = null;
39
+ try {
40
+ const rawCount = withTimeoutDb(db,
41
+ "SELECT COUNT(*) as cnt FROM episodes WHERE consolidation_status = 'raw' OR consolidation_status IS NULL",
42
+ [], { mode: 'get' });
43
+ const cnt = rawCount?.cnt ?? 0;
44
+ const consolThreshold = config?.consolidation?.threshold ?? 50;
45
+ if (cnt >= consolThreshold) {
46
+ consolidationMsg = `[Mindlore] ${cnt} raw episode birikti — \`/mindlore-maintain consolidate\` ile birleştirmeyi düşün.`;
47
+ }
48
+ } catch (_err) { /* consolidation_status column may not exist yet */ }
49
+ return { chains, consolidationMsg };
50
+ }
51
+
52
+ function checkStaleContent(db) {
53
+ try {
54
+ const thirtyDaysAgo = new Date(Date.now() - (30 * 24 * 60 * 60 * 1000)).toISOString();
55
+ const row = withTimeoutDb(db, 'SELECT COUNT(*) as cnt FROM file_hashes WHERE last_indexed < ?', [thirtyDaysAgo], { mode: 'get' });
56
+ const staleCount = row?.cnt ?? 0;
57
+ if (staleCount > 3) {
58
+ return `[Mindlore: ${staleCount} dosya 30+ gundur guncellenmemis — \`/mindlore-evolve\` dusun]`;
59
+ }
60
+ } catch (_staleErr) { /* file_hashes may not exist */ }
61
+ return null;
62
+ }
63
+
64
+ function loadDbContent({ db, baseDir, config, output, timings, latestDeltaContent, sessionId }) {
65
+ const project = path.basename(process.cwd());
66
+ // Session payload: Session summary, Decisions, Friction, Learnings
67
+ const tPayload = Date.now();
68
+ try {
69
+ const { buildSessionPayload } = require('../dist/scripts/lib/session-payload.js');
70
+ const payloadBudget = config?.tokenBudget?.sessionInject ?? 2000;
71
+ const payload = buildSessionPayload({ db, baseDir, project, tokenBudget: payloadBudget, latestDeltaContent, sessionId });
72
+ for (const section of payload.sections) {
73
+ output.push(`[Mindlore ${section.label}]\n${section.content}`);
74
+ }
75
+ } catch (_payloadErr) {
76
+ // Session payload is optional — don't break session start
77
+ }
78
+ timings.db_payload = Date.now() - tPayload;
79
+
80
+ // Supersedes chain display + episode consolidation reminder
81
+ const tSuperseded = Date.now();
82
+ if (hasEpisodesTable(db)) {
83
+ const { chains, consolidationMsg } = getEpisodeStats(db, config, project);
84
+ if (chains.length > 0) {
85
+ output.push(`[Mindlore Supersedes]\n${formatSupersededChains(chains)}`);
86
+ }
87
+ if (consolidationMsg) {
88
+ output.push(consolidationMsg);
89
+ }
90
+ }
91
+ timings.db_episodes = Date.now() - tSuperseded;
92
+
93
+ // Stale content check
94
+ const tStale = Date.now();
95
+ const staleMsg = checkStaleContent(db);
96
+ if (staleMsg) {
97
+ output.push(staleMsg);
98
+ }
99
+ timings.db_stale = Date.now() - tStale;
100
+
101
+ // Auto reflect trigger (Q1) + Graduated lesson count (Q3)
102
+ try {
103
+ const counts = getNominationCounts(db, project);
104
+ if (counts.staged >= (config?.graduation?.reflectThreshold ?? 5)) {
105
+ output.push(`[Mindlore] ${counts.staged} bekleyen nomination var \`/mindlore-reflect\` çalıştır`);
106
+ }
107
+ if (counts.graduated > 0) {
108
+ output.push(`[Mindlore Graduation] ${counts.graduated} lesson mezun oldu`);
109
+ }
110
+ } catch (_reflectErr) { /* graduation not available */ }
111
+ }
112
+
113
+ function main() {
114
+ const t0 = Date.now();
115
+ const baseDir = findMindloreDir();
116
+ if (!baseDir) return; // No .mindlore/ found, silently skip
117
+
118
+ // Read session_id from stdin (Claude Code passes { session_id } to SessionStart hooks)
119
+ let sessionId;
120
+ try {
121
+ const stdinData = JSON.parse(fs.readFileSync(0, 'utf8') || '{}');
122
+ sessionId = stdinData.session_id || undefined;
123
+ } catch { sessionId = undefined; }
124
+
125
+ const output = [];
126
+ const config = readConfig(baseDir);
127
+ const timings = {};
128
+ let sourceChars = 0;
129
+
130
+ // Inject INDEX.md
131
+ const tIndex = Date.now();
132
+ const indexPath = path.join(baseDir, 'INDEX.md');
133
+ if (fs.existsSync(indexPath)) {
134
+ const content = fs.readFileSync(indexPath, 'utf8').trim();
135
+ sourceChars += content.length;
136
+ output.push(`[Mindlore INDEX]\n${content}`);
137
+ }
138
+ timings.index_read = Date.now() - tIndex;
139
+
140
+ // Inject latest delta + reflect trigger (single readdirSync)
141
+ const tDiary = Date.now();
142
+ const diaryDir = path.join(baseDir, 'diary');
143
+ let latestDeltaContent = undefined;
144
+ if (fs.existsSync(diaryDir)) {
145
+ try {
146
+ const diaryFiles = listSnapshots(diaryDir).filter(f => f.startsWith('delta-'));
147
+
148
+ if (diaryFiles.length > 0) {
149
+ const latestName = diaryFiles[diaryFiles.length - 1];
150
+ const latestPath = path.join(diaryDir, latestName);
151
+ const deltaContent = fs.readFileSync(latestPath, 'utf8').trim();
152
+ sourceChars += deltaContent.length;
153
+ latestDeltaContent = deltaContent;
154
+ const { meta } = parseFrontmatter(deltaContent);
155
+ const deltaProject = meta.project || null;
156
+ const currentProject = getProjectName();
157
+ if (!deltaProject || deltaProject.toLowerCase() === currentProject.toLowerCase()) {
158
+ output.push(`[Mindlore Delta: ${latestName}]\n${truncateChangedFiles(truncateCommits(deltaContent))}`);
159
+ }
160
+ }
161
+
162
+ // Reflect trigger
163
+ const threshold = config?.reflect?.threshold ?? 5;
164
+ if (diaryFiles.length >= threshold) {
165
+ output.push(`[Mindlore] ${diaryFiles.length} diary entry birikti — \`/mindlore-log reflect\` calistirmayi dusun.`);
166
+ }
167
+ } catch (_err) { /* skip */ }
168
+ }
169
+ timings.diary_walk = Date.now() - tDiary;
170
+
171
+ // Version check: compare .version (installed) vs .pkg-version (package)
172
+ const tVersion = Date.now();
173
+ // Both are flat strings written by init — no JSON parse needed on session start
174
+ const versionPath = path.join(baseDir, '.version');
175
+ const pkgVersionPath = path.join(baseDir, '.pkg-version');
176
+ try {
177
+ if (fs.existsSync(versionPath) && fs.existsSync(pkgVersionPath)) {
178
+ const installed = fs.readFileSync(versionPath, 'utf8').trim();
179
+ const pkgVersion = fs.readFileSync(pkgVersionPath, 'utf8').trim();
180
+ if (pkgVersion && pkgVersion !== installed) {
181
+ output.push(`[Mindlore: Guncelleme mevcut (${installed} → ${pkgVersion}). \`npx mindlore init\` calistirin.]`);
182
+ }
183
+ }
184
+ } catch (_err) { /* skip */ }
185
+ timings.version_check = Date.now() - tVersion;
186
+
187
+ // v0.5.4: Consolidated session payload (replaces scattered episodes/activity/alerts injection)
188
+ const tDb = Date.now();
189
+ const outputLenBeforeDb = output.reduce((s, o) => s + o.length, 0);
190
+ try {
191
+ const dbPath = path.join(baseDir, 'mindlore.db');
192
+ const tDbOpen = Date.now();
193
+ const db = tryOpenDb(dbPath);
194
+ timings.db_open = Date.now() - tDbOpen;
195
+ timings.db_integrity = 0;
196
+
197
+ if (db) {
198
+ try {
199
+ // Schema version check: warn if DB is behind expected version
200
+ const tSchema = Date.now();
201
+ try {
202
+ const { EXPECTED_SCHEMA_VERSION } = require('../dist/scripts/lib/all-migrations.js');
203
+ const row = db.prepare('SELECT MAX(version) as v FROM schema_versions').get();
204
+ const current = row?.v ?? 0;
205
+ if (current < EXPECTED_SCHEMA_VERSION) {
206
+ output.push(`[Mindlore: schema güncel değil (v${current} v${EXPECTED_SCHEMA_VERSION}). \`npx mindlore upgrade\` çalıştır.]`);
207
+ }
208
+ } catch (_schemaErr) { /* schema_versions may not exist yet */ }
209
+ timings.schema_check = Date.now() - tSchema;
210
+
211
+ loadDbContent({ db, baseDir, config, output, timings, latestDeltaContent, sessionId });
212
+ } catch (err) {
213
+ if (isCorruptionError(err)) {
214
+ recoverCorruptDb(db, dbPath, 'session-focus');
215
+ }
216
+ } finally {
217
+ try { db.close(); } catch { /* already closed by recovery */ }
218
+ }
219
+ }
220
+ } catch (_err) { /* graceful skip */ }
221
+ const outputLenAfterDb = output.reduce((s, o) => s + o.length, 0);
222
+ sourceChars += (outputLenAfterDb - outputLenBeforeDb);
223
+ timings.db_total = Date.now() - tDb;
224
+
225
+ timings.total = Date.now() - t0;
226
+ hookLog('session-focus', 'info', `timings: ${JSON.stringify(timings)}`);
227
+
228
+ // Token budget for session inject
229
+ // Defaults match DEFAULT_TOKEN_BUDGET in scripts/lib/constants.ts
230
+ const budgetConfig = config?.tokenBudget ?? {};
231
+ const maxInjectChars = (budgetConfig.sessionInject || 2000) * 4;
232
+
233
+ let joined = output.join('\n\n');
234
+ if (joined.length > maxInjectChars) {
235
+ joined = joined.slice(0, maxInjectChars) + '\n[...truncated by token budget]';
236
+ }
237
+
238
+ // v0.6.1: Daemon auto-start removed (daemon deprecated — MCP Server in v0.7)
239
+
240
+ if (joined.length > 0) {
241
+ process.stdout.write(joined + '\n');
242
+ }
243
+
244
+ const inject_tokens = Math.ceil(joined.length / 4);
245
+ const source_tokens = Math.ceil(sourceChars / 4);
246
+ return { inject_tokens, source_tokens };
247
+ }
248
+
249
+ withTelemetry('mindlore-session-focus', main).catch(err => {
250
+ hookLog('mindlore-session-focus', 'error', err?.message ?? String(err));
251
+ process.exit(0);
252
+ });
253
+
254
+ if (typeof module !== 'undefined') {
255
+ module.exports = { truncateCommits, truncateChangedFiles, getEpisodeStats, checkStaleContent };
256
+ }