mindlore 0.4.2 → 0.5.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.
- package/README.md +4 -2
- package/dist/scripts/lib/constants.d.ts +4 -0
- package/dist/scripts/lib/constants.d.ts.map +1 -1
- package/dist/scripts/lib/constants.js +5 -1
- package/dist/scripts/lib/constants.js.map +1 -1
- package/dist/scripts/lib/db-helpers.d.ts +15 -0
- package/dist/scripts/lib/db-helpers.d.ts.map +1 -1
- package/dist/scripts/lib/db-helpers.js +51 -0
- package/dist/scripts/lib/db-helpers.js.map +1 -1
- package/dist/scripts/lib/embedding.d.ts +5 -0
- package/dist/scripts/lib/embedding.d.ts.map +1 -0
- package/dist/scripts/lib/embedding.js +44 -0
- package/dist/scripts/lib/embedding.js.map +1 -0
- package/dist/scripts/lib/hybrid-search.d.ts +62 -0
- package/dist/scripts/lib/hybrid-search.d.ts.map +1 -0
- package/dist/scripts/lib/hybrid-search.js +150 -0
- package/dist/scripts/lib/hybrid-search.js.map +1 -0
- package/dist/scripts/lib/migrations.d.ts +3 -0
- package/dist/scripts/lib/migrations.d.ts.map +1 -0
- package/dist/scripts/lib/migrations.js +38 -0
- package/dist/scripts/lib/migrations.js.map +1 -0
- package/dist/scripts/lib/schema-version.d.ts +13 -0
- package/dist/scripts/lib/schema-version.d.ts.map +1 -0
- package/dist/scripts/lib/schema-version.js +37 -0
- package/dist/scripts/lib/schema-version.js.map +1 -0
- package/dist/scripts/lib/synonym.d.ts +4 -0
- package/dist/scripts/lib/synonym.d.ts.map +1 -0
- package/dist/scripts/lib/synonym.js +37 -0
- package/dist/scripts/lib/synonym.js.map +1 -0
- package/dist/scripts/mindlore-fts5-index.d.ts +2 -1
- package/dist/scripts/mindlore-fts5-index.d.ts.map +1 -1
- package/dist/scripts/mindlore-fts5-index.js +57 -5
- package/dist/scripts/mindlore-fts5-index.js.map +1 -1
- package/dist/scripts/mindlore-fts5-search.d.ts +3 -2
- package/dist/scripts/mindlore-fts5-search.d.ts.map +1 -1
- package/dist/scripts/mindlore-fts5-search.js +89 -35
- package/dist/scripts/mindlore-fts5-search.js.map +1 -1
- package/dist/scripts/mindlore-health-check.js +52 -0
- package/dist/scripts/mindlore-health-check.js.map +1 -1
- package/dist/tests/embedding.test.d.ts +6 -0
- package/dist/tests/embedding.test.d.ts.map +1 -0
- package/dist/tests/embedding.test.js +71 -0
- package/dist/tests/embedding.test.js.map +1 -0
- package/dist/tests/fts5.test.js +82 -0
- package/dist/tests/fts5.test.js.map +1 -1
- package/dist/tests/helpers/db.d.ts +5 -0
- package/dist/tests/helpers/db.d.ts.map +1 -1
- package/dist/tests/helpers/db.js +19 -0
- package/dist/tests/helpers/db.js.map +1 -1
- package/dist/tests/hook-smoke.test.js +2 -2
- package/dist/tests/hook-smoke.test.js.map +1 -1
- package/dist/tests/hybrid-search.test.d.ts +2 -0
- package/dist/tests/hybrid-search.test.d.ts.map +1 -0
- package/dist/tests/hybrid-search.test.js +114 -0
- package/dist/tests/hybrid-search.test.js.map +1 -0
- package/dist/tests/research-guard.test.d.ts +2 -0
- package/dist/tests/research-guard.test.d.ts.map +1 -0
- package/dist/tests/research-guard.test.js +143 -0
- package/dist/tests/research-guard.test.js.map +1 -0
- package/dist/tests/schema-version.test.d.ts +2 -0
- package/dist/tests/schema-version.test.d.ts.map +1 -0
- package/dist/tests/schema-version.test.js +99 -0
- package/dist/tests/schema-version.test.js.map +1 -0
- package/dist/tests/search-hook.test.js +11 -0
- package/dist/tests/search-hook.test.js.map +1 -1
- package/dist/tests/synonym.test.d.ts +2 -0
- package/dist/tests/synonym.test.d.ts.map +1 -0
- package/dist/tests/synonym.test.js +47 -0
- package/dist/tests/synonym.test.js.map +1 -0
- package/hooks/lib/mindlore-common.cjs +94 -0
- package/hooks/mindlore-research-guard.cjs +144 -0
- package/hooks/mindlore-search.cjs +56 -42
- package/hooks/mindlore-session-end.cjs +41 -13
- package/package.json +3 -1
- package/plugin.json +6 -1
- package/templates/config.json +15 -1
|
@@ -104,4 +104,15 @@ describe('Search Hook — FTS5 Query', () => {
|
|
|
104
104
|
expect(results.length).toBe(2);
|
|
105
105
|
});
|
|
106
106
|
});
|
|
107
|
+
describe('Search Hook Hybrid Integration', () => {
|
|
108
|
+
test('should fall back gracefully when hybrid search is not available', () => {
|
|
109
|
+
// Simulate hook's search function — pure FTS5 path
|
|
110
|
+
// Uses the shared DB already set up in beforeEach
|
|
111
|
+
const { searchFts5 } = require('../scripts/lib/hybrid-search.js');
|
|
112
|
+
const db = new better_sqlite3_1.default(DB_PATH, { readonly: true });
|
|
113
|
+
const results = searchFts5(db, 'react hooks', 3);
|
|
114
|
+
expect(results.length).toBeGreaterThan(0);
|
|
115
|
+
db.close();
|
|
116
|
+
});
|
|
117
|
+
});
|
|
107
118
|
//# sourceMappingURL=search-hook.test.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"search-hook.test.js","sourceRoot":"","sources":["../../tests/search-hook.test.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"search-hook.test.js","sourceRoot":"","sources":["../../tests/search-hook.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"synonym.test.d.ts","sourceRoot":"","sources":["../../tests/synonym.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const synonym_js_1 = require("../scripts/lib/synonym.js");
|
|
4
|
+
describe('Synonym Expansion', () => {
|
|
5
|
+
const synonyms = {
|
|
6
|
+
auth: ['authentication', 'login', 'kimlik doğrulama'],
|
|
7
|
+
güvenlik: ['security', 'hardening', 'sertleştirme'],
|
|
8
|
+
db: ['database', 'veritabanı', 'sqlite'],
|
|
9
|
+
};
|
|
10
|
+
test('should expand known synonym', () => {
|
|
11
|
+
const result = (0, synonym_js_1.expandQuery)('auth token', synonyms);
|
|
12
|
+
expect(result).toContain('auth');
|
|
13
|
+
expect(result).toContain('authentication');
|
|
14
|
+
expect(result).toContain('login');
|
|
15
|
+
expect(result).toContain('token');
|
|
16
|
+
});
|
|
17
|
+
test('should not duplicate original term', () => {
|
|
18
|
+
const result = (0, synonym_js_1.expandQuery)('auth', synonyms);
|
|
19
|
+
const authCount = result.filter(t => t === 'auth').length;
|
|
20
|
+
expect(authCount).toBe(1);
|
|
21
|
+
});
|
|
22
|
+
test('should handle Turkish synonyms', () => {
|
|
23
|
+
const result = (0, synonym_js_1.expandQuery)('güvenlik audit', synonyms);
|
|
24
|
+
expect(result).toContain('güvenlik');
|
|
25
|
+
expect(result).toContain('security');
|
|
26
|
+
expect(result).toContain('hardening');
|
|
27
|
+
expect(result).toContain('audit');
|
|
28
|
+
});
|
|
29
|
+
test('should return original terms when no synonyms match', () => {
|
|
30
|
+
const result = (0, synonym_js_1.expandQuery)('typescript hooks', synonyms);
|
|
31
|
+
expect(result).toEqual(['typescript', 'hooks']);
|
|
32
|
+
});
|
|
33
|
+
test('should handle empty input', () => {
|
|
34
|
+
const result = (0, synonym_js_1.expandQuery)('', synonyms);
|
|
35
|
+
expect(result).toEqual([]);
|
|
36
|
+
});
|
|
37
|
+
test('should load synonyms from config object', () => {
|
|
38
|
+
const config = { synonyms };
|
|
39
|
+
const loaded = (0, synonym_js_1.loadSynonyms)(config);
|
|
40
|
+
expect(loaded).toEqual(synonyms);
|
|
41
|
+
});
|
|
42
|
+
test('should return empty object when config has no synonyms', () => {
|
|
43
|
+
const loaded = (0, synonym_js_1.loadSynonyms)({});
|
|
44
|
+
expect(loaded).toEqual({});
|
|
45
|
+
});
|
|
46
|
+
});
|
|
47
|
+
//# sourceMappingURL=synonym.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"synonym.test.js","sourceRoot":"","sources":["../../tests/synonym.test.ts"],"names":[],"mappings":";;AAAA,0DAAsE;AAEtE,QAAQ,CAAC,mBAAmB,EAAE,GAAG,EAAE;IACjC,MAAM,QAAQ,GAAG;QACf,IAAI,EAAE,CAAC,gBAAgB,EAAE,OAAO,EAAE,kBAAkB,CAAC;QACrD,QAAQ,EAAE,CAAC,UAAU,EAAE,WAAW,EAAE,cAAc,CAAC;QACnD,EAAE,EAAE,CAAC,UAAU,EAAE,YAAY,EAAE,QAAQ,CAAC;KACzC,CAAC;IAEF,IAAI,CAAC,6BAA6B,EAAE,GAAG,EAAE;QACvC,MAAM,MAAM,GAAG,IAAA,wBAAW,EAAC,YAAY,EAAE,QAAQ,CAAC,CAAC;QACnD,MAAM,CAAC,MAAM,CAAC,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC;QACjC,MAAM,CAAC,MAAM,CAAC,CAAC,SAAS,CAAC,gBAAgB,CAAC,CAAC;QAC3C,MAAM,CAAC,MAAM,CAAC,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC;QAClC,MAAM,CAAC,MAAM,CAAC,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC;IACpC,CAAC,CAAC,CAAC;IAEH,IAAI,CAAC,oCAAoC,EAAE,GAAG,EAAE;QAC9C,MAAM,MAAM,GAAG,IAAA,wBAAW,EAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;QAC7C,MAAM,SAAS,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,KAAK,MAAM,CAAC,CAAC,MAAM,CAAC;QAC1D,MAAM,CAAC,SAAS,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAC5B,CAAC,CAAC,CAAC;IAEH,IAAI,CAAC,gCAAgC,EAAE,GAAG,EAAE;QAC1C,MAAM,MAAM,GAAG,IAAA,wBAAW,EAAC,gBAAgB,EAAE,QAAQ,CAAC,CAAC;QACvD,MAAM,CAAC,MAAM,CAAC,CAAC,SAAS,CAAC,UAAU,CAAC,CAAC;QACrC,MAAM,CAAC,MAAM,CAAC,CAAC,SAAS,CAAC,UAAU,CAAC,CAAC;QACrC,MAAM,CAAC,MAAM,CAAC,CAAC,SAAS,CAAC,WAAW,CAAC,CAAC;QACtC,MAAM,CAAC,MAAM,CAAC,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC;IACpC,CAAC,CAAC,CAAC;IAEH,IAAI,CAAC,qDAAqD,EAAE,GAAG,EAAE;QAC/D,MAAM,MAAM,GAAG,IAAA,wBAAW,EAAC,kBAAkB,EAAE,QAAQ,CAAC,CAAC;QACzD,MAAM,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC,CAAC,YAAY,EAAE,OAAO,CAAC,CAAC,CAAC;IAClD,CAAC,CAAC,CAAC;IAEH,IAAI,CAAC,2BAA2B,EAAE,GAAG,EAAE;QACrC,MAAM,MAAM,GAAG,IAAA,wBAAW,EAAC,EAAE,EAAE,QAAQ,CAAC,CAAC;QACzC,MAAM,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;IAC7B,CAAC,CAAC,CAAC;IAEH,IAAI,CAAC,yCAAyC,EAAE,GAAG,EAAE;QACnD,MAAM,MAAM,GAAG,EAAE,QAAQ,EAAE,CAAC;QAC5B,MAAM,MAAM,GAAG,IAAA,yBAAY,EAAC,MAAM,CAAC,CAAC;QACpC,MAAM,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;IACnC,CAAC,CAAC,CAAC;IAEH,IAAI,CAAC,wDAAwD,EAAE,GAAG,EAAE;QAClE,MAAM,MAAM,GAAG,IAAA,yBAAY,EAAC,EAAE,CAAC,CAAC;QAChC,MAAM,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;IAC7B,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
|
|
@@ -532,6 +532,64 @@ function formatMultiSessionEpisodes(episodes) {
|
|
|
532
532
|
return lines.join('\n');
|
|
533
533
|
}
|
|
534
534
|
|
|
535
|
+
// Shared FTS5 search utilities (used by mindlore-search + mindlore-research-guard)
|
|
536
|
+
const STOP_WORDS = new Set([
|
|
537
|
+
// English
|
|
538
|
+
'the', 'a', 'an', 'is', 'are', 'was', 'were', 'be', 'been', 'being',
|
|
539
|
+
'have', 'has', 'had', 'do', 'does', 'did', 'will', 'would', 'could',
|
|
540
|
+
'should', 'may', 'might', 'can', 'shall', 'to', 'of', 'in', 'for',
|
|
541
|
+
'on', 'with', 'at', 'by', 'from', 'as', 'into', 'through', 'during',
|
|
542
|
+
'it', 'its', 'this', 'that', 'these', 'those', 'what', 'which', 'who',
|
|
543
|
+
'whom', 'how', 'when', 'where', 'why', 'not', 'no', 'nor', 'so',
|
|
544
|
+
'if', 'or', 'but', 'all', 'each', 'every', 'both', 'few', 'more',
|
|
545
|
+
'most', 'other', 'some', 'such', 'only', 'own', 'same', 'than',
|
|
546
|
+
'and', 'about', 'between', 'after', 'before', 'above', 'below',
|
|
547
|
+
'up', 'down', 'out', 'very', 'just', 'also', 'now', 'then',
|
|
548
|
+
'here', 'there', 'too', 'yet', 'my', 'your', 'his', 'her', 'our',
|
|
549
|
+
'their', 'me', 'him', 'us', 'them', 'i', 'you', 'he', 'she', 'we', 'they',
|
|
550
|
+
// Turkish
|
|
551
|
+
'bir', 'bu', 'su', 'ne', 'nasil', 'neden', 'var', 'yok', 'mi', 'mu',
|
|
552
|
+
'ile', 'icin', 'de', 'da', 've', 'veya', 'ama', 'ise', 'hem',
|
|
553
|
+
'bakalim', 'gel', 'git', 'yap', 'et', 'al', 'ver',
|
|
554
|
+
'evet', 'hayir', 'tamam', 'ok', 'oldu', 'olur', 'dur',
|
|
555
|
+
'simdi', 'sonra', 'once', 'hemen', 'biraz',
|
|
556
|
+
'lan', 'ya', 'ki', 'abi', 'hadi', 'hey', 'selam',
|
|
557
|
+
'olarak', 'olan', 'gibi', 'kadar', 'daha', 'cok', 'hem',
|
|
558
|
+
'bunu', 'buna', 'icinde', 'uzerinde', 'arasinda',
|
|
559
|
+
'sonucu', 'tarafindan', 'zaten', 'gayet',
|
|
560
|
+
'acaba', 'nedir', 'midir', 'mudur',
|
|
561
|
+
// Generic technical (appears everywhere, not distinctive)
|
|
562
|
+
'hook', 'file', 'dosya', 'kullan', 'ekle', 'yaz', 'oku', 'calistir',
|
|
563
|
+
'kontrol', 'test', 'check', 'run', 'add', 'update', 'config',
|
|
564
|
+
'setup', 'install', 'start', 'stop', 'create', 'delete', 'remove', 'set',
|
|
565
|
+
'get', 'list', 'show', 'view', 'open', 'close', 'save', 'load',
|
|
566
|
+
]);
|
|
567
|
+
|
|
568
|
+
/**
|
|
569
|
+
* Extract topic keywords from text. Preserves Turkish chars.
|
|
570
|
+
* @param {string} text - Input text
|
|
571
|
+
* @param {number} [maxKeywords=8] - Max keywords to return
|
|
572
|
+
* @returns {string[]} Unique keywords
|
|
573
|
+
*/
|
|
574
|
+
function extractKeywords(text, maxKeywords = 8) {
|
|
575
|
+
const words = text
|
|
576
|
+
.toLowerCase()
|
|
577
|
+
.replace(/[^\w\s\u00e7\u011f\u0131\u00f6\u015f\u00fc-]/g, ' ')
|
|
578
|
+
.split(/\s+/)
|
|
579
|
+
.filter((w) => w.length >= 3 && !STOP_WORDS.has(w) && !/^\d+$/.test(w));
|
|
580
|
+
return [...new Set(words)].slice(0, maxKeywords);
|
|
581
|
+
}
|
|
582
|
+
|
|
583
|
+
/**
|
|
584
|
+
* Sanitize keyword for FTS5 MATCH — strip special chars, quote-wrap.
|
|
585
|
+
* @param {string} kw - Raw keyword
|
|
586
|
+
* @returns {string|null} Quoted keyword or null if too short
|
|
587
|
+
*/
|
|
588
|
+
function sanitizeKeyword(kw) {
|
|
589
|
+
const clean = kw.replace(/["*(){}[\]^~:]/g, '').replace(/-/g, ' ').trim();
|
|
590
|
+
return clean.length >= 2 ? `"${clean}"` : null;
|
|
591
|
+
}
|
|
592
|
+
|
|
535
593
|
module.exports = {
|
|
536
594
|
MINDLORE_DIR,
|
|
537
595
|
GLOBAL_MINDLORE_DIR,
|
|
@@ -571,4 +629,40 @@ module.exports = {
|
|
|
571
629
|
formatSupersededChains,
|
|
572
630
|
queryMultiSessionEpisodes,
|
|
573
631
|
formatMultiSessionEpisodes,
|
|
632
|
+
// FTS5 search utilities (v0.4.3)
|
|
633
|
+
STOP_WORDS,
|
|
634
|
+
extractKeywords,
|
|
635
|
+
sanitizeKeyword,
|
|
636
|
+
// Hybrid search helpers (v0.5.0)
|
|
637
|
+
loadSqliteVecCjs,
|
|
638
|
+
hasVecTableCjs,
|
|
574
639
|
};
|
|
640
|
+
|
|
641
|
+
/**
|
|
642
|
+
* Try to load sqlite-vec extension. Returns true if successful.
|
|
643
|
+
* @param {import('better-sqlite3').Database} db
|
|
644
|
+
* @returns {boolean}
|
|
645
|
+
*/
|
|
646
|
+
function loadSqliteVecCjs(db) {
|
|
647
|
+
try {
|
|
648
|
+
const sqliteVec = require('sqlite-vec');
|
|
649
|
+
sqliteVec.load(db);
|
|
650
|
+
return true;
|
|
651
|
+
} catch (_err) {
|
|
652
|
+
return false;
|
|
653
|
+
}
|
|
654
|
+
}
|
|
655
|
+
|
|
656
|
+
/**
|
|
657
|
+
* Check if documents_vec table exists.
|
|
658
|
+
* @param {import('better-sqlite3').Database} db
|
|
659
|
+
* @returns {boolean}
|
|
660
|
+
*/
|
|
661
|
+
function hasVecTableCjs(db) {
|
|
662
|
+
try {
|
|
663
|
+
db.prepare('SELECT slug FROM documents_vec LIMIT 0').run();
|
|
664
|
+
return true;
|
|
665
|
+
} catch (_err) {
|
|
666
|
+
return false;
|
|
667
|
+
}
|
|
668
|
+
}
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* mindlore-research-guard — PreToolUse (Agent) hook
|
|
6
|
+
*
|
|
7
|
+
* Before spawning a researcher agent, checks FTS5 for existing knowledge.
|
|
8
|
+
* - High quality + recent (30 days) match → exit 2 (block)
|
|
9
|
+
* - Old or low quality match → exit 0 with warning (additionalContext)
|
|
10
|
+
* - No match → silent pass
|
|
11
|
+
*
|
|
12
|
+
* Prevents redundant web research when knowledge already exists in DB.
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
const fs = require('fs');
|
|
16
|
+
const path = require('path');
|
|
17
|
+
const { getAllDbs, requireDatabase, extractKeywords, sanitizeKeyword } = require('./lib/mindlore-common.cjs');
|
|
18
|
+
|
|
19
|
+
// Keywords that signal a research/web-search intent in agent prompts
|
|
20
|
+
// Note: entries with dots/stars are regex patterns, rest are literals
|
|
21
|
+
const RESEARCH_SIGNALS = [
|
|
22
|
+
'research', 'araştır', 'arastir', 'investigate', 'search for',
|
|
23
|
+
'web search', 'websearch', 'webfetch', 'fetch.*url', 'look up',
|
|
24
|
+
'find out', 'check.*docs', 'documentation.*for',
|
|
25
|
+
];
|
|
26
|
+
const RESEARCH_REGEX = new RegExp(RESEARCH_SIGNALS.join('|'), 'i');
|
|
27
|
+
|
|
28
|
+
// Exclude ingest/internal operations (they intentionally fetch URLs)
|
|
29
|
+
const EXCLUDE_REGEX = /\[mindlore:|\bmindlore-ingest\b|ingest.*url|save.*raw|\[research-override\]/i;
|
|
30
|
+
|
|
31
|
+
const MAX_AGE_DAYS = 30;
|
|
32
|
+
|
|
33
|
+
function isRecent(dateStr) {
|
|
34
|
+
if (!dateStr) return false;
|
|
35
|
+
const d = new Date(dateStr);
|
|
36
|
+
if (isNaN(d.getTime())) return false;
|
|
37
|
+
const diff = (Date.now() - d.getTime()) / (1000 * 60 * 60 * 24);
|
|
38
|
+
return diff <= MAX_AGE_DAYS;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Search FTS5 using a single OR query instead of per-path×keyword loop.
|
|
43
|
+
* Returns top matches with quality and date from FTS5 columns (no file I/O).
|
|
44
|
+
*/
|
|
45
|
+
function searchDbs(keywords) {
|
|
46
|
+
const Database = requireDatabase();
|
|
47
|
+
if (!Database) return [];
|
|
48
|
+
|
|
49
|
+
const sanitized = keywords.map(sanitizeKeyword).filter(Boolean);
|
|
50
|
+
if (sanitized.length === 0) return [];
|
|
51
|
+
|
|
52
|
+
const matchQuery = sanitized.join(' OR ');
|
|
53
|
+
const dbPaths = getAllDbs();
|
|
54
|
+
const results = [];
|
|
55
|
+
|
|
56
|
+
for (const dbPath of dbPaths) {
|
|
57
|
+
try {
|
|
58
|
+
const db = new Database(dbPath, { readonly: true });
|
|
59
|
+
|
|
60
|
+
// Single FTS5 query — O(1) instead of O(paths × keywords)
|
|
61
|
+
const rows = db.prepare(
|
|
62
|
+
`SELECT path, slug, title, description, quality, date_captured, rank
|
|
63
|
+
FROM mindlore_fts
|
|
64
|
+
WHERE mindlore_fts MATCH ?
|
|
65
|
+
ORDER BY rank
|
|
66
|
+
LIMIT 10`
|
|
67
|
+
).all(matchQuery);
|
|
68
|
+
|
|
69
|
+
for (const row of rows) {
|
|
70
|
+
const quality = (row.quality || 'medium').toLowerCase();
|
|
71
|
+
const date_captured = row.date_captured || null;
|
|
72
|
+
|
|
73
|
+
results.push({
|
|
74
|
+
slug: row.slug || path.basename(row.path, '.md'),
|
|
75
|
+
title: row.title || row.description || row.slug || '',
|
|
76
|
+
quality,
|
|
77
|
+
date_captured,
|
|
78
|
+
recent: isRecent(date_captured),
|
|
79
|
+
rank: row.rank,
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
db.close();
|
|
84
|
+
} catch (_err) { /* db open or query failed */ }
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// Sort by rank (lower = better match in FTS5)
|
|
88
|
+
results.sort((a, b) => a.rank - b.rank);
|
|
89
|
+
return results.slice(0, 5);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
function main() {
|
|
93
|
+
let input;
|
|
94
|
+
try {
|
|
95
|
+
const raw = fs.readFileSync(0, 'utf8').trim();
|
|
96
|
+
if (!raw) return;
|
|
97
|
+
input = JSON.parse(raw);
|
|
98
|
+
} catch (_err) {
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
const toolName = input.tool_name || '';
|
|
103
|
+
if (toolName !== 'Agent') return;
|
|
104
|
+
|
|
105
|
+
const toolInput = input.tool_input || {};
|
|
106
|
+
const prompt = (toolInput.prompt || '') + ' ' + (toolInput.description || '');
|
|
107
|
+
|
|
108
|
+
// Skip mindlore internal operations and explicit overrides
|
|
109
|
+
if (EXCLUDE_REGEX.test(prompt)) return;
|
|
110
|
+
|
|
111
|
+
// Only trigger on research-intent agents
|
|
112
|
+
if (!RESEARCH_REGEX.test(prompt)) return;
|
|
113
|
+
|
|
114
|
+
const keywords = extractKeywords(prompt, 10);
|
|
115
|
+
if (keywords.length < 2) return;
|
|
116
|
+
|
|
117
|
+
const matches = searchDbs(keywords);
|
|
118
|
+
if (matches.length === 0) return;
|
|
119
|
+
|
|
120
|
+
// Check for high-quality recent matches
|
|
121
|
+
const strongMatches = matches.filter((m) => m.quality === 'high' && m.recent);
|
|
122
|
+
|
|
123
|
+
if (strongMatches.length > 0) {
|
|
124
|
+
// BLOCK: high quality + recent knowledge exists
|
|
125
|
+
const slugList = strongMatches.map((m) => ` - ${m.slug} (${m.title})`).join('\n');
|
|
126
|
+
const msg = `[mindlore-research-guard] BLOK: Bu konuda guncel, yuksek kaliteli bilgi DB'de zaten var.\n` +
|
|
127
|
+
`Once mevcut bilgiyi oku:\n${slugList}\n` +
|
|
128
|
+
`Eger bilgi yetersizse, prompt'a "[research-override]" ekleyerek tekrar dene.`;
|
|
129
|
+
process.stderr.write(msg);
|
|
130
|
+
process.exit(2);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// WARN: old or low-quality matches exist
|
|
134
|
+
const slugList = matches.map((m) => `${m.slug} (${m.quality}, ${m.date_captured || 'tarih yok'})`).join(', ');
|
|
135
|
+
const output = {
|
|
136
|
+
hookSpecificOutput: {
|
|
137
|
+
hookEventName: 'PreToolUse',
|
|
138
|
+
additionalContext: `[mindlore-research-guard] DB'de ilgili bilgi var ama eski/dusuk kalite: ${slugList}. Guncelleme gerekebilir — arastirma sonrasi DB'yi guncelle.`,
|
|
139
|
+
},
|
|
140
|
+
};
|
|
141
|
+
process.stdout.write(JSON.stringify(output));
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
main();
|
|
@@ -10,53 +10,18 @@
|
|
|
10
10
|
|
|
11
11
|
const fs = require('fs');
|
|
12
12
|
const path = require('path');
|
|
13
|
-
const { getAllDbs, requireDatabase, extractHeadings, readHookStdin } = require('./lib/mindlore-common.cjs');
|
|
13
|
+
const { getAllDbs, requireDatabase, extractHeadings, readHookStdin, extractKeywords, readConfig, loadSqliteVecCjs, hasVecTableCjs } = require('./lib/mindlore-common.cjs');
|
|
14
14
|
|
|
15
15
|
const MAX_RESULTS = 3;
|
|
16
16
|
const MIN_QUERY_WORDS = 3;
|
|
17
17
|
const MIN_KEYWORD_HITS = 2;
|
|
18
18
|
|
|
19
|
-
//
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
'on', 'with', 'at', 'by', 'from', 'as', 'into', 'through', 'during',
|
|
26
|
-
'it', 'its', 'this', 'that', 'these', 'those', 'what', 'which', 'who',
|
|
27
|
-
'whom', 'how', 'when', 'where', 'why', 'not', 'no', 'nor', 'so',
|
|
28
|
-
'if', 'or', 'but', 'all', 'each', 'every', 'both', 'few', 'more',
|
|
29
|
-
'most', 'other', 'some', 'such', 'only', 'own', 'same', 'than',
|
|
30
|
-
'and', 'about', 'between', 'after', 'before', 'above', 'below',
|
|
31
|
-
'up', 'down', 'out', 'very', 'just', 'also', 'now', 'then',
|
|
32
|
-
'here', 'there', 'too', 'yet', 'my', 'your', 'his', 'her', 'our',
|
|
33
|
-
'their', 'me', 'him', 'us', 'them', 'i', 'you', 'he', 'she', 'we', 'they',
|
|
34
|
-
// Turkish
|
|
35
|
-
'bir', 'bu', 'su', 'ne', 'nasil', 'neden', 'var', 'yok', 'mi', 'mu',
|
|
36
|
-
'ile', 'icin', 'de', 'da', 've', 'veya', 'ama', 'ise', 'hem',
|
|
37
|
-
'bakalim', 'gel', 'git', 'yap', 'et', 'al', 'ver',
|
|
38
|
-
'evet', 'hayir', 'tamam', 'ok', 'oldu', 'olur', 'dur',
|
|
39
|
-
'simdi', 'sonra', 'once', 'hemen', 'biraz',
|
|
40
|
-
'lan', 'ya', 'ki', 'abi', 'hadi', 'hey', 'selam',
|
|
41
|
-
'olarak', 'olan', 'gibi', 'kadar', 'daha', 'cok', 'hem',
|
|
42
|
-
'bunu', 'buna', 'icinde', 'uzerinde', 'arasinda',
|
|
43
|
-
'sonucu', 'tarafindan', 'zaten', 'gayet',
|
|
44
|
-
'acaba', 'nedir', 'midir', 'mudur',
|
|
45
|
-
// Generic technical (appears everywhere, not distinctive)
|
|
46
|
-
'hook', 'file', 'dosya', 'kullan', 'ekle', 'yaz', 'oku', 'calistir',
|
|
47
|
-
'kontrol', 'test', 'check', 'run', 'add', 'update', 'config',
|
|
48
|
-
'setup', 'install', 'start', 'stop', 'create', 'delete', 'remove', 'set',
|
|
49
|
-
'get', 'list', 'show', 'view', 'open', 'close', 'save', 'load',
|
|
50
|
-
]);
|
|
51
|
-
|
|
52
|
-
function extractKeywords(text) {
|
|
53
|
-
const words = text
|
|
54
|
-
.toLowerCase()
|
|
55
|
-
.replace(/[^\w\s\u00e7\u011f\u0131\u00f6\u015f\u00fc-]/g, ' ')
|
|
56
|
-
.split(/\s+/)
|
|
57
|
-
.filter((w) => w.length >= 3 && !STOP_WORDS.has(w) && !/^\d+$/.test(w));
|
|
58
|
-
|
|
59
|
-
return [...new Set(words)].slice(0, 8);
|
|
19
|
+
// Try to load hybrid search module (built TS)
|
|
20
|
+
let hybridSearchMod;
|
|
21
|
+
try {
|
|
22
|
+
hybridSearchMod = require('../dist/scripts/lib/hybrid-search.js');
|
|
23
|
+
} catch (_err) {
|
|
24
|
+
// hybrid-search not built yet — pure FTS5 mode
|
|
60
25
|
}
|
|
61
26
|
|
|
62
27
|
/**
|
|
@@ -67,6 +32,55 @@ function searchDb(dbPath, keywords, Database) {
|
|
|
67
32
|
const db = new Database(dbPath, { readonly: true });
|
|
68
33
|
const results = [];
|
|
69
34
|
|
|
35
|
+
// v0.5.0: Try hybrid search with synonym expansion (no embedding — hooks are sync)
|
|
36
|
+
if (hybridSearchMod && loadSqliteVecCjs(db) && hasVecTableCjs(db)) {
|
|
37
|
+
try {
|
|
38
|
+
const config = readConfig(baseDir);
|
|
39
|
+
const synonyms = (config && config.synonyms) ? config.synonyms : {};
|
|
40
|
+
|
|
41
|
+
// Expand keywords with synonyms
|
|
42
|
+
const expandedTerms = keywords.slice();
|
|
43
|
+
for (const kw of keywords) {
|
|
44
|
+
const lower = kw.toLowerCase();
|
|
45
|
+
if (synonyms[lower]) {
|
|
46
|
+
expandedTerms.push(...synonyms[lower]);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const fusedResults = hybridSearchMod.hybridSearch(db, expandedTerms.join(' '), {
|
|
51
|
+
maxResults: MAX_RESULTS,
|
|
52
|
+
project: path.basename(process.cwd()),
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
if (fusedResults.length > 0) {
|
|
56
|
+
for (const r of fusedResults) {
|
|
57
|
+
const filePath = r.path || '';
|
|
58
|
+
let headings = [];
|
|
59
|
+
if (filePath && fs.existsSync(filePath)) {
|
|
60
|
+
const content = fs.readFileSync(filePath, 'utf8');
|
|
61
|
+
headings = extractHeadings(content, 3);
|
|
62
|
+
}
|
|
63
|
+
results.push({
|
|
64
|
+
path: filePath,
|
|
65
|
+
slug: r.slug,
|
|
66
|
+
description: r.description || '',
|
|
67
|
+
category: r.category || '',
|
|
68
|
+
title: r.title || '',
|
|
69
|
+
tags: r.tags || '',
|
|
70
|
+
headings,
|
|
71
|
+
hits: 1,
|
|
72
|
+
rank: r.score,
|
|
73
|
+
baseDir,
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
db.close();
|
|
77
|
+
return results;
|
|
78
|
+
}
|
|
79
|
+
} catch (_err) {
|
|
80
|
+
// Hybrid search failed — fall through to FTS5
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
70
84
|
try {
|
|
71
85
|
const allPaths = db.prepare('SELECT DISTINCT path FROM mindlore_fts').all();
|
|
72
86
|
const matchStmt = db.prepare('SELECT rank FROM mindlore_fts WHERE path = ? AND mindlore_fts MATCH ?');
|
|
@@ -11,9 +11,26 @@
|
|
|
11
11
|
|
|
12
12
|
const fs = require('fs');
|
|
13
13
|
const path = require('path');
|
|
14
|
-
const
|
|
14
|
+
const os = require('os');
|
|
15
|
+
const { execSync, spawn } = require('child_process');
|
|
15
16
|
const { findMindloreDir, globalDir, getProjectName, openDatabase, ensureEpisodesTable, hasEpisodesTable, insertBareEpisode, insertFtsRow } = require('./lib/mindlore-common.cjs');
|
|
16
17
|
|
|
18
|
+
// --worker mode: heavy ops run in detached child process (survives parent exit)
|
|
19
|
+
if (process.argv.includes('--worker')) {
|
|
20
|
+
const dataPath = process.argv[process.argv.indexOf('--worker') + 1];
|
|
21
|
+
try {
|
|
22
|
+
const raw = fs.readFileSync(dataPath, 'utf8');
|
|
23
|
+
fs.unlinkSync(dataPath); // cleanup temp file before any processing
|
|
24
|
+
const { baseDir, project, commits, changedFiles, reads } = JSON.parse(raw);
|
|
25
|
+
writeBareEpisode(baseDir, project, commits, changedFiles, reads);
|
|
26
|
+
syncObsidian(baseDir);
|
|
27
|
+
syncGlobalRepo();
|
|
28
|
+
} catch (_err) {
|
|
29
|
+
// Graceful fail — worker errors are silent
|
|
30
|
+
}
|
|
31
|
+
process.exit(0);
|
|
32
|
+
}
|
|
33
|
+
|
|
17
34
|
function formatDate(date) {
|
|
18
35
|
const y = date.getFullYear();
|
|
19
36
|
const m = String(date.getMonth() + 1).padStart(2, '0');
|
|
@@ -140,14 +157,25 @@ function main() {
|
|
|
140
157
|
fs.appendFileSync(logPath, logEntry, 'utf8');
|
|
141
158
|
}
|
|
142
159
|
|
|
143
|
-
//
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
160
|
+
// Heavy ops: detach into child process so CC can exit immediately.
|
|
161
|
+
// Fixes "Hook cancelled" when CC kills the hook before completion.
|
|
162
|
+
// See: https://github.com/anthropics/claude-code/issues/41577
|
|
163
|
+
try {
|
|
164
|
+
const workerData = JSON.stringify({ baseDir, project, commits, changedFiles, reads });
|
|
165
|
+
const tmpFile = path.join(os.tmpdir(), `mindlore-worker-${Date.now()}.json`);
|
|
166
|
+
fs.writeFileSync(tmpFile, workerData, 'utf8');
|
|
167
|
+
const child = spawn(process.execPath, [__filename, '--worker', tmpFile], {
|
|
168
|
+
detached: true,
|
|
169
|
+
stdio: 'ignore',
|
|
170
|
+
cwd: process.cwd(),
|
|
171
|
+
});
|
|
172
|
+
child.unref();
|
|
173
|
+
} catch (_err) {
|
|
174
|
+
// Fallback: run inline if spawn fails
|
|
175
|
+
writeBareEpisode(baseDir, project, commits, changedFiles, reads);
|
|
176
|
+
syncObsidian(baseDir);
|
|
177
|
+
syncGlobalRepo();
|
|
178
|
+
}
|
|
151
179
|
}
|
|
152
180
|
|
|
153
181
|
/**
|
|
@@ -204,7 +232,7 @@ function writeBareEpisode(baseDir, project, commits, changedFiles, reads) {
|
|
|
204
232
|
description: truncatedSummary,
|
|
205
233
|
type: 'episode',
|
|
206
234
|
category: 'episodes',
|
|
207
|
-
title: truncatedSummary
|
|
235
|
+
title: truncatedSummary,
|
|
208
236
|
content: [truncatedSummary, body ?? ''].join('\n').trim(),
|
|
209
237
|
tags: 'session',
|
|
210
238
|
quality: null,
|
|
@@ -218,8 +246,8 @@ function writeBareEpisode(baseDir, project, commits, changedFiles, reads) {
|
|
|
218
246
|
writeBoth();
|
|
219
247
|
|
|
220
248
|
db.close();
|
|
221
|
-
} catch (
|
|
222
|
-
|
|
249
|
+
} catch (err) {
|
|
250
|
+
process.stderr.write(`[mindlore] episode write failed: ${err?.message ?? err}\n`);
|
|
223
251
|
}
|
|
224
252
|
}
|
|
225
253
|
|
|
@@ -332,7 +360,7 @@ function syncGlobalRepo() {
|
|
|
332
360
|
|
|
333
361
|
if (!status) return; // nothing to commit
|
|
334
362
|
|
|
335
|
-
execSync('git add
|
|
363
|
+
execSync('git add *.md mindlore.db config.json diary/ sources/ domains/ analyses/ decisions/ raw/ connections/ insights/ learnings/', { cwd: gDir, timeout: 5000, stdio: 'pipe' });
|
|
336
364
|
const now = new Date().toISOString().slice(0, 19);
|
|
337
365
|
execSync(`git commit -m "mindlore auto-sync ${now}"`, {
|
|
338
366
|
cwd: gDir,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "mindlore",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.0",
|
|
4
4
|
"description": "AI-native knowledge system for Claude Code",
|
|
5
5
|
"type": "commonjs",
|
|
6
6
|
"bin": {
|
|
@@ -52,9 +52,11 @@
|
|
|
52
52
|
"@types/node": "^25.6.0",
|
|
53
53
|
"@typescript-eslint/eslint-plugin": "^8.58.1",
|
|
54
54
|
"@typescript-eslint/parser": "^8.58.1",
|
|
55
|
+
"@xenova/transformers": "^2.17.2",
|
|
55
56
|
"eslint": "^9.0.0",
|
|
56
57
|
"globals": "^15.0.0",
|
|
57
58
|
"jest": "^29.7.0",
|
|
59
|
+
"sqlite-vec": "^0.1.9",
|
|
58
60
|
"ts-jest": "^29.4.9",
|
|
59
61
|
"typescript": "^6.0.2"
|
|
60
62
|
},
|
package/plugin.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "mindlore",
|
|
3
3
|
"description": "AI-native knowledge system for Claude Code. Persistent, searchable, evolving knowledge base with FTS5.",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.5.0",
|
|
5
5
|
"skills": [
|
|
6
6
|
{
|
|
7
7
|
"name": "mindlore-ingest",
|
|
@@ -94,6 +94,11 @@
|
|
|
94
94
|
"event": "PreToolUse",
|
|
95
95
|
"script": "hooks/mindlore-model-router.cjs",
|
|
96
96
|
"if": "Agent"
|
|
97
|
+
},
|
|
98
|
+
{
|
|
99
|
+
"event": "PreToolUse",
|
|
100
|
+
"script": "hooks/mindlore-research-guard.cjs",
|
|
101
|
+
"if": "Agent"
|
|
97
102
|
}
|
|
98
103
|
]
|
|
99
104
|
}
|
package/templates/config.json
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"version": "0.
|
|
2
|
+
"version": "0.5.0",
|
|
3
3
|
"models": {
|
|
4
4
|
"ingest": "haiku",
|
|
5
5
|
"evolve": "sonnet",
|
|
@@ -12,5 +12,19 @@
|
|
|
12
12
|
"session_focus": {
|
|
13
13
|
"max_episodes": 3,
|
|
14
14
|
"multi_session_days": 3
|
|
15
|
+
},
|
|
16
|
+
"synonyms": {
|
|
17
|
+
"auth": ["authentication", "login", "kimlik doğrulama"],
|
|
18
|
+
"güvenlik": ["security", "hardening", "sertleştirme"],
|
|
19
|
+
"db": ["database", "veritabanı", "sqlite"],
|
|
20
|
+
"api": ["endpoint", "rest", "graphql"],
|
|
21
|
+
"ui": ["frontend", "arayüz", "interface"],
|
|
22
|
+
"test": ["testing", "jest", "unit test"]
|
|
23
|
+
},
|
|
24
|
+
"hybrid": {
|
|
25
|
+
"enabled": true,
|
|
26
|
+
"ftsWeight": 0.4,
|
|
27
|
+
"vecWeight": 0.6,
|
|
28
|
+
"k": 60
|
|
15
29
|
}
|
|
16
30
|
}
|