mindlore 0.7.0 → 0.7.2
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 +70 -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/all-migrations.d.ts.map +1 -1
- package/dist/scripts/lib/all-migrations.js +3 -0
- package/dist/scripts/lib/all-migrations.js.map +1 -1
- package/dist/scripts/lib/constants.d.ts +7 -2
- package/dist/scripts/lib/constants.d.ts.map +1 -1
- package/dist/scripts/lib/constants.js +17 -22
- package/dist/scripts/lib/constants.js.map +1 -1
- package/dist/scripts/lib/mcp-tools.d.ts.map +1 -1
- package/dist/scripts/lib/mcp-tools.js +63 -78
- package/dist/scripts/lib/mcp-tools.js.map +1 -1
- package/dist/scripts/lib/migrations-v072.d.ts +3 -0
- package/dist/scripts/lib/migrations-v072.d.ts.map +1 -0
- package/dist/scripts/lib/migrations-v072.js +25 -0
- package/dist/scripts/lib/migrations-v072.js.map +1 -0
- package/dist/scripts/lib/relation-helpers.d.ts +15 -0
- package/dist/scripts/lib/relation-helpers.d.ts.map +1 -0
- package/dist/scripts/lib/relation-helpers.js +30 -0
- package/dist/scripts/lib/relation-helpers.js.map +1 -0
- package/dist/scripts/lib/tool-adapters/get-adapter.d.ts +21 -0
- package/dist/scripts/lib/tool-adapters/get-adapter.d.ts.map +1 -0
- package/dist/scripts/lib/tool-adapters/get-adapter.js +51 -0
- package/dist/scripts/lib/tool-adapters/get-adapter.js.map +1 -0
- package/dist/scripts/lib/tool-adapters/relate-adapter.d.ts +34 -0
- package/dist/scripts/lib/tool-adapters/relate-adapter.d.ts.map +1 -0
- package/dist/scripts/lib/tool-adapters/relate-adapter.js +43 -0
- package/dist/scripts/lib/tool-adapters/relate-adapter.js.map +1 -0
- package/dist/scripts/lib/tool-adapters/search-adapter.d.ts +5 -0
- package/dist/scripts/lib/tool-adapters/search-adapter.d.ts.map +1 -1
- package/dist/scripts/lib/tool-adapters/search-adapter.js +37 -0
- package/dist/scripts/lib/tool-adapters/search-adapter.js.map +1 -1
- package/dist/scripts/mcp-server.js +1 -1
- package/dist/scripts/mcp-server.js.map +1 -1
- package/dist/tests/dont-repeat-dedup.test.d.ts +2 -0
- package/dist/tests/dont-repeat-dedup.test.d.ts.map +1 -0
- package/dist/tests/dont-repeat-dedup.test.js +93 -0
- package/dist/tests/dont-repeat-dedup.test.js.map +1 -0
- package/dist/tests/e2e-kg-pipeline.test.d.ts +2 -0
- package/dist/tests/e2e-kg-pipeline.test.d.ts.map +1 -0
- package/dist/tests/e2e-kg-pipeline.test.js +59 -0
- package/dist/tests/e2e-kg-pipeline.test.js.map +1 -0
- package/dist/tests/helpers/db.d.ts.map +1 -1
- package/dist/tests/helpers/db.js +2 -1
- package/dist/tests/helpers/db.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/mcp-get-tool.test.d.ts +2 -0
- package/dist/tests/mcp-get-tool.test.d.ts.map +1 -0
- package/dist/tests/mcp-get-tool.test.js +93 -0
- package/dist/tests/mcp-get-tool.test.js.map +1 -0
- package/dist/tests/mcp-relate-tool.test.d.ts +2 -0
- package/dist/tests/mcp-relate-tool.test.d.ts.map +1 -0
- package/dist/tests/mcp-relate-tool.test.js +85 -0
- package/dist/tests/mcp-relate-tool.test.js.map +1 -0
- package/dist/tests/mcp-server.test.js +3 -1
- package/dist/tests/mcp-server.test.js.map +1 -1
- package/dist/tests/mcp-tools.test.js +20 -0
- package/dist/tests/mcp-tools.test.js.map +1 -1
- package/dist/tests/memory-relate.test.d.ts +2 -0
- package/dist/tests/memory-relate.test.d.ts.map +1 -0
- package/dist/tests/memory-relate.test.js +70 -0
- package/dist/tests/memory-relate.test.js.map +1 -0
- package/dist/tests/migrations-v063.test.js +1 -1
- package/dist/tests/migrations-v072.test.d.ts +2 -0
- package/dist/tests/migrations-v072.test.d.ts.map +1 -0
- package/dist/tests/migrations-v072.test.js +74 -0
- package/dist/tests/migrations-v072.test.js.map +1 -0
- package/dist/tests/plugin-cache-regression.test.d.ts +2 -0
- package/dist/tests/plugin-cache-regression.test.d.ts.map +1 -0
- package/dist/tests/plugin-cache-regression.test.js +19 -0
- package/dist/tests/plugin-cache-regression.test.js.map +1 -0
- 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 +606 -0
- package/hooks/cc-session-sync.cjs +856 -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 +75 -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 +1156 -93
- package/hooks/mindlore-session-end.cjs +155 -276
- package/hooks/mindlore-session-focus.cjs +672 -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 +243 -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 +8 -3
- package/plugin.json +5 -4
- package/templates/config.json +1 -1
|
@@ -1,106 +1,81 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
* Outputs token estimate via additionalContext JSON.
|
|
11
|
-
* Also stores token info in _session-reads.json for read-guard.
|
|
12
|
-
*/
|
|
13
|
-
|
|
14
|
-
const fs = require('fs');
|
|
15
|
-
const path = require('path');
|
|
16
|
-
const { findMindloreDir, getProjectName, hookLog, withTelemetry } = require('./lib/mindlore-common.cjs');
|
|
17
|
-
|
|
18
|
-
const CODE_EXTS = new Set(['.ts', '.tsx', '.js', '.jsx', '.py', '.rs', '.go', '.java', '.c', '.cpp', '.h', '.css', '.scss', '.sql', '.sh', '.yaml', '.yml', '.json', '.toml', '.xml', '.cjs', '.mjs']);
|
|
19
|
-
const PROSE_EXTS = new Set(['.md', '.txt', '.rst', '.adoc']);
|
|
20
|
-
|
|
2
|
+
"use strict";
|
|
3
|
+
|
|
4
|
+
// hooks/src/mindlore-post-read.cjs
|
|
5
|
+
var fs = require("fs");
|
|
6
|
+
var path = require("path");
|
|
7
|
+
var { findMindloreDir, getProjectName, hookLog, withTelemetry } = require("./lib/mindlore-common.cjs");
|
|
8
|
+
var CODE_EXTS = /* @__PURE__ */ new Set([".ts", ".tsx", ".js", ".jsx", ".py", ".rs", ".go", ".java", ".c", ".cpp", ".h", ".css", ".scss", ".sql", ".sh", ".yaml", ".yml", ".json", ".toml", ".xml", ".cjs", ".mjs"]);
|
|
9
|
+
var PROSE_EXTS = /* @__PURE__ */ new Set([".md", ".txt", ".rst", ".adoc"]);
|
|
21
10
|
function estimateTokens(charCount, ext) {
|
|
22
|
-
const ratio = CODE_EXTS.has(ext) ? 3.5 : PROSE_EXTS.has(ext) ? 4
|
|
11
|
+
const ratio = CODE_EXTS.has(ext) ? 3.5 : PROSE_EXTS.has(ext) ? 4 : 3.75;
|
|
23
12
|
return Math.ceil(charCount / ratio);
|
|
24
13
|
}
|
|
25
|
-
|
|
26
14
|
function main() {
|
|
27
15
|
const baseDir = findMindloreDir();
|
|
28
16
|
if (!baseDir) return;
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
process.stdin.
|
|
33
|
-
process.stdin.on(
|
|
34
|
-
process.stdin.on(
|
|
35
|
-
process.stdin.on('end', () => {
|
|
17
|
+
let input = "";
|
|
18
|
+
const stdinTimeout = setTimeout(() => process.exit(0), 3e3);
|
|
19
|
+
process.stdin.setEncoding("utf8");
|
|
20
|
+
process.stdin.on("error", () => process.exit(0));
|
|
21
|
+
process.stdin.on("data", (chunk) => input += chunk);
|
|
22
|
+
process.stdin.on("end", () => {
|
|
36
23
|
clearTimeout(stdinTimeout);
|
|
37
24
|
try {
|
|
38
|
-
const data = JSON.parse(input ||
|
|
25
|
+
const data = JSON.parse(input || "{}");
|
|
39
26
|
const toolInput = data.tool_input || {};
|
|
40
27
|
const toolOutput = data.tool_output || {};
|
|
41
|
-
|
|
42
|
-
const filePath = toolInput.file_path || toolInput.path || '';
|
|
28
|
+
const filePath = toolInput.file_path || toolInput.path || "";
|
|
43
29
|
if (!filePath) return process.exit(0);
|
|
44
|
-
|
|
45
|
-
// Skip .mindlore/ internals
|
|
46
30
|
const resolved = path.resolve(filePath);
|
|
47
31
|
if (resolved.startsWith(path.resolve(baseDir))) return process.exit(0);
|
|
48
|
-
|
|
49
|
-
// Get content length from tool output or read file
|
|
50
32
|
let charCount = 0;
|
|
51
33
|
if (toolOutput.content) {
|
|
52
|
-
charCount = typeof toolOutput.content ===
|
|
53
|
-
? toolOutput.content.length
|
|
54
|
-
: JSON.stringify(toolOutput.content).length;
|
|
34
|
+
charCount = typeof toolOutput.content === "string" ? toolOutput.content.length : JSON.stringify(toolOutput.content).length;
|
|
55
35
|
} else {
|
|
56
|
-
// Fallback: read file size
|
|
57
36
|
try {
|
|
58
37
|
const stat = fs.statSync(resolved);
|
|
59
38
|
charCount = stat.size;
|
|
60
|
-
} catch {
|
|
39
|
+
} catch {
|
|
40
|
+
return process.exit(0);
|
|
41
|
+
}
|
|
61
42
|
}
|
|
62
|
-
|
|
63
43
|
if (charCount === 0) return process.exit(0);
|
|
64
|
-
|
|
65
44
|
const ext = path.extname(filePath).toLowerCase();
|
|
66
45
|
const tokens = estimateTokens(charCount, ext);
|
|
67
|
-
|
|
68
|
-
// Update _session-reads.json with token info
|
|
69
|
-
const diaryDir = path.join(baseDir, 'diary');
|
|
46
|
+
const diaryDir = path.join(baseDir, "diary");
|
|
70
47
|
const readsPath = path.join(diaryDir, `_session-reads-${getProjectName()}.json`);
|
|
71
48
|
let reads = {};
|
|
72
49
|
if (fs.existsSync(readsPath)) {
|
|
73
|
-
try {
|
|
50
|
+
try {
|
|
51
|
+
reads = JSON.parse(fs.readFileSync(readsPath, "utf8"));
|
|
52
|
+
} catch {
|
|
53
|
+
reads = {};
|
|
54
|
+
}
|
|
74
55
|
}
|
|
75
|
-
|
|
76
56
|
const normalizedPath = path.resolve(filePath);
|
|
77
57
|
const key = normalizedPath;
|
|
78
|
-
|
|
79
|
-
if (typeof reads[key] === 'number') {
|
|
80
|
-
// Upgrade from old format (just count) to new format (object)
|
|
58
|
+
if (typeof reads[key] === "number") {
|
|
81
59
|
reads[key] = { count: reads[key], tokens, chars: charCount };
|
|
82
|
-
} else if (reads[key] && typeof reads[key] ===
|
|
60
|
+
} else if (reads[key] && typeof reads[key] === "object") {
|
|
83
61
|
reads[key].tokens = tokens;
|
|
84
62
|
reads[key].chars = charCount;
|
|
85
63
|
} else {
|
|
86
64
|
reads[key] = { count: 1, tokens, chars: charCount };
|
|
87
65
|
}
|
|
88
|
-
|
|
89
|
-
fs.writeFileSync(readsPath, JSON.stringify(reads, null, 2), 'utf8');
|
|
90
|
-
|
|
91
|
-
// Output token estimate to Claude via additionalContext
|
|
66
|
+
fs.writeFileSync(readsPath, JSON.stringify(reads, null, 2), "utf8");
|
|
92
67
|
const basename = path.basename(filePath);
|
|
93
68
|
process.stdout.write(JSON.stringify({
|
|
94
69
|
hookSpecificOutput: {
|
|
95
|
-
hookEventName:
|
|
96
|
-
additionalContext: `[Mindlore: ${basename}
|
|
70
|
+
hookEventName: "PostToolUse",
|
|
71
|
+
additionalContext: `[Mindlore: ${basename} \u2014 ~${tokens} token (${charCount} char). Edit etmeyeceksen ctx_execute_file kullan.]`
|
|
97
72
|
}
|
|
98
73
|
}));
|
|
99
74
|
} catch {
|
|
100
|
-
// Silent fail
|
|
101
75
|
}
|
|
102
76
|
process.exit(0);
|
|
103
77
|
});
|
|
104
78
|
}
|
|
105
|
-
|
|
106
|
-
|
|
79
|
+
withTelemetry("mindlore-post-read", main).catch((err) => {
|
|
80
|
+
hookLog("post-read", "error", err?.message ?? String(err));
|
|
81
|
+
});
|
|
@@ -1,21 +1,13 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* mindlore-pre-compact — PreCompact hook
|
|
6
|
-
*
|
|
7
|
-
* Before context compaction:
|
|
8
|
-
* 1. Ensure FTS5 index is up to date
|
|
9
|
-
* 2. Write pre-compact episode to episodes/ and append log entry
|
|
10
|
-
*/
|
|
11
|
-
|
|
12
|
-
const fs = require('fs');
|
|
13
|
-
const path = require('path');
|
|
14
|
-
const { findMindloreDir, openDatabase, hookLog, withTelemetry, listSnapshots } = require('./lib/mindlore-common.cjs');
|
|
2
|
+
"use strict";
|
|
15
3
|
|
|
4
|
+
// hooks/src/mindlore-pre-compact.cjs
|
|
5
|
+
var fs = require("fs");
|
|
6
|
+
var path = require("path");
|
|
7
|
+
var { findMindloreDir, openDatabase, hookLog, withTelemetry, listSnapshots } = require("./lib/mindlore-common.cjs");
|
|
16
8
|
function collectRecentEpisodes(baseDir) {
|
|
17
9
|
try {
|
|
18
|
-
const dbPath = path.join(baseDir,
|
|
10
|
+
const dbPath = path.join(baseDir, "mindlore.db");
|
|
19
11
|
const db = openDatabase(dbPath, { readonly: true });
|
|
20
12
|
if (!db) return [];
|
|
21
13
|
try {
|
|
@@ -25,11 +17,11 @@ function collectRecentEpisodes(baseDir) {
|
|
|
25
17
|
if (episodes.length === 0) return [];
|
|
26
18
|
const grouped = {};
|
|
27
19
|
for (const ep of episodes) {
|
|
28
|
-
const kind = ep.kind ||
|
|
20
|
+
const kind = ep.kind || "other";
|
|
29
21
|
if (!grouped[kind]) grouped[kind] = [];
|
|
30
22
|
grouped[kind].push(ep.summary);
|
|
31
23
|
}
|
|
32
|
-
const lines = [
|
|
24
|
+
const lines = ["## Session Episodes"];
|
|
33
25
|
for (const [kind, items] of Object.entries(grouped)) {
|
|
34
26
|
lines.push(`### ${kind}`);
|
|
35
27
|
for (const item of items) lines.push(`- ${item}`);
|
|
@@ -42,26 +34,26 @@ function collectRecentEpisodes(baseDir) {
|
|
|
42
34
|
return [];
|
|
43
35
|
}
|
|
44
36
|
}
|
|
45
|
-
|
|
46
37
|
function collectGitDiff() {
|
|
47
38
|
try {
|
|
48
|
-
const { execFileSync } = require(
|
|
49
|
-
let diffStat =
|
|
39
|
+
const { execFileSync } = require("child_process");
|
|
40
|
+
let diffStat = "";
|
|
50
41
|
try {
|
|
51
|
-
diffStat = execFileSync(
|
|
52
|
-
} catch {
|
|
53
|
-
|
|
42
|
+
diffStat = execFileSync("git", ["diff", "--stat", "HEAD"], { encoding: "utf8", timeout: 5e3, stdio: ["pipe", "pipe", "pipe"] }).trim();
|
|
43
|
+
} catch {
|
|
44
|
+
diffStat = "";
|
|
45
|
+
}
|
|
46
|
+
if (diffStat) return ["## Changed Files (uncommitted)", "```", diffStat, "```"];
|
|
54
47
|
return [];
|
|
55
48
|
} catch (_err) {
|
|
56
49
|
return [];
|
|
57
50
|
}
|
|
58
51
|
}
|
|
59
|
-
|
|
60
52
|
function getActivePlan() {
|
|
61
53
|
try {
|
|
62
|
-
const plansDir = path.join(process.cwd(),
|
|
54
|
+
const plansDir = path.join(process.cwd(), ".claude", "plans");
|
|
63
55
|
if (!fs.existsSync(plansDir)) return [];
|
|
64
|
-
const plans = fs.readdirSync(plansDir).filter(f => f.endsWith(
|
|
56
|
+
const plans = fs.readdirSync(plansDir).filter((f) => f.endsWith(".md"));
|
|
65
57
|
if (plans.length === 0) return [];
|
|
66
58
|
const latestPlan = plans.sort().pop();
|
|
67
59
|
return [`## Active Plan: ${latestPlan}`];
|
|
@@ -69,86 +61,76 @@ function getActivePlan() {
|
|
|
69
61
|
return [];
|
|
70
62
|
}
|
|
71
63
|
}
|
|
72
|
-
|
|
73
64
|
function main() {
|
|
74
65
|
const baseDir = findMindloreDir();
|
|
75
66
|
if (!baseDir) return;
|
|
76
|
-
|
|
77
|
-
// Trigger FTS5 sync via the index script
|
|
78
|
-
const indexScript = path.join(__dirname, '..', 'scripts', 'mindlore-fts5-index.cjs');
|
|
67
|
+
const indexScript = path.join(__dirname, "..", "scripts", "mindlore-fts5-index.cjs");
|
|
79
68
|
if (fs.existsSync(indexScript)) {
|
|
80
69
|
try {
|
|
81
|
-
const { spawnSync } = require(
|
|
82
|
-
spawnSync(
|
|
83
|
-
timeout:
|
|
84
|
-
stdio:
|
|
85
|
-
windowsHide: true
|
|
70
|
+
const { spawnSync } = require("child_process");
|
|
71
|
+
spawnSync("node", [indexScript, baseDir], {
|
|
72
|
+
timeout: 1e4,
|
|
73
|
+
stdio: "pipe",
|
|
74
|
+
windowsHide: true
|
|
86
75
|
});
|
|
87
76
|
} catch (_err) {
|
|
88
|
-
// Non-fatal — index might fail if better-sqlite3 not available
|
|
89
77
|
}
|
|
90
78
|
}
|
|
91
|
-
|
|
92
|
-
const now = new Date();
|
|
79
|
+
const now = /* @__PURE__ */ new Date();
|
|
93
80
|
const iso = now.toISOString();
|
|
94
|
-
const ts = iso.replace(/[:.]/g,
|
|
95
|
-
|
|
96
|
-
const episodesDir = path.join(baseDir, 'episodes');
|
|
81
|
+
const ts = iso.replace(/[:.]/g, "-");
|
|
82
|
+
const episodesDir = path.join(baseDir, "episodes");
|
|
97
83
|
try {
|
|
98
84
|
const episodePath = path.join(episodesDir, `pre-compact-${ts}.md`);
|
|
99
85
|
const content = [
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
86
|
+
"---",
|
|
87
|
+
"type: episode",
|
|
88
|
+
"subtype: pre-compact",
|
|
103
89
|
`date: ${iso.slice(0, 10)}`,
|
|
104
90
|
`project: ${path.basename(process.cwd())}`,
|
|
105
|
-
|
|
106
|
-
|
|
91
|
+
"---",
|
|
92
|
+
"",
|
|
107
93
|
`Pre-compact snapshot at ${iso}.`,
|
|
108
|
-
`Working directory: ${process.cwd()}
|
|
109
|
-
].join(
|
|
110
|
-
fs.writeFileSync(episodePath, content,
|
|
111
|
-
} catch (_err) {
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
const logPath = path.join(baseDir, 'log.md');
|
|
94
|
+
`Working directory: ${process.cwd()}`
|
|
95
|
+
].join("\n");
|
|
96
|
+
fs.writeFileSync(episodePath, content, "utf8");
|
|
97
|
+
} catch (_err) {
|
|
98
|
+
}
|
|
99
|
+
const logPath = path.join(baseDir, "log.md");
|
|
115
100
|
try {
|
|
116
|
-
const entry = `| ${iso.slice(0, 10)} | pre-compact | FTS5 flush before compaction
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
const diaryDir = path.join(baseDir,
|
|
101
|
+
const entry = `| ${iso.slice(0, 10)} | pre-compact | FTS5 flush before compaction |
|
|
102
|
+
`;
|
|
103
|
+
fs.appendFileSync(logPath, entry, "utf8");
|
|
104
|
+
} catch (_err) {
|
|
105
|
+
}
|
|
106
|
+
const diaryDir = path.join(baseDir, "diary");
|
|
122
107
|
try {
|
|
123
108
|
const sections = [];
|
|
124
109
|
sections.push(...collectRecentEpisodes(baseDir));
|
|
125
110
|
sections.push(...collectGitDiff());
|
|
126
111
|
sections.push(...getActivePlan());
|
|
127
|
-
|
|
128
112
|
if (sections.length > 0) {
|
|
129
113
|
const snapshotContent = [
|
|
130
|
-
|
|
131
|
-
|
|
114
|
+
"---",
|
|
115
|
+
"type: compaction-snapshot",
|
|
132
116
|
`date: ${iso.slice(0, 10)}`,
|
|
133
117
|
`project: ${path.basename(process.cwd())}`,
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
...sections
|
|
137
|
-
].join(
|
|
118
|
+
"---",
|
|
119
|
+
"",
|
|
120
|
+
...sections
|
|
121
|
+
].join("\n");
|
|
138
122
|
fs.writeFileSync(path.join(diaryDir, `compaction-snapshot-${ts}.md`), snapshotContent);
|
|
139
123
|
}
|
|
140
|
-
|
|
141
|
-
const snapshots = listSnapshots(diaryDir).filter(f => f.startsWith('compaction-'));
|
|
124
|
+
const snapshots = listSnapshots(diaryDir).filter((f) => f.startsWith("compaction-"));
|
|
142
125
|
while (snapshots.length > 5) {
|
|
143
126
|
const oldest = snapshots.shift();
|
|
144
127
|
if (oldest) fs.unlinkSync(path.join(diaryDir, oldest));
|
|
145
128
|
}
|
|
146
|
-
} catch (_err) {
|
|
147
|
-
|
|
148
|
-
process.stdout.write(
|
|
129
|
+
} catch (_err) {
|
|
130
|
+
}
|
|
131
|
+
process.stdout.write("[Mindlore: pre-compact FTS5 flush complete]\n");
|
|
149
132
|
}
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
hookLog('mindlore-pre-compact', 'error', err?.message ?? String(err));
|
|
133
|
+
withTelemetry("mindlore-pre-compact", main).catch((err) => {
|
|
134
|
+
hookLog("mindlore-pre-compact", "error", err?.message ?? String(err));
|
|
153
135
|
process.exit(0);
|
|
154
136
|
});
|
|
@@ -1,60 +1,40 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* mindlore-read-guard — PreToolUse hook (if: "Read")
|
|
6
|
-
*
|
|
7
|
-
* Repeated-read detection: detects files read multiple times
|
|
8
|
-
* in the same session and emits a soft warning.
|
|
9
|
-
* Does NOT block (exit 0) — advisory only.
|
|
10
|
-
*
|
|
11
|
-
* Storage: .mindlore/diary/_session-reads.json
|
|
12
|
-
* Cleanup: session-end hook writes stats to delta then deletes the file.
|
|
13
|
-
*/
|
|
14
|
-
|
|
15
|
-
const fs = require('fs');
|
|
16
|
-
const path = require('path');
|
|
17
|
-
const { findMindloreDir, readHookStdin, getProjectName, hookLog, extractSkeleton, withTelemetrySync } = require('./lib/mindlore-common.cjs');
|
|
2
|
+
"use strict";
|
|
18
3
|
|
|
4
|
+
// hooks/src/mindlore-read-guard.cjs
|
|
5
|
+
var fs = require("fs");
|
|
6
|
+
var path = require("path");
|
|
7
|
+
var { findMindloreDir, readHookStdin, getProjectName, hookLog, extractSkeleton, withTelemetrySync } = require("./lib/mindlore-common.cjs");
|
|
19
8
|
function main() {
|
|
20
9
|
const baseDir = findMindloreDir();
|
|
21
10
|
if (!baseDir) return;
|
|
22
|
-
|
|
23
|
-
const filePath = readHookStdin(['file_path', 'path']);
|
|
11
|
+
const filePath = readHookStdin(["file_path", "path"]);
|
|
24
12
|
if (!filePath) return;
|
|
25
|
-
|
|
26
|
-
// Only track CWD-relative files, skip .mindlore/ internals
|
|
27
13
|
const cwd = process.cwd();
|
|
28
14
|
const resolved = path.resolve(filePath);
|
|
29
15
|
if (!resolved.startsWith(cwd)) return;
|
|
30
16
|
if (resolved.startsWith(path.resolve(baseDir))) return;
|
|
31
|
-
|
|
32
|
-
// Load or create session reads tracker
|
|
33
|
-
const diaryDir = path.join(baseDir, 'diary');
|
|
17
|
+
const diaryDir = path.join(baseDir, "diary");
|
|
34
18
|
if (!fs.existsSync(diaryDir)) {
|
|
35
19
|
fs.mkdirSync(diaryDir, { recursive: true });
|
|
36
20
|
}
|
|
37
|
-
|
|
38
21
|
const readsPath = path.join(diaryDir, `_session-reads-${getProjectName()}.json`);
|
|
39
22
|
let reads = {};
|
|
40
23
|
if (fs.existsSync(readsPath)) {
|
|
41
24
|
try {
|
|
42
|
-
reads = JSON.parse(fs.readFileSync(readsPath,
|
|
25
|
+
reads = JSON.parse(fs.readFileSync(readsPath, "utf8"));
|
|
43
26
|
} catch (_err) {
|
|
44
27
|
reads = {};
|
|
45
28
|
}
|
|
46
29
|
}
|
|
47
|
-
|
|
48
30
|
const normalizedPath = path.resolve(filePath);
|
|
49
31
|
const existing = reads[normalizedPath];
|
|
50
|
-
|
|
51
|
-
// Support both old format (number) and new format (object with tokens)
|
|
52
32
|
let count, tokens;
|
|
53
|
-
if (typeof existing ===
|
|
33
|
+
if (typeof existing === "number") {
|
|
54
34
|
count = existing + 1;
|
|
55
35
|
tokens = 0;
|
|
56
36
|
reads[normalizedPath] = { count, tokens: 0, chars: 0 };
|
|
57
|
-
} else if (existing && typeof existing ===
|
|
37
|
+
} else if (existing && typeof existing === "object") {
|
|
58
38
|
count = (existing.count || 0) + 1;
|
|
59
39
|
tokens = existing.tokens || 0;
|
|
60
40
|
existing.count = count;
|
|
@@ -64,42 +44,39 @@ function main() {
|
|
|
64
44
|
tokens = 0;
|
|
65
45
|
reads[normalizedPath] = { count, tokens: 0, chars: 0 };
|
|
66
46
|
}
|
|
67
|
-
|
|
68
|
-
// Write updated reads
|
|
69
|
-
fs.writeFileSync(readsPath, JSON.stringify(reads, null, 2), 'utf8');
|
|
70
|
-
|
|
47
|
+
fs.writeFileSync(readsPath, JSON.stringify(reads, null, 2), "utf8");
|
|
71
48
|
const basename = path.basename(filePath);
|
|
72
|
-
const tokenInfo = tokens > 0 ? ` (~${tokens} token)` :
|
|
73
|
-
|
|
74
|
-
// Block on 3+ repeated reads (exit 2 = block tool call)
|
|
49
|
+
const tokenInfo = tokens > 0 ? ` (~${tokens} token)` : "";
|
|
75
50
|
if (count >= 3) {
|
|
76
|
-
const totalWaste = tokens > 0 ? ` Toplam israf: ~${tokens * (count - 1)} token.` :
|
|
51
|
+
const totalWaste = tokens > 0 ? ` Toplam israf: ~${tokens * (count - 1)} token.` : "";
|
|
77
52
|
process.stderr.write(`[Mindlore BLOCK] ${basename}${tokenInfo} bu session'da ${count}. kez okunuyor.${totalWaste} Edit icin gerekiyorsa once degisikligini yap, sonra tekrar oku. Analiz icin ctx_execute_file kullan.`);
|
|
78
53
|
process.exit(2);
|
|
79
54
|
}
|
|
80
|
-
|
|
81
|
-
// Warn on 2nd read (exit 0 = allow but warn)
|
|
82
55
|
if (count > 1) {
|
|
83
|
-
const totalWaste = tokens > 0 ? ` Toplam tekrar: ~${tokens * (count - 1)} token.` :
|
|
84
|
-
let skeletonSection =
|
|
56
|
+
const totalWaste = tokens > 0 ? ` Toplam tekrar: ~${tokens * (count - 1)} token.` : "";
|
|
57
|
+
let skeletonSection = "";
|
|
85
58
|
try {
|
|
86
59
|
const ext = path.extname(filePath).slice(1);
|
|
87
|
-
const fileContent = fs.readFileSync(filePath,
|
|
88
|
-
if (fileContent.length <
|
|
60
|
+
const fileContent = fs.readFileSync(filePath, "utf8");
|
|
61
|
+
if (fileContent.length < 5e5) {
|
|
89
62
|
const skeleton = extractSkeleton(fileContent, ext);
|
|
90
63
|
if (skeleton !== fileContent) {
|
|
91
|
-
const truncated = skeleton.length >
|
|
92
|
-
skeletonSection =
|
|
64
|
+
const truncated = skeleton.length > 2e3 ? skeleton.slice(0, 2e3) + "\n...[truncated]" : skeleton;
|
|
65
|
+
skeletonSection = "\n\n" + truncated;
|
|
93
66
|
}
|
|
94
67
|
}
|
|
95
|
-
} catch (_e) {
|
|
68
|
+
} catch (_e) {
|
|
69
|
+
}
|
|
96
70
|
process.stdout.write(JSON.stringify({
|
|
97
71
|
hookSpecificOutput: {
|
|
98
|
-
hookEventName:
|
|
99
|
-
additionalContext: `[Mindlore: ${basename}${tokenInfo} bu session'da ${count}. kez okunuyor.${totalWaste} Bir sonraki okuma engellenecek
|
|
72
|
+
hookEventName: "PreToolUse",
|
|
73
|
+
additionalContext: `[Mindlore: ${basename}${tokenInfo} bu session'da ${count}. kez okunuyor.${totalWaste} Bir sonraki okuma engellenecek \u2014 Edit gerekiyorsa simdi yap.]${skeletonSection}`
|
|
100
74
|
}
|
|
101
75
|
}));
|
|
102
76
|
}
|
|
103
77
|
}
|
|
104
|
-
|
|
105
|
-
|
|
78
|
+
try {
|
|
79
|
+
withTelemetrySync("mindlore-read-guard", main);
|
|
80
|
+
} catch (err) {
|
|
81
|
+
hookLog("read-guard", "error", err?.message ?? String(err));
|
|
82
|
+
}
|