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,200 +1,200 @@
1
- #!/usr/bin/env node
2
- 'use strict';
3
-
4
- /**
5
- * mindlore-search — UserPromptSubmit hook
6
- *
7
- * Thin wrapper over search-engine.ts pipeline.
8
- * Extracts keywords from user prompt, delegates search to modular engine,
9
- * injects top results with description + headings.
10
- */
11
-
12
- const fs = require('fs');
13
- const path = require('path');
14
- const { getAllDbs, openDatabase, extractHeadings, readHookStdin, readConfig, hookLog, incrementRecallCount, withTelemetry } = require('./lib/mindlore-common.cjs');
15
-
16
- const MAX_RESULTS = 3;
17
- const MIN_QUERY_WORDS = 3;
18
-
19
- let searchEngineMod;
20
- try {
21
- searchEngineMod = require('../dist/scripts/lib/search-engine.js');
22
- } catch (_err) {
23
- // search-engine not built yet
24
- }
25
-
26
- let SearchCacheMod;
27
- try {
28
- SearchCacheMod = require('../dist/scripts/lib/search-cache.js');
29
- } catch (_err) {
30
- // search-cache not built yet
31
- }
32
-
33
- function main() {
34
- const userMessage = readHookStdin(['prompt', 'content', 'message', 'query']);
35
- if (!userMessage || userMessage.length < MIN_QUERY_WORDS) return;
36
-
37
- const dbPaths = getAllDbs();
38
- if (dbPaths.length === 0) return;
39
-
40
- if (!searchEngineMod) {
41
- hookLog('search', 'warn', 'search-engine module not available — skipping');
42
- return;
43
- }
44
-
45
- const project = path.basename(process.cwd());
46
- const config = readConfig(path.dirname(dbPaths[0]));
47
- const synonyms = (config && config.synonyms) ? config.synonyms : {};
48
-
49
- // Read session_id from stdin for throttling
50
- let sessionId;
51
- try {
52
- const stdinData = JSON.parse(process.env.CLAUDE_HOOK_STDIN || '{}');
53
- sessionId = stdinData.session_id || 'unknown';
54
- } catch (_) {
55
- sessionId = 'unknown';
56
- }
57
-
58
- const allResults = [];
59
- for (const dbPath of dbPaths) {
60
- const db = openDatabase(dbPath);
61
- if (!db) continue;
62
- try {
63
- // Cache + throttle
64
- let cache;
65
- let effectiveMax = MAX_RESULTS;
66
- if (SearchCacheMod) {
67
- cache = new SearchCacheMod.SearchCache(db, { ttlMs: 300000 });
68
- const throttle = new SearchCacheMod.SearchThrottle(db);
69
- const callCount = throttle.incrementCallCount(sessionId);
70
- effectiveMax = throttle.getMaxResults(callCount);
71
- if (effectiveMax === 0) {
72
- hookLog('search', 'info', `Throttled (call #${callCount})`);
73
- db.close();
74
- continue;
75
- }
76
- const cached = cache.get(userMessage);
77
- if (cached) {
78
- const baseDir = path.dirname(dbPath);
79
- for (const r of cached) allResults.push({ ...r, baseDir });
80
- db.close();
81
- continue;
82
- }
83
- }
84
-
85
- const results = searchEngineMod.search(db, userMessage, {
86
- project,
87
- maxResults: effectiveMax,
88
- synonyms,
89
- });
90
-
91
- if (cache) cache.set(userMessage, results);
92
-
93
- const baseDir = path.dirname(dbPath);
94
- for (const r of results) {
95
- allResults.push({ ...r, baseDir });
96
- }
97
-
98
- // Recall count inside loop — avoid reopening DB
99
- try {
100
- const txn = db.transaction(() => {
101
- for (const r of results) incrementRecallCount(db, r.path);
102
- });
103
- txn();
104
- } catch (_e) { /* graceful */ }
105
- } catch (err) {
106
- hookLog('search', 'warn', `Search error: ${err?.message || err}`);
107
- } finally {
108
- db.close();
109
- }
110
- }
111
-
112
- // Deduplicate by full path
113
- const seen = new Set();
114
- const unique = [];
115
- for (const r of allResults) {
116
- const normalized = path.resolve(r.path);
117
- if (!seen.has(normalized)) {
118
- seen.add(normalized);
119
- unique.push(r);
120
- }
121
- }
122
-
123
- // Sort by score descending, take top N
124
- unique.sort((a, b) => b.score - a.score);
125
- const relevant = unique.slice(0, MAX_RESULTS);
126
- if (relevant.length === 0) return;
127
-
128
- // Token budget from config
129
- const budget = (config && config.tokenBudget) || {};
130
- const perResultChars = ((budget.perResult || 500) * 4);
131
- const totalChars = ((budget.searchResults || 1500) * 4);
132
-
133
- // Build output
134
- const output = [];
135
- let totalUsed = 0;
136
- for (const r of relevant) {
137
- if (totalUsed >= totalChars) break;
138
- const relativePath = path.relative(r.baseDir, r.path).replace(/\\/g, '/');
139
-
140
- let headings = [];
141
- const contentStr = r.content || '';
142
- if (contentStr) {
143
- try {
144
- headings = extractHeadings(contentStr, 3);
145
- } catch (_err) { /* skip */ }
146
- }
147
-
148
- const category = r.category || path.dirname(relativePath).split('/')[0];
149
- const title = r.title || r.slug || path.basename(r.path, '.md');
150
- const description = r.description || '';
151
-
152
- const headingStr = headings.length > 0 ? `\nBasliklar: ${headings.join(', ')}` : '';
153
- const tagsStr = r.tags ? `\nTags: ${r.tags}` : '';
154
- const snippetOrDesc = r.snippet || description;
155
- const entry = `[Mindlore: ${category}/${title}] ${snippetOrDesc}\nDosya: ${relativePath}${tagsStr}${headingStr}`;
156
- const truncated = entry.slice(0, perResultChars);
157
- totalUsed += truncated.length;
158
- output.push(truncated);
159
- }
160
-
161
- if (output.length > 0) {
162
- let outputStr = output.join('\n\n') + '\n';
163
-
164
- const OFFLOAD_THRESHOLD = 10240;
165
- if (outputStr.length > OFFLOAD_THRESHOLD) {
166
- const baseDir = path.dirname(dbPaths[0]);
167
- const tmpDir = path.join(baseDir, 'tmp');
168
- fs.mkdirSync(tmpDir, { recursive: true });
169
-
170
- try {
171
- const oneHourAgo = Date.now() - 3600000;
172
- const files = fs.readdirSync(tmpDir)
173
- .filter(f => f.startsWith('search-'))
174
- .map(f => ({ name: f, mtime: fs.statSync(path.join(tmpDir, f)).mtimeMs }))
175
- .sort((a, b) => b.mtime - a.mtime);
176
- for (let i = 0; i < files.length; i++) {
177
- if (i >= 20 || files[i].mtime < oneHourAgo) {
178
- try { fs.unlinkSync(path.join(tmpDir, files[i].name)); } catch { /* ignore */ }
179
- }
180
- }
181
- } catch { /* cleanup is best-effort */ }
182
- const fileName = `search-${Date.now()}.md`;
183
- const filePath = path.join(tmpDir, fileName);
184
- fs.writeFileSync(filePath, outputStr, 'utf8');
185
-
186
- const summary = outputStr.slice(0, 500).replace(/\n/g, ' ').trim();
187
- outputStr = `[Mindlore Search: ${outputStr.length} chars offloaded to ${filePath}]\n` +
188
- `Summary: ${summary}...\n` +
189
- `[Read full results: ${filePath}]`;
190
- hookLog('search', 'info', 'offloaded to tmp/ (' + outputStr.length + ' chars)');
191
- }
192
-
193
- process.stdout.write(outputStr);
194
- }
195
- }
196
-
197
- withTelemetry('mindlore-search', main).catch(err => {
198
- hookLog('mindlore-search', 'error', err?.message ?? String(err));
199
- process.exit(0);
200
- });
1
+ #!/usr/bin/env node
2
+ 'use strict';
3
+
4
+ /**
5
+ * mindlore-search — UserPromptSubmit hook
6
+ *
7
+ * Thin wrapper over search-engine.ts pipeline.
8
+ * Extracts keywords from user prompt, delegates search to modular engine,
9
+ * injects top results with description + headings.
10
+ */
11
+
12
+ const fs = require('fs');
13
+ const path = require('path');
14
+ const { getAllDbs, openDatabase, extractHeadings, readHookStdin, readConfig, hookLog, incrementRecallCount, withTelemetry } = require('./lib/mindlore-common.cjs');
15
+
16
+ const MAX_RESULTS = 3;
17
+ const MIN_QUERY_WORDS = 3;
18
+
19
+ let searchEngineMod;
20
+ try {
21
+ searchEngineMod = require('../dist/scripts/lib/search-engine.js');
22
+ } catch (_err) {
23
+ // search-engine not built yet
24
+ }
25
+
26
+ let SearchCacheMod;
27
+ try {
28
+ SearchCacheMod = require('../dist/scripts/lib/search-cache.js');
29
+ } catch (_err) {
30
+ // search-cache not built yet
31
+ }
32
+
33
+ function main() {
34
+ const userMessage = readHookStdin(['prompt', 'content', 'message', 'query']);
35
+ if (!userMessage || userMessage.length < MIN_QUERY_WORDS) return;
36
+
37
+ const dbPaths = getAllDbs();
38
+ if (dbPaths.length === 0) return;
39
+
40
+ if (!searchEngineMod) {
41
+ hookLog('search', 'warn', 'search-engine module not available — skipping');
42
+ return;
43
+ }
44
+
45
+ const project = path.basename(process.cwd());
46
+ const config = readConfig(path.dirname(dbPaths[0]));
47
+ const synonyms = (config && config.synonyms) ? config.synonyms : {};
48
+
49
+ // Read session_id from stdin for throttling
50
+ let sessionId;
51
+ try {
52
+ const stdinData = JSON.parse(process.env.CLAUDE_HOOK_STDIN || '{}');
53
+ sessionId = stdinData.session_id || 'unknown';
54
+ } catch (_) {
55
+ sessionId = 'unknown';
56
+ }
57
+
58
+ const allResults = [];
59
+ for (const dbPath of dbPaths) {
60
+ const db = openDatabase(dbPath);
61
+ if (!db) continue;
62
+ try {
63
+ // Cache + throttle
64
+ let cache;
65
+ let effectiveMax = MAX_RESULTS;
66
+ if (SearchCacheMod) {
67
+ cache = new SearchCacheMod.SearchCache(db, { ttlMs: 300000 });
68
+ const throttle = new SearchCacheMod.SearchThrottle(db);
69
+ const callCount = throttle.incrementCallCount(sessionId);
70
+ effectiveMax = throttle.getMaxResults(callCount);
71
+ if (effectiveMax === 0) {
72
+ hookLog('search', 'info', `Throttled (call #${callCount})`);
73
+ db.close();
74
+ continue;
75
+ }
76
+ const cached = cache.get(userMessage);
77
+ if (cached) {
78
+ const baseDir = path.dirname(dbPath);
79
+ for (const r of cached) allResults.push({ ...r, baseDir });
80
+ db.close();
81
+ continue;
82
+ }
83
+ }
84
+
85
+ const results = searchEngineMod.search(db, userMessage, {
86
+ project,
87
+ maxResults: effectiveMax,
88
+ synonyms,
89
+ });
90
+
91
+ if (cache) cache.set(userMessage, results);
92
+
93
+ const baseDir = path.dirname(dbPath);
94
+ for (const r of results) {
95
+ allResults.push({ ...r, baseDir });
96
+ }
97
+
98
+ // Recall count inside loop — avoid reopening DB
99
+ try {
100
+ const txn = db.transaction(() => {
101
+ for (const r of results) incrementRecallCount(db, r.path);
102
+ });
103
+ txn();
104
+ } catch (_e) { /* graceful */ }
105
+ } catch (err) {
106
+ hookLog('search', 'warn', `Search error: ${err?.message || err}`);
107
+ } finally {
108
+ db.close();
109
+ }
110
+ }
111
+
112
+ // Deduplicate by full path
113
+ const seen = new Set();
114
+ const unique = [];
115
+ for (const r of allResults) {
116
+ const normalized = path.resolve(r.path);
117
+ if (!seen.has(normalized)) {
118
+ seen.add(normalized);
119
+ unique.push(r);
120
+ }
121
+ }
122
+
123
+ // Sort by score descending, take top N
124
+ unique.sort((a, b) => b.score - a.score);
125
+ const relevant = unique.slice(0, MAX_RESULTS);
126
+ if (relevant.length === 0) return;
127
+
128
+ // Token budget from config
129
+ const budget = (config && config.tokenBudget) || {};
130
+ const perResultChars = ((budget.perResult || 500) * 4);
131
+ const totalChars = ((budget.searchResults || 1500) * 4);
132
+
133
+ // Build output
134
+ const output = [];
135
+ let totalUsed = 0;
136
+ for (const r of relevant) {
137
+ if (totalUsed >= totalChars) break;
138
+ const relativePath = path.relative(r.baseDir, r.path).replace(/\\/g, '/');
139
+
140
+ let headings = [];
141
+ const contentStr = r.content || '';
142
+ if (contentStr) {
143
+ try {
144
+ headings = extractHeadings(contentStr, 3);
145
+ } catch (_err) { /* skip */ }
146
+ }
147
+
148
+ const category = r.category || path.dirname(relativePath).split('/')[0];
149
+ const title = r.title || r.slug || path.basename(r.path, '.md');
150
+ const description = r.description || '';
151
+
152
+ const headingStr = headings.length > 0 ? `\nBasliklar: ${headings.join(', ')}` : '';
153
+ const tagsStr = r.tags ? `\nTags: ${r.tags}` : '';
154
+ const snippetOrDesc = r.snippet || description;
155
+ const entry = `[Mindlore: ${category}/${title}] ${snippetOrDesc}\nDosya: ${relativePath}${tagsStr}${headingStr}`;
156
+ const truncated = entry.slice(0, perResultChars);
157
+ totalUsed += truncated.length;
158
+ output.push(truncated);
159
+ }
160
+
161
+ if (output.length > 0) {
162
+ let outputStr = output.join('\n\n') + '\n';
163
+
164
+ const OFFLOAD_THRESHOLD = 10240;
165
+ if (outputStr.length > OFFLOAD_THRESHOLD) {
166
+ const baseDir = path.dirname(dbPaths[0]);
167
+ const tmpDir = path.join(baseDir, 'tmp');
168
+ fs.mkdirSync(tmpDir, { recursive: true });
169
+
170
+ try {
171
+ const oneHourAgo = Date.now() - 3600000;
172
+ const files = fs.readdirSync(tmpDir)
173
+ .filter(f => f.startsWith('search-'))
174
+ .map(f => ({ name: f, mtime: fs.statSync(path.join(tmpDir, f)).mtimeMs }))
175
+ .sort((a, b) => b.mtime - a.mtime);
176
+ for (let i = 0; i < files.length; i++) {
177
+ if (i >= 20 || files[i].mtime < oneHourAgo) {
178
+ try { fs.unlinkSync(path.join(tmpDir, files[i].name)); } catch { /* ignore */ }
179
+ }
180
+ }
181
+ } catch { /* cleanup is best-effort */ }
182
+ const fileName = `search-${Date.now()}.md`;
183
+ const filePath = path.join(tmpDir, fileName);
184
+ fs.writeFileSync(filePath, outputStr, 'utf8');
185
+
186
+ const summary = outputStr.slice(0, 500).replace(/\n/g, ' ').trim();
187
+ outputStr = `[Mindlore Search: ${outputStr.length} chars offloaded to ${filePath}]\n` +
188
+ `Summary: ${summary}...\n` +
189
+ `[Read full results: ${filePath}]`;
190
+ hookLog('search', 'info', 'offloaded to tmp/ (' + outputStr.length + ' chars)');
191
+ }
192
+
193
+ process.stdout.write(outputStr);
194
+ }
195
+ }
196
+
197
+ withTelemetry('mindlore-search', main).catch(err => {
198
+ hookLog('mindlore-search', 'error', err?.message ?? String(err));
199
+ process.exit(0);
200
+ });