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