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.
- package/README.md +259 -259
- package/SCHEMA.md +292 -292
- package/dist/scripts/cc-memory-bulk-sync.d.ts.map +1 -1
- package/dist/scripts/cc-memory-bulk-sync.js +47 -42
- package/dist/scripts/cc-memory-bulk-sync.js.map +1 -1
- package/dist/scripts/cc-session-sync.d.ts.map +1 -1
- package/dist/scripts/cc-session-sync.js +58 -48
- package/dist/scripts/cc-session-sync.js.map +1 -1
- package/dist/scripts/init.js +8 -8
- package/dist/scripts/init.js.map +1 -1
- package/dist/scripts/lib/all-migrations.d.ts.map +1 -1
- package/dist/scripts/lib/all-migrations.js +7 -1
- package/dist/scripts/lib/all-migrations.js.map +1 -1
- package/dist/scripts/lib/consolidation.d.ts +4 -3
- package/dist/scripts/lib/consolidation.d.ts.map +1 -1
- package/dist/scripts/lib/consolidation.js +10 -10
- package/dist/scripts/lib/consolidation.js.map +1 -1
- package/dist/scripts/lib/constants.d.ts +1 -7
- package/dist/scripts/lib/constants.d.ts.map +1 -1
- package/dist/scripts/lib/constants.js +2 -9
- package/dist/scripts/lib/constants.js.map +1 -1
- package/dist/scripts/lib/db-helpers.d.ts +0 -15
- package/dist/scripts/lib/db-helpers.d.ts.map +1 -1
- package/dist/scripts/lib/db-helpers.js +1 -51
- package/dist/scripts/lib/db-helpers.js.map +1 -1
- package/dist/scripts/lib/decay.d.ts.map +1 -1
- package/dist/scripts/lib/decay.js +9 -9
- package/dist/scripts/lib/decay.js.map +1 -1
- package/dist/scripts/lib/episodes.js +23 -23
- package/dist/scripts/lib/migrations-v061.js +21 -21
- package/dist/scripts/lib/migrations-v062.js +11 -11
- package/dist/scripts/lib/migrations-v063.js +14 -14
- package/dist/scripts/lib/migrations-v067.d.ts +7 -0
- package/dist/scripts/lib/migrations-v067.d.ts.map +1 -0
- package/dist/scripts/lib/migrations-v067.js +50 -0
- package/dist/scripts/lib/migrations-v067.js.map +1 -0
- package/dist/scripts/lib/migrations-v068.d.ts +3 -0
- package/dist/scripts/lib/migrations-v068.d.ts.map +1 -0
- package/dist/scripts/lib/migrations-v068.js +37 -0
- package/dist/scripts/lib/migrations-v068.js.map +1 -0
- package/dist/scripts/lib/migrations.d.ts.map +1 -1
- package/dist/scripts/lib/migrations.js +0 -15
- package/dist/scripts/lib/migrations.js.map +1 -1
- package/dist/scripts/lib/schema-version.js +6 -6
- package/dist/scripts/lib/search-cache.js +11 -11
- package/dist/scripts/lib/session-payload.d.ts +9 -1
- package/dist/scripts/lib/session-payload.d.ts.map +1 -1
- package/dist/scripts/lib/session-payload.js +11 -10
- package/dist/scripts/lib/session-payload.js.map +1 -1
- package/dist/scripts/lib/triage.js +3 -3
- package/dist/scripts/mindlore-backup.js +9 -9
- package/dist/scripts/mindlore-fts5-index.d.ts +1 -2
- package/dist/scripts/mindlore-fts5-index.d.ts.map +1 -1
- package/dist/scripts/mindlore-fts5-index.js +12 -64
- package/dist/scripts/mindlore-fts5-index.js.map +1 -1
- package/dist/scripts/mindlore-health-check.d.ts.map +1 -1
- package/dist/scripts/mindlore-health-check.js +0 -11
- package/dist/scripts/mindlore-health-check.js.map +1 -1
- package/dist/tests/cc-memory-bulk-sync.test.js +23 -0
- package/dist/tests/cc-memory-bulk-sync.test.js.map +1 -1
- package/dist/tests/cc-session-sync.test.js +25 -0
- package/dist/tests/cc-session-sync.test.js.map +1 -1
- package/dist/tests/compaction-snapshot.test.js +2 -2
- package/dist/tests/consolidation.test.js +5 -5
- package/dist/tests/consolidation.test.js.map +1 -1
- package/dist/tests/decay.test.js +9 -9
- package/dist/tests/diary.test.js +4 -4
- package/dist/tests/episode-kind-constant.test.d.ts +2 -0
- package/dist/tests/episode-kind-constant.test.d.ts.map +1 -0
- package/dist/tests/episode-kind-constant.test.js +28 -0
- package/dist/tests/episode-kind-constant.test.js.map +1 -0
- package/dist/tests/episodes-inject.test.js +14 -14
- package/dist/tests/episodes-inject.test.js.map +1 -1
- package/dist/tests/fts5.test.js +66 -125
- package/dist/tests/fts5.test.js.map +1 -1
- package/dist/tests/globalSetup.d.ts +2 -0
- package/dist/tests/globalSetup.d.ts.map +1 -0
- package/dist/tests/globalSetup.js +36 -0
- package/dist/tests/globalSetup.js.map +1 -0
- package/dist/tests/helpers/db.d.ts +13 -5
- package/dist/tests/helpers/db.d.ts.map +1 -1
- package/dist/tests/helpers/db.js +61 -33
- package/dist/tests/helpers/db.js.map +1 -1
- package/dist/tests/lesson-graduation.test.d.ts +2 -0
- package/dist/tests/lesson-graduation.test.d.ts.map +1 -0
- package/dist/tests/lesson-graduation.test.js +83 -0
- package/dist/tests/lesson-graduation.test.js.map +1 -0
- package/dist/tests/migrations-v053.test.js +16 -16
- package/dist/tests/migrations-v061.test.js +10 -10
- package/dist/tests/migrations-v063.test.js +2 -2
- package/dist/tests/migrations-v067.test.d.ts +2 -0
- package/dist/tests/migrations-v067.test.d.ts.map +1 -0
- package/dist/tests/migrations-v067.test.js +115 -0
- package/dist/tests/migrations-v067.test.js.map +1 -0
- package/dist/tests/migrations-v068.test.d.ts +2 -0
- package/dist/tests/migrations-v068.test.d.ts.map +1 -0
- package/dist/tests/migrations-v068.test.js +53 -0
- package/dist/tests/migrations-v068.test.js.map +1 -0
- package/dist/tests/nomination-counts.test.d.ts +2 -0
- package/dist/tests/nomination-counts.test.d.ts.map +1 -0
- package/dist/tests/nomination-counts.test.js +51 -0
- package/dist/tests/nomination-counts.test.js.map +1 -0
- package/dist/tests/recall-telemetry.test.js +8 -8
- package/dist/tests/schema-version.test.js +3 -7
- package/dist/tests/schema-version.test.js.map +1 -1
- package/dist/tests/search-hook.test.js +2 -2
- package/dist/tests/sec-regression.test.js +0 -50
- package/dist/tests/sec-regression.test.js.map +1 -1
- package/dist/tests/session-end-cleanup.test.d.ts +2 -0
- package/dist/tests/session-end-cleanup.test.d.ts.map +1 -0
- package/dist/tests/session-end-cleanup.test.js +59 -0
- package/dist/tests/session-end-cleanup.test.js.map +1 -0
- package/dist/tests/session-focus.test.js +69 -10
- package/dist/tests/session-focus.test.js.map +1 -1
- package/dist/tests/session-payload.test.js +11 -11
- package/dist/tests/session-payload.test.js.map +1 -1
- package/dist/tests/session-summary.test.js +2 -2
- package/dist/tests/session-summary.test.js.map +1 -1
- package/hooks/lib/constants.cjs +15 -0
- package/hooks/lib/mindlore-common.cjs +974 -1004
- package/hooks/mindlore-cwd-changed.cjs +57 -57
- package/hooks/mindlore-decision-detector.cjs +54 -54
- package/hooks/mindlore-dont-repeat.cjs +222 -222
- package/hooks/mindlore-fts5-sync.cjs +97 -88
- package/hooks/mindlore-index.cjs +229 -229
- package/hooks/mindlore-model-router.cjs +54 -54
- package/hooks/mindlore-post-compact.cjs +69 -69
- package/hooks/mindlore-post-read.cjs +106 -106
- package/hooks/mindlore-pre-compact.cjs +154 -154
- package/hooks/mindlore-read-guard.cjs +105 -105
- package/hooks/mindlore-research-guard.cjs +176 -176
- package/hooks/mindlore-search.cjs +200 -200
- package/hooks/mindlore-session-end.cjs +509 -523
- package/hooks/mindlore-session-focus.cjs +256 -245
- package/package.json +75 -78
- package/plugin.json +1 -1
- package/skills/mindlore-diary/SKILL.md +85 -85
- package/skills/mindlore-evolve/SKILL.md +126 -126
- package/skills/mindlore-explore/SKILL.md +109 -109
- package/skills/mindlore-ingest/SKILL.md +195 -195
- package/skills/mindlore-maintain/SKILL.md +125 -125
- package/skills/mindlore-query/SKILL.md +151 -151
- package/skills/mindlore-reflect/SKILL.md +141 -131
- package/skills/mindlore-stats/SKILL.md +106 -106
- package/templates/INDEX.md +14 -14
- package/templates/SCHEMA.md +292 -292
- package/templates/config.json +1 -1
- package/templates/extraction/article.md +15 -15
- package/templates/extraction/changelog.md +15 -15
- package/templates/extraction/default.md +15 -15
- package/templates/extraction/docs.md +15 -15
- package/templates/extraction/github-repo.md +17 -17
- package/dist/scripts/lib/daemon.d.ts +0 -16
- package/dist/scripts/lib/daemon.d.ts.map +0 -1
- package/dist/scripts/lib/daemon.js +0 -133
- package/dist/scripts/lib/daemon.js.map +0 -1
- package/dist/scripts/lib/embedding.d.ts +0 -5
- package/dist/scripts/lib/embedding.d.ts.map +0 -1
- package/dist/scripts/lib/embedding.js +0 -44
- package/dist/scripts/lib/embedding.js.map +0 -1
- package/dist/scripts/mindlore-daemon.d.ts +0 -2
- package/dist/scripts/mindlore-daemon.d.ts.map +0 -1
- package/dist/scripts/mindlore-daemon.js +0 -117
- package/dist/scripts/mindlore-daemon.js.map +0 -1
- package/dist/tests/daemon-integration.test.d.ts +0 -2
- package/dist/tests/daemon-integration.test.d.ts.map +0 -1
- package/dist/tests/daemon-integration.test.js +0 -37
- package/dist/tests/daemon-integration.test.js.map +0 -1
- package/dist/tests/daemon.test.d.ts +0 -2
- package/dist/tests/daemon.test.d.ts.map +0 -1
- package/dist/tests/daemon.test.js +0 -187
- package/dist/tests/daemon.test.js.map +0 -1
- package/dist/tests/embedding-hf-integration.test.d.ts +0 -2
- package/dist/tests/embedding-hf-integration.test.d.ts.map +0 -1
- package/dist/tests/embedding-hf-integration.test.js +0 -52
- package/dist/tests/embedding-hf-integration.test.js.map +0 -1
- package/dist/tests/embedding.test.d.ts +0 -6
- package/dist/tests/embedding.test.d.ts.map +0 -1
- package/dist/tests/embedding.test.js +0 -71
- package/dist/tests/embedding.test.js.map +0 -1
- package/dist/tests/sqlite-vec-v12.test.d.ts +0 -2
- package/dist/tests/sqlite-vec-v12.test.d.ts.map +0 -1
- package/dist/tests/sqlite-vec-v12.test.js +0 -72
- 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
|
+
});
|