mindlore 0.6.8 → 0.7.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 (209) 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 +4 -2
  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 +9 -7
  8. package/dist/scripts/cc-session-sync.js.map +1 -1
  9. package/dist/scripts/fetch-raw.js +5 -4
  10. package/dist/scripts/fetch-raw.js.map +1 -1
  11. package/dist/scripts/init.js +16 -12
  12. package/dist/scripts/init.js.map +1 -1
  13. package/dist/scripts/lib/consolidation.js +10 -10
  14. package/dist/scripts/lib/constants.d.ts +3 -0
  15. package/dist/scripts/lib/constants.d.ts.map +1 -1
  16. package/dist/scripts/lib/constants.js +11 -1
  17. package/dist/scripts/lib/constants.js.map +1 -1
  18. package/dist/scripts/lib/decay.js +9 -9
  19. package/dist/scripts/lib/episodes.js +23 -23
  20. package/dist/scripts/lib/err-msg.d.ts +2 -0
  21. package/dist/scripts/lib/err-msg.d.ts.map +1 -0
  22. package/dist/scripts/lib/err-msg.js +7 -0
  23. package/dist/scripts/lib/err-msg.js.map +1 -0
  24. package/dist/scripts/lib/mcp-namespace.d.ts +2 -0
  25. package/dist/scripts/lib/mcp-namespace.d.ts.map +1 -0
  26. package/dist/scripts/lib/mcp-namespace.js +21 -0
  27. package/dist/scripts/lib/mcp-namespace.js.map +1 -0
  28. package/dist/scripts/lib/mcp-telemetry.d.ts +11 -0
  29. package/dist/scripts/lib/mcp-telemetry.d.ts.map +1 -0
  30. package/dist/scripts/lib/mcp-telemetry.js +37 -0
  31. package/dist/scripts/lib/mcp-telemetry.js.map +1 -0
  32. package/dist/scripts/lib/mcp-tools.d.ts +10 -0
  33. package/dist/scripts/lib/mcp-tools.d.ts.map +1 -0
  34. package/dist/scripts/lib/mcp-tools.js +121 -0
  35. package/dist/scripts/lib/mcp-tools.js.map +1 -0
  36. package/dist/scripts/lib/migrations-v061.js +21 -21
  37. package/dist/scripts/lib/migrations-v062.js +11 -11
  38. package/dist/scripts/lib/migrations-v063.js +14 -14
  39. package/dist/scripts/lib/migrations-v067.js +11 -11
  40. package/dist/scripts/lib/rrf.d.ts.map +1 -1
  41. package/dist/scripts/lib/rrf.js +2 -1
  42. package/dist/scripts/lib/rrf.js.map +1 -1
  43. package/dist/scripts/lib/schema-version.js +6 -6
  44. package/dist/scripts/lib/search-cache.js +11 -11
  45. package/dist/scripts/lib/search-engine.d.ts +1 -0
  46. package/dist/scripts/lib/search-engine.d.ts.map +1 -1
  47. package/dist/scripts/lib/search-engine.js +9 -5
  48. package/dist/scripts/lib/search-engine.js.map +1 -1
  49. package/dist/scripts/lib/secure-io.d.ts +11 -0
  50. package/dist/scripts/lib/secure-io.d.ts.map +1 -0
  51. package/dist/scripts/lib/secure-io.js +26 -0
  52. package/dist/scripts/lib/secure-io.js.map +1 -0
  53. package/dist/scripts/lib/session-payload.js +7 -7
  54. package/dist/scripts/lib/slugify.d.ts +2 -0
  55. package/dist/scripts/lib/slugify.d.ts.map +1 -0
  56. package/dist/scripts/lib/slugify.js +13 -0
  57. package/dist/scripts/lib/slugify.js.map +1 -0
  58. package/dist/scripts/lib/smart-snippet.d.ts +9 -0
  59. package/dist/scripts/lib/smart-snippet.d.ts.map +1 -0
  60. package/dist/scripts/lib/smart-snippet.js +47 -0
  61. package/dist/scripts/lib/smart-snippet.js.map +1 -0
  62. package/dist/scripts/lib/tool-adapters/brief-adapter.d.ts +15 -0
  63. package/dist/scripts/lib/tool-adapters/brief-adapter.d.ts.map +1 -0
  64. package/dist/scripts/lib/tool-adapters/brief-adapter.js +66 -0
  65. package/dist/scripts/lib/tool-adapters/brief-adapter.js.map +1 -0
  66. package/dist/scripts/lib/tool-adapters/decide-adapter.d.ts +31 -0
  67. package/dist/scripts/lib/tool-adapters/decide-adapter.d.ts.map +1 -0
  68. package/dist/scripts/lib/tool-adapters/decide-adapter.js +71 -0
  69. package/dist/scripts/lib/tool-adapters/decide-adapter.js.map +1 -0
  70. package/dist/scripts/lib/tool-adapters/ingest-adapter.d.ts +16 -0
  71. package/dist/scripts/lib/tool-adapters/ingest-adapter.d.ts.map +1 -0
  72. package/dist/scripts/lib/tool-adapters/ingest-adapter.js +58 -0
  73. package/dist/scripts/lib/tool-adapters/ingest-adapter.js.map +1 -0
  74. package/dist/scripts/lib/tool-adapters/recall-adapter.d.ts +20 -0
  75. package/dist/scripts/lib/tool-adapters/recall-adapter.d.ts.map +1 -0
  76. package/dist/scripts/lib/tool-adapters/recall-adapter.js +69 -0
  77. package/dist/scripts/lib/tool-adapters/recall-adapter.js.map +1 -0
  78. package/dist/scripts/lib/tool-adapters/search-adapter.d.ts +22 -0
  79. package/dist/scripts/lib/tool-adapters/search-adapter.d.ts.map +1 -0
  80. package/dist/scripts/lib/tool-adapters/search-adapter.js +32 -0
  81. package/dist/scripts/lib/tool-adapters/search-adapter.js.map +1 -0
  82. package/dist/scripts/lib/tool-adapters/stats-adapter.d.ts +15 -0
  83. package/dist/scripts/lib/tool-adapters/stats-adapter.d.ts.map +1 -0
  84. package/dist/scripts/lib/tool-adapters/stats-adapter.js +66 -0
  85. package/dist/scripts/lib/tool-adapters/stats-adapter.js.map +1 -0
  86. package/dist/scripts/lib/triage.js +3 -3
  87. package/dist/scripts/lib/validate-manifest.d.ts +8 -0
  88. package/dist/scripts/lib/validate-manifest.d.ts.map +1 -0
  89. package/dist/scripts/lib/validate-manifest.js +80 -0
  90. package/dist/scripts/lib/validate-manifest.js.map +1 -0
  91. package/dist/scripts/maintain-cleanup.d.ts.map +1 -1
  92. package/dist/scripts/maintain-cleanup.js +3 -2
  93. package/dist/scripts/maintain-cleanup.js.map +1 -1
  94. package/dist/scripts/mcp-server.d.ts +3 -0
  95. package/dist/scripts/mcp-server.d.ts.map +1 -0
  96. package/dist/scripts/mcp-server.js +85 -0
  97. package/dist/scripts/mcp-server.js.map +1 -0
  98. package/dist/scripts/mindlore-backup.js +9 -9
  99. package/dist/scripts/mindlore-doctor.d.ts.map +1 -1
  100. package/dist/scripts/mindlore-doctor.js +4 -6
  101. package/dist/scripts/mindlore-doctor.js.map +1 -1
  102. package/dist/scripts/mindlore-fts5-index.js +12 -12
  103. package/dist/scripts/mindlore-fts5-index.js.map +1 -1
  104. package/dist/scripts/mindlore-health-check.d.ts.map +1 -1
  105. package/dist/scripts/mindlore-health-check.js +2 -2
  106. package/dist/scripts/mindlore-health-check.js.map +1 -1
  107. package/dist/scripts/validate-manifest-cli.d.ts +2 -0
  108. package/dist/scripts/validate-manifest-cli.d.ts.map +1 -0
  109. package/dist/scripts/validate-manifest-cli.js +38 -0
  110. package/dist/scripts/validate-manifest-cli.js.map +1 -0
  111. package/dist/tests/cc-memory-sync.test.js +3 -3
  112. package/dist/tests/chunks-migration.test.js +1 -1
  113. package/dist/tests/compaction-snapshot.test.js +2 -2
  114. package/dist/tests/consolidation.test.js +3 -3
  115. package/dist/tests/decay.test.js +9 -9
  116. package/dist/tests/diary.test.js +4 -4
  117. package/dist/tests/episode-file.test.js +2 -1
  118. package/dist/tests/episode-file.test.js.map +1 -1
  119. package/dist/tests/episodes-inject.test.js +9 -9
  120. package/dist/tests/err-msg.test.d.ts +2 -0
  121. package/dist/tests/err-msg.test.d.ts.map +1 -0
  122. package/dist/tests/err-msg.test.js +24 -0
  123. package/dist/tests/err-msg.test.js.map +1 -0
  124. package/dist/tests/fetch-raw.test.js +1 -2
  125. package/dist/tests/fetch-raw.test.js.map +1 -1
  126. package/dist/tests/fts5.test.js +75 -75
  127. package/dist/tests/fuzzy.test.js +1 -1
  128. package/dist/tests/git-snapshot.test.js +3 -5
  129. package/dist/tests/git-snapshot.test.js.map +1 -1
  130. package/dist/tests/helpers/db.d.ts +1 -2
  131. package/dist/tests/helpers/db.d.ts.map +1 -1
  132. package/dist/tests/helpers/db.js +18 -26
  133. package/dist/tests/helpers/db.js.map +1 -1
  134. package/dist/tests/manifest-v2.test.d.ts +2 -0
  135. package/dist/tests/manifest-v2.test.d.ts.map +1 -0
  136. package/dist/tests/manifest-v2.test.js +67 -0
  137. package/dist/tests/manifest-v2.test.js.map +1 -0
  138. package/dist/tests/mcp-server.test.d.ts +2 -0
  139. package/dist/tests/mcp-server.test.d.ts.map +1 -0
  140. package/dist/tests/mcp-server.test.js +118 -0
  141. package/dist/tests/mcp-server.test.js.map +1 -0
  142. package/dist/tests/mcp-tools.test.d.ts +2 -0
  143. package/dist/tests/mcp-tools.test.d.ts.map +1 -0
  144. package/dist/tests/mcp-tools.test.js +243 -0
  145. package/dist/tests/mcp-tools.test.js.map +1 -0
  146. package/dist/tests/migrations-v053.test.js +16 -16
  147. package/dist/tests/migrations-v061.test.js +10 -10
  148. package/dist/tests/migrations-v063.test.js +4 -4
  149. package/dist/tests/migrations-v068.test.js +6 -3
  150. package/dist/tests/migrations-v068.test.js.map +1 -1
  151. package/dist/tests/nomination-counts.test.js +6 -6
  152. package/dist/tests/nomination-counts.test.js.map +1 -1
  153. package/dist/tests/pre-compact.test.js +2 -1
  154. package/dist/tests/pre-compact.test.js.map +1 -1
  155. package/dist/tests/recall-telemetry.test.js +12 -11
  156. package/dist/tests/recall-telemetry.test.js.map +1 -1
  157. package/dist/tests/rrf.test.js +3 -3
  158. package/dist/tests/search-engine.test.js +1 -1
  159. package/dist/tests/search-hook.test.js +2 -2
  160. package/dist/tests/search-offload.test.js +1 -1
  161. package/dist/tests/search-offload.test.js.map +1 -1
  162. package/dist/tests/secure-io.test.d.ts +2 -0
  163. package/dist/tests/secure-io.test.d.ts.map +1 -0
  164. package/dist/tests/secure-io.test.js +65 -0
  165. package/dist/tests/secure-io.test.js.map +1 -0
  166. package/dist/tests/session-payload.test.js +1 -1
  167. package/dist/tests/session-summary.test.js +1 -1
  168. package/dist/tests/similarity.test.js +1 -1
  169. package/dist/tests/skill-path-resolution.test.js +22 -3
  170. package/dist/tests/skill-path-resolution.test.js.map +1 -1
  171. package/dist/tests/smart-snippet.test.d.ts +2 -0
  172. package/dist/tests/smart-snippet.test.d.ts.map +1 -0
  173. package/dist/tests/smart-snippet.test.js +67 -0
  174. package/dist/tests/smart-snippet.test.js.map +1 -0
  175. package/dist/tests/triage.test.js +1 -1
  176. package/hooks/lib/constants.cjs +15 -15
  177. package/hooks/lib/mindlore-common.cjs +975 -974
  178. package/hooks/mindlore-cwd-changed.cjs +57 -57
  179. package/hooks/mindlore-decision-detector.cjs +54 -54
  180. package/hooks/mindlore-dont-repeat.cjs +222 -222
  181. package/hooks/mindlore-fts5-sync.cjs +98 -97
  182. package/hooks/mindlore-index.cjs +230 -229
  183. package/hooks/mindlore-model-router.cjs +54 -54
  184. package/hooks/mindlore-post-compact.cjs +69 -69
  185. package/hooks/mindlore-post-read.cjs +106 -106
  186. package/hooks/mindlore-pre-compact.cjs +154 -154
  187. package/hooks/mindlore-read-guard.cjs +105 -105
  188. package/hooks/mindlore-research-guard.cjs +176 -176
  189. package/hooks/mindlore-search.cjs +200 -200
  190. package/hooks/mindlore-session-end.cjs +510 -509
  191. package/hooks/mindlore-session-focus.cjs +256 -256
  192. package/package.json +77 -75
  193. package/plugin.json +8 -1
  194. package/skills/mindlore-diary/SKILL.md +85 -85
  195. package/skills/mindlore-evolve/SKILL.md +126 -126
  196. package/skills/mindlore-explore/SKILL.md +109 -109
  197. package/skills/mindlore-ingest/SKILL.md +195 -195
  198. package/skills/mindlore-maintain/SKILL.md +125 -125
  199. package/skills/mindlore-query/SKILL.md +151 -151
  200. package/skills/mindlore-reflect/SKILL.md +141 -141
  201. package/skills/mindlore-stats/SKILL.md +106 -106
  202. package/templates/INDEX.md +14 -14
  203. package/templates/SCHEMA.md +292 -292
  204. package/templates/config.json +1 -1
  205. package/templates/extraction/article.md +15 -15
  206. package/templates/extraction/changelog.md +15 -15
  207. package/templates/extraction/default.md +15 -15
  208. package/templates/extraction/docs.md +15 -15
  209. package/templates/extraction/github-repo.md +17 -17
@@ -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
+ });