mindlore 0.7.0 → 0.7.1
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 +30 -3
- package/dist/scripts/bundle-hooks.d.ts +2 -0
- package/dist/scripts/bundle-hooks.d.ts.map +1 -0
- package/dist/scripts/bundle-hooks.js +68 -0
- package/dist/scripts/bundle-hooks.js.map +1 -0
- package/dist/scripts/init.js +0 -3
- package/dist/scripts/init.js.map +1 -1
- package/dist/scripts/lib/constants.d.ts +0 -2
- package/dist/scripts/lib/constants.d.ts.map +1 -1
- package/dist/scripts/lib/constants.js +0 -21
- package/dist/scripts/lib/constants.js.map +1 -1
- package/dist/tests/hook-smoke.test.js +1 -1
- package/dist/tests/hook-smoke.test.js.map +1 -1
- package/dist/tests/search-hook.test.js +1 -1
- package/dist/tests/search-hook.test.js.map +1 -1
- package/hooks/cc-memory-bulk-sync.cjs +592 -0
- package/hooks/cc-session-sync.cjs +842 -0
- package/hooks/hooks.json +149 -0
- package/hooks/lib/mindlore-common.cjs +2 -2
- package/hooks/lib/secure-io.cjs +17 -0
- package/hooks/mindlore-cwd-changed.cjs +19 -34
- package/hooks/mindlore-decision-detector.cjs +40 -31
- package/hooks/mindlore-dont-repeat.cjs +57 -115
- package/hooks/mindlore-fts5-sync.cjs +15 -44
- package/hooks/mindlore-index.cjs +100 -101
- package/hooks/mindlore-model-router.cjs +20 -32
- package/hooks/mindlore-post-compact.cjs +26 -42
- package/hooks/mindlore-post-read.cjs +35 -60
- package/hooks/mindlore-pre-compact.cjs +55 -73
- package/hooks/mindlore-read-guard.cjs +28 -51
- package/hooks/mindlore-research-guard.cjs +63 -101
- package/hooks/mindlore-search.cjs +1142 -93
- package/hooks/mindlore-session-end.cjs +155 -276
- package/hooks/mindlore-session-focus.cjs +639 -110
- package/hooks/src/lib/constants.cjs +15 -0
- package/hooks/src/lib/mindlore-common.cjs +975 -0
- package/hooks/src/lib/mindlore-common.d.cts +72 -0
- package/hooks/src/lib/secure-io.cjs +17 -0
- package/hooks/src/lib/types.d.ts +58 -0
- package/hooks/src/mindlore-cwd-changed.cjs +57 -0
- package/hooks/src/mindlore-decision-detector.cjs +54 -0
- package/hooks/src/mindlore-dont-repeat.cjs +222 -0
- package/hooks/src/mindlore-fts5-sync.cjs +98 -0
- package/hooks/src/mindlore-index.cjs +230 -0
- package/hooks/src/mindlore-model-router.cjs +54 -0
- package/hooks/src/mindlore-post-compact.cjs +69 -0
- package/hooks/src/mindlore-post-read.cjs +106 -0
- package/hooks/src/mindlore-pre-compact.cjs +154 -0
- package/hooks/src/mindlore-read-guard.cjs +105 -0
- package/hooks/src/mindlore-research-guard.cjs +176 -0
- package/hooks/src/mindlore-search.cjs +200 -0
- package/hooks/src/mindlore-session-end.cjs +511 -0
- package/hooks/src/mindlore-session-focus.cjs +256 -0
- package/package.json +7 -3
- package/plugin.json +3 -3
- package/templates/config.json +1 -1
|
@@ -1,63 +1,46 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
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',
|
|
2
|
+
"use strict";
|
|
3
|
+
|
|
4
|
+
// hooks/src/mindlore-research-guard.cjs
|
|
5
|
+
var fs = require("fs");
|
|
6
|
+
var path = require("path");
|
|
7
|
+
var { getAllDbs, requireDatabase, extractKeywords, sanitizeKeyword, hookLog, withTelemetrySync } = require("./lib/mindlore-common.cjs");
|
|
8
|
+
var RESEARCH_SIGNALS = [
|
|
9
|
+
"research",
|
|
10
|
+
"ara\u015Ft\u0131r",
|
|
11
|
+
"arastir",
|
|
12
|
+
"investigate",
|
|
13
|
+
"search for",
|
|
14
|
+
"web search",
|
|
15
|
+
"websearch",
|
|
16
|
+
"webfetch",
|
|
17
|
+
"fetch.*url",
|
|
18
|
+
"look up",
|
|
19
|
+
"find out",
|
|
20
|
+
"check.*docs",
|
|
21
|
+
"documentation.*for"
|
|
25
22
|
];
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
const EXCLUDE_REGEX = /\[mindlore:|\bmindlore-ingest\b|ingest.*url|save.*raw|\[research-override\]/i;
|
|
30
|
-
|
|
31
|
-
const MAX_AGE_DAYS = 30;
|
|
32
|
-
|
|
23
|
+
var RESEARCH_REGEX = new RegExp(RESEARCH_SIGNALS.join("|"), "i");
|
|
24
|
+
var EXCLUDE_REGEX = /\[mindlore:|\bmindlore-ingest\b|ingest.*url|save.*raw|\[research-override\]/i;
|
|
25
|
+
var MAX_AGE_DAYS = 30;
|
|
33
26
|
function isRecent(dateStr) {
|
|
34
27
|
if (!dateStr) return false;
|
|
35
28
|
const d = new Date(dateStr);
|
|
36
29
|
if (isNaN(d.getTime())) return false;
|
|
37
|
-
const diff = (Date.now() - d.getTime()) / (
|
|
30
|
+
const diff = (Date.now() - d.getTime()) / (1e3 * 60 * 60 * 24);
|
|
38
31
|
return diff <= MAX_AGE_DAYS;
|
|
39
32
|
}
|
|
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
33
|
function searchDbs(keywords) {
|
|
46
34
|
const Database = requireDatabase();
|
|
47
35
|
if (!Database) return [];
|
|
48
|
-
|
|
49
36
|
const sanitized = keywords.map(sanitizeKeyword).filter(Boolean);
|
|
50
37
|
if (sanitized.length === 0) return [];
|
|
51
|
-
|
|
52
|
-
const matchQuery = sanitized.join(' OR ');
|
|
38
|
+
const matchQuery = sanitized.join(" OR ");
|
|
53
39
|
const dbPaths = getAllDbs();
|
|
54
40
|
const results = [];
|
|
55
|
-
|
|
56
41
|
for (const dbPath of dbPaths) {
|
|
57
42
|
try {
|
|
58
43
|
const db = new Database(dbPath, { readonly: true });
|
|
59
|
-
|
|
60
|
-
// Single FTS5 query — O(1) instead of O(paths × keywords)
|
|
61
44
|
const rows = db.prepare(
|
|
62
45
|
`SELECT path, slug, title, description, quality, date_captured, rank
|
|
63
46
|
FROM mindlore_fts
|
|
@@ -65,112 +48,91 @@ function searchDbs(keywords) {
|
|
|
65
48
|
ORDER BY rank
|
|
66
49
|
LIMIT 10`
|
|
67
50
|
).all(matchQuery);
|
|
68
|
-
|
|
69
51
|
for (const row of rows) {
|
|
70
|
-
const quality = (row.quality ||
|
|
52
|
+
const quality = (row.quality || "medium").toLowerCase();
|
|
71
53
|
const date_captured = row.date_captured || null;
|
|
72
|
-
|
|
73
54
|
results.push({
|
|
74
|
-
slug: row.slug || path.basename(row.path,
|
|
75
|
-
title: row.title || row.description || row.slug ||
|
|
55
|
+
slug: row.slug || path.basename(row.path, ".md"),
|
|
56
|
+
title: row.title || row.description || row.slug || "",
|
|
76
57
|
quality,
|
|
77
58
|
date_captured,
|
|
78
59
|
recent: isRecent(date_captured),
|
|
79
|
-
rank: row.rank
|
|
60
|
+
rank: row.rank
|
|
80
61
|
});
|
|
81
62
|
}
|
|
82
|
-
|
|
83
63
|
db.close();
|
|
84
|
-
} catch (_err) {
|
|
64
|
+
} catch (_err) {
|
|
65
|
+
}
|
|
85
66
|
}
|
|
86
|
-
|
|
87
|
-
// Sort by rank (lower = better match in FTS5)
|
|
88
67
|
results.sort((a, b) => a.rank - b.rank);
|
|
89
68
|
return results.slice(0, 5);
|
|
90
69
|
}
|
|
91
|
-
|
|
92
70
|
function main() {
|
|
93
71
|
let input;
|
|
94
72
|
try {
|
|
95
|
-
const raw = fs.readFileSync(0,
|
|
73
|
+
const raw = fs.readFileSync(0, "utf8").trim();
|
|
96
74
|
if (!raw) return;
|
|
97
75
|
input = JSON.parse(raw);
|
|
98
76
|
} catch (_err) {
|
|
99
77
|
return;
|
|
100
78
|
}
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
if (toolName !== 'Agent') return;
|
|
104
|
-
|
|
79
|
+
const toolName = input.tool_name || "";
|
|
80
|
+
if (toolName !== "Agent") return;
|
|
105
81
|
const toolInput = input.tool_input || {};
|
|
106
|
-
|
|
107
|
-
// Only block agents with web access — let local-only agents pass
|
|
108
|
-
const WEB_CAPABLE_TYPES = ['researcher', 'general-purpose'];
|
|
82
|
+
const WEB_CAPABLE_TYPES = ["researcher", "general-purpose"];
|
|
109
83
|
const LOCAL_ONLY_TYPES = [
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
84
|
+
"Explore",
|
|
85
|
+
"coder",
|
|
86
|
+
"code-reviewer",
|
|
87
|
+
"Plan",
|
|
88
|
+
"bug-analyzer",
|
|
89
|
+
"security-reviewer",
|
|
90
|
+
"contrarian",
|
|
91
|
+
"scope-guardian",
|
|
92
|
+
"quality-gate",
|
|
93
|
+
"test-runner"
|
|
113
94
|
];
|
|
114
|
-
const subagentType = toolInput.subagent_type ||
|
|
115
|
-
const description = (toolInput.description ||
|
|
116
|
-
|
|
117
|
-
// Known local-only agent → always pass
|
|
95
|
+
const subagentType = toolInput.subagent_type || "";
|
|
96
|
+
const description = (toolInput.description || "").toLowerCase();
|
|
118
97
|
if (LOCAL_ONLY_TYPES.includes(subagentType)) return;
|
|
119
|
-
|
|
120
|
-
// Known web-capable agent → continue to FTS5 check
|
|
121
|
-
// Unknown or empty subagent_type → check description for research intent
|
|
122
98
|
if (subagentType && !WEB_CAPABLE_TYPES.includes(subagentType)) return;
|
|
123
|
-
|
|
124
|
-
// If no subagent_type, check description for web research intent (reuse RESEARCH_REGEX)
|
|
125
99
|
if (!subagentType && !RESEARCH_REGEX.test(description)) return;
|
|
126
|
-
|
|
127
|
-
const prompt = (toolInput.prompt || '') + ' ' + (toolInput.description || '');
|
|
128
|
-
|
|
129
|
-
// Skip mindlore internal operations and explicit overrides
|
|
100
|
+
const prompt = (toolInput.prompt || "") + " " + (toolInput.description || "");
|
|
130
101
|
if (EXCLUDE_REGEX.test(prompt)) return;
|
|
131
|
-
|
|
132
|
-
// If subagent_type is a known research type, skip prompt-level regex check
|
|
133
|
-
// Otherwise require research signals in the prompt text
|
|
134
102
|
const isKnownResearchType = WEB_CAPABLE_TYPES.includes(subagentType);
|
|
135
103
|
if (!isKnownResearchType && !RESEARCH_REGEX.test(prompt)) return;
|
|
136
|
-
|
|
137
104
|
const keywords = extractKeywords(prompt, 10);
|
|
138
105
|
if (keywords.length < 2) return;
|
|
139
|
-
|
|
140
106
|
const matches = searchDbs(keywords);
|
|
141
107
|
if (matches.length === 0) return;
|
|
142
|
-
|
|
143
|
-
// Prevents false positives like "claude-code-repo" matching on generic words
|
|
144
108
|
const lcKeywords = keywords.map((k) => k.toLowerCase());
|
|
145
109
|
const relevantMatches = matches.filter((m) => {
|
|
146
110
|
const haystack = `${m.slug} ${m.title}`.toLowerCase();
|
|
147
111
|
const overlap = lcKeywords.filter((k) => haystack.includes(k));
|
|
148
112
|
return overlap.length >= 2;
|
|
149
113
|
});
|
|
150
|
-
|
|
151
114
|
if (relevantMatches.length === 0) return;
|
|
152
|
-
|
|
153
|
-
// Check for high-quality recent matches among relevant ones
|
|
154
|
-
const strongMatches = relevantMatches.filter((m) => m.quality === 'high' && m.recent);
|
|
155
|
-
|
|
115
|
+
const strongMatches = relevantMatches.filter((m) => m.quality === "high" && m.recent);
|
|
156
116
|
if (strongMatches.length > 0) {
|
|
157
|
-
const
|
|
158
|
-
const msg = `[mindlore-research-guard] BLOK: Bu konuda guncel, yuksek kaliteli bilgi DB'de zaten var
|
|
159
|
-
|
|
160
|
-
|
|
117
|
+
const slugList2 = strongMatches.map((m) => ` - ${m.slug} (${m.title})`).join("\n");
|
|
118
|
+
const msg = `[mindlore-research-guard] BLOK: Bu konuda guncel, yuksek kaliteli bilgi DB'de zaten var.
|
|
119
|
+
Once mevcut bilgiyi oku:
|
|
120
|
+
${slugList2}
|
|
121
|
+
Eger bilgi yetersizse, prompt'a "[research-override]" ekleyerek tekrar dene.`;
|
|
161
122
|
process.stderr.write(msg);
|
|
162
123
|
process.exit(2);
|
|
163
124
|
}
|
|
164
|
-
|
|
165
|
-
// WARN: relevant but old or low-quality matches exist
|
|
166
|
-
const slugList = relevantMatches.map((m) => `${m.slug} (${m.quality}, ${m.date_captured || 'tarih yok'})`).join(', ');
|
|
125
|
+
const slugList = relevantMatches.map((m) => `${m.slug} (${m.quality}, ${m.date_captured || "tarih yok"})`).join(", ");
|
|
167
126
|
const output = {
|
|
168
127
|
hookSpecificOutput: {
|
|
169
|
-
hookEventName:
|
|
170
|
-
additionalContext: `[mindlore-research-guard] DB'de ilgili bilgi var ama eski/dusuk kalite: ${slugList}. Guncelleme gerekebilir
|
|
171
|
-
}
|
|
128
|
+
hookEventName: "PreToolUse",
|
|
129
|
+
additionalContext: `[mindlore-research-guard] DB'de ilgili bilgi var ama eski/dusuk kalite: ${slugList}. Guncelleme gerekebilir \u2014 arastirma sonrasi DB'yi guncelle.`
|
|
130
|
+
}
|
|
172
131
|
};
|
|
173
132
|
process.stdout.write(JSON.stringify(output));
|
|
174
133
|
}
|
|
175
|
-
|
|
176
|
-
|
|
134
|
+
try {
|
|
135
|
+
withTelemetrySync("mindlore-research-guard", main);
|
|
136
|
+
} catch (err) {
|
|
137
|
+
hookLog("research-guard", "error", err?.message ?? String(err));
|
|
138
|
+
}
|