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,154 +1,154 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
'use strict';
|
|
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');
|
|
15
|
-
|
|
16
|
-
function collectRecentEpisodes(baseDir) {
|
|
17
|
-
try {
|
|
18
|
-
const dbPath = path.join(baseDir, 'mindlore.db');
|
|
19
|
-
const db = openDatabase(dbPath, { readonly: true });
|
|
20
|
-
if (!db) return [];
|
|
21
|
-
try {
|
|
22
|
-
const episodes = db.prepare(
|
|
23
|
-
"SELECT kind, summary FROM episodes WHERE created_at > datetime('now', '-4 hours') ORDER BY created_at DESC LIMIT 20"
|
|
24
|
-
).all();
|
|
25
|
-
if (episodes.length === 0) return [];
|
|
26
|
-
const grouped = {};
|
|
27
|
-
for (const ep of episodes) {
|
|
28
|
-
const kind = ep.kind || 'other';
|
|
29
|
-
if (!grouped[kind]) grouped[kind] = [];
|
|
30
|
-
grouped[kind].push(ep.summary);
|
|
31
|
-
}
|
|
32
|
-
const lines = ['## Session Episodes'];
|
|
33
|
-
for (const [kind, items] of Object.entries(grouped)) {
|
|
34
|
-
lines.push(`### ${kind}`);
|
|
35
|
-
for (const item of items) lines.push(`- ${item}`);
|
|
36
|
-
}
|
|
37
|
-
return lines;
|
|
38
|
-
} finally {
|
|
39
|
-
db.close();
|
|
40
|
-
}
|
|
41
|
-
} catch (_err) {
|
|
42
|
-
return [];
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
function collectGitDiff() {
|
|
47
|
-
try {
|
|
48
|
-
const { execFileSync } = require('child_process');
|
|
49
|
-
let diffStat = '';
|
|
50
|
-
try {
|
|
51
|
-
diffStat = execFileSync('git', ['diff', '--stat', 'HEAD'], { encoding: 'utf8', timeout: 5000, stdio: ['pipe', 'pipe', 'pipe'] }).trim();
|
|
52
|
-
} catch { diffStat = ''; }
|
|
53
|
-
if (diffStat) return ['## Changed Files (uncommitted)', '```', diffStat, '```'];
|
|
54
|
-
return [];
|
|
55
|
-
} catch (_err) {
|
|
56
|
-
return [];
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
function getActivePlan() {
|
|
61
|
-
try {
|
|
62
|
-
const plansDir = path.join(process.cwd(), '.claude', 'plans');
|
|
63
|
-
if (!fs.existsSync(plansDir)) return [];
|
|
64
|
-
const plans = fs.readdirSync(plansDir).filter(f => f.endsWith('.md'));
|
|
65
|
-
if (plans.length === 0) return [];
|
|
66
|
-
const latestPlan = plans.sort().pop();
|
|
67
|
-
return [`## Active Plan: ${latestPlan}`];
|
|
68
|
-
} catch (_err) {
|
|
69
|
-
return [];
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
function main() {
|
|
74
|
-
const baseDir = findMindloreDir();
|
|
75
|
-
if (!baseDir) return;
|
|
76
|
-
|
|
77
|
-
// Trigger FTS5 sync via the index script
|
|
78
|
-
const indexScript = path.join(__dirname, '..', 'scripts', 'mindlore-fts5-index.cjs');
|
|
79
|
-
if (fs.existsSync(indexScript)) {
|
|
80
|
-
try {
|
|
81
|
-
const { spawnSync } = require('child_process');
|
|
82
|
-
spawnSync('node', [indexScript, baseDir], {
|
|
83
|
-
timeout: 10000,
|
|
84
|
-
stdio: 'pipe',
|
|
85
|
-
windowsHide: true,
|
|
86
|
-
});
|
|
87
|
-
} catch (_err) {
|
|
88
|
-
// Non-fatal — index might fail if better-sqlite3 not available
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
const now = new Date();
|
|
93
|
-
const iso = now.toISOString();
|
|
94
|
-
const ts = iso.replace(/[:.]/g, '-');
|
|
95
|
-
|
|
96
|
-
const episodesDir = path.join(baseDir, 'episodes');
|
|
97
|
-
try {
|
|
98
|
-
const episodePath = path.join(episodesDir, `pre-compact-${ts}.md`);
|
|
99
|
-
const content = [
|
|
100
|
-
'---',
|
|
101
|
-
'type: episode',
|
|
102
|
-
'subtype: pre-compact',
|
|
103
|
-
`date: ${iso.slice(0, 10)}`,
|
|
104
|
-
`project: ${path.basename(process.cwd())}`,
|
|
105
|
-
'---',
|
|
106
|
-
'',
|
|
107
|
-
`Pre-compact snapshot at ${iso}.`,
|
|
108
|
-
`Working directory: ${process.cwd()}`,
|
|
109
|
-
].join('\n');
|
|
110
|
-
fs.writeFileSync(episodePath, content, 'utf8');
|
|
111
|
-
} catch (_err) { /* episodes dir may not exist */ }
|
|
112
|
-
|
|
113
|
-
// Append log entry
|
|
114
|
-
const logPath = path.join(baseDir, 'log.md');
|
|
115
|
-
try {
|
|
116
|
-
const entry = `| ${iso.slice(0, 10)} | pre-compact | FTS5 flush before compaction |\n`;
|
|
117
|
-
fs.appendFileSync(logPath, entry, 'utf8');
|
|
118
|
-
} catch (_err) { /* log file may not exist */ }
|
|
119
|
-
|
|
120
|
-
// Build compaction snapshot (#17)
|
|
121
|
-
const diaryDir = path.join(baseDir, 'diary');
|
|
122
|
-
try {
|
|
123
|
-
const sections = [];
|
|
124
|
-
sections.push(...collectRecentEpisodes(baseDir));
|
|
125
|
-
sections.push(...collectGitDiff());
|
|
126
|
-
sections.push(...getActivePlan());
|
|
127
|
-
|
|
128
|
-
if (sections.length > 0) {
|
|
129
|
-
const snapshotContent = [
|
|
130
|
-
'---',
|
|
131
|
-
'type: compaction-snapshot',
|
|
132
|
-
`date: ${iso.slice(0, 10)}`,
|
|
133
|
-
`project: ${path.basename(process.cwd())}`,
|
|
134
|
-
'---',
|
|
135
|
-
'',
|
|
136
|
-
...sections,
|
|
137
|
-
].join('\n');
|
|
138
|
-
fs.writeFileSync(path.join(diaryDir, `compaction-snapshot-${ts}.md`), snapshotContent);
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
const snapshots = listSnapshots(diaryDir).filter(f => f.startsWith('compaction-'));
|
|
142
|
-
while (snapshots.length > 5) {
|
|
143
|
-
const oldest = snapshots.shift();
|
|
144
|
-
if (oldest) fs.unlinkSync(path.join(diaryDir, oldest));
|
|
145
|
-
}
|
|
146
|
-
} catch (_err) { /* snapshot is best-effort */ }
|
|
147
|
-
|
|
148
|
-
process.stdout.write('[Mindlore: pre-compact FTS5 flush complete]\n');
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
withTelemetry('mindlore-pre-compact', main).catch(err => {
|
|
152
|
-
hookLog('mindlore-pre-compact', 'error', err?.message ?? String(err));
|
|
153
|
-
process.exit(0);
|
|
154
|
-
});
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
'use strict';
|
|
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');
|
|
15
|
+
|
|
16
|
+
function collectRecentEpisodes(baseDir) {
|
|
17
|
+
try {
|
|
18
|
+
const dbPath = path.join(baseDir, 'mindlore.db');
|
|
19
|
+
const db = openDatabase(dbPath, { readonly: true });
|
|
20
|
+
if (!db) return [];
|
|
21
|
+
try {
|
|
22
|
+
const episodes = db.prepare(
|
|
23
|
+
"SELECT kind, summary FROM episodes WHERE created_at > datetime('now', '-4 hours') ORDER BY created_at DESC LIMIT 20"
|
|
24
|
+
).all();
|
|
25
|
+
if (episodes.length === 0) return [];
|
|
26
|
+
const grouped = {};
|
|
27
|
+
for (const ep of episodes) {
|
|
28
|
+
const kind = ep.kind || 'other';
|
|
29
|
+
if (!grouped[kind]) grouped[kind] = [];
|
|
30
|
+
grouped[kind].push(ep.summary);
|
|
31
|
+
}
|
|
32
|
+
const lines = ['## Session Episodes'];
|
|
33
|
+
for (const [kind, items] of Object.entries(grouped)) {
|
|
34
|
+
lines.push(`### ${kind}`);
|
|
35
|
+
for (const item of items) lines.push(`- ${item}`);
|
|
36
|
+
}
|
|
37
|
+
return lines;
|
|
38
|
+
} finally {
|
|
39
|
+
db.close();
|
|
40
|
+
}
|
|
41
|
+
} catch (_err) {
|
|
42
|
+
return [];
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function collectGitDiff() {
|
|
47
|
+
try {
|
|
48
|
+
const { execFileSync } = require('child_process');
|
|
49
|
+
let diffStat = '';
|
|
50
|
+
try {
|
|
51
|
+
diffStat = execFileSync('git', ['diff', '--stat', 'HEAD'], { encoding: 'utf8', timeout: 5000, stdio: ['pipe', 'pipe', 'pipe'] }).trim();
|
|
52
|
+
} catch { diffStat = ''; }
|
|
53
|
+
if (diffStat) return ['## Changed Files (uncommitted)', '```', diffStat, '```'];
|
|
54
|
+
return [];
|
|
55
|
+
} catch (_err) {
|
|
56
|
+
return [];
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function getActivePlan() {
|
|
61
|
+
try {
|
|
62
|
+
const plansDir = path.join(process.cwd(), '.claude', 'plans');
|
|
63
|
+
if (!fs.existsSync(plansDir)) return [];
|
|
64
|
+
const plans = fs.readdirSync(plansDir).filter(f => f.endsWith('.md'));
|
|
65
|
+
if (plans.length === 0) return [];
|
|
66
|
+
const latestPlan = plans.sort().pop();
|
|
67
|
+
return [`## Active Plan: ${latestPlan}`];
|
|
68
|
+
} catch (_err) {
|
|
69
|
+
return [];
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function main() {
|
|
74
|
+
const baseDir = findMindloreDir();
|
|
75
|
+
if (!baseDir) return;
|
|
76
|
+
|
|
77
|
+
// Trigger FTS5 sync via the index script
|
|
78
|
+
const indexScript = path.join(__dirname, '..', 'scripts', 'mindlore-fts5-index.cjs');
|
|
79
|
+
if (fs.existsSync(indexScript)) {
|
|
80
|
+
try {
|
|
81
|
+
const { spawnSync } = require('child_process');
|
|
82
|
+
spawnSync('node', [indexScript, baseDir], {
|
|
83
|
+
timeout: 10000,
|
|
84
|
+
stdio: 'pipe',
|
|
85
|
+
windowsHide: true,
|
|
86
|
+
});
|
|
87
|
+
} catch (_err) {
|
|
88
|
+
// Non-fatal — index might fail if better-sqlite3 not available
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
const now = new Date();
|
|
93
|
+
const iso = now.toISOString();
|
|
94
|
+
const ts = iso.replace(/[:.]/g, '-');
|
|
95
|
+
|
|
96
|
+
const episodesDir = path.join(baseDir, 'episodes');
|
|
97
|
+
try {
|
|
98
|
+
const episodePath = path.join(episodesDir, `pre-compact-${ts}.md`);
|
|
99
|
+
const content = [
|
|
100
|
+
'---',
|
|
101
|
+
'type: episode',
|
|
102
|
+
'subtype: pre-compact',
|
|
103
|
+
`date: ${iso.slice(0, 10)}`,
|
|
104
|
+
`project: ${path.basename(process.cwd())}`,
|
|
105
|
+
'---',
|
|
106
|
+
'',
|
|
107
|
+
`Pre-compact snapshot at ${iso}.`,
|
|
108
|
+
`Working directory: ${process.cwd()}`,
|
|
109
|
+
].join('\n');
|
|
110
|
+
fs.writeFileSync(episodePath, content, 'utf8');
|
|
111
|
+
} catch (_err) { /* episodes dir may not exist */ }
|
|
112
|
+
|
|
113
|
+
// Append log entry
|
|
114
|
+
const logPath = path.join(baseDir, 'log.md');
|
|
115
|
+
try {
|
|
116
|
+
const entry = `| ${iso.slice(0, 10)} | pre-compact | FTS5 flush before compaction |\n`;
|
|
117
|
+
fs.appendFileSync(logPath, entry, 'utf8');
|
|
118
|
+
} catch (_err) { /* log file may not exist */ }
|
|
119
|
+
|
|
120
|
+
// Build compaction snapshot (#17)
|
|
121
|
+
const diaryDir = path.join(baseDir, 'diary');
|
|
122
|
+
try {
|
|
123
|
+
const sections = [];
|
|
124
|
+
sections.push(...collectRecentEpisodes(baseDir));
|
|
125
|
+
sections.push(...collectGitDiff());
|
|
126
|
+
sections.push(...getActivePlan());
|
|
127
|
+
|
|
128
|
+
if (sections.length > 0) {
|
|
129
|
+
const snapshotContent = [
|
|
130
|
+
'---',
|
|
131
|
+
'type: compaction-snapshot',
|
|
132
|
+
`date: ${iso.slice(0, 10)}`,
|
|
133
|
+
`project: ${path.basename(process.cwd())}`,
|
|
134
|
+
'---',
|
|
135
|
+
'',
|
|
136
|
+
...sections,
|
|
137
|
+
].join('\n');
|
|
138
|
+
fs.writeFileSync(path.join(diaryDir, `compaction-snapshot-${ts}.md`), snapshotContent);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
const snapshots = listSnapshots(diaryDir).filter(f => f.startsWith('compaction-'));
|
|
142
|
+
while (snapshots.length > 5) {
|
|
143
|
+
const oldest = snapshots.shift();
|
|
144
|
+
if (oldest) fs.unlinkSync(path.join(diaryDir, oldest));
|
|
145
|
+
}
|
|
146
|
+
} catch (_err) { /* snapshot is best-effort */ }
|
|
147
|
+
|
|
148
|
+
process.stdout.write('[Mindlore: pre-compact FTS5 flush complete]\n');
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
withTelemetry('mindlore-pre-compact', main).catch(err => {
|
|
152
|
+
hookLog('mindlore-pre-compact', 'error', err?.message ?? String(err));
|
|
153
|
+
process.exit(0);
|
|
154
|
+
});
|
|
@@ -1,105 +1,105 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
'use strict';
|
|
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');
|
|
18
|
-
|
|
19
|
-
function main() {
|
|
20
|
-
const baseDir = findMindloreDir();
|
|
21
|
-
if (!baseDir) return;
|
|
22
|
-
|
|
23
|
-
const filePath = readHookStdin(['file_path', 'path']);
|
|
24
|
-
if (!filePath) return;
|
|
25
|
-
|
|
26
|
-
// Only track CWD-relative files, skip .mindlore/ internals
|
|
27
|
-
const cwd = process.cwd();
|
|
28
|
-
const resolved = path.resolve(filePath);
|
|
29
|
-
if (!resolved.startsWith(cwd)) return;
|
|
30
|
-
if (resolved.startsWith(path.resolve(baseDir))) return;
|
|
31
|
-
|
|
32
|
-
// Load or create session reads tracker
|
|
33
|
-
const diaryDir = path.join(baseDir, 'diary');
|
|
34
|
-
if (!fs.existsSync(diaryDir)) {
|
|
35
|
-
fs.mkdirSync(diaryDir, { recursive: true });
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
const readsPath = path.join(diaryDir, `_session-reads-${getProjectName()}.json`);
|
|
39
|
-
let reads = {};
|
|
40
|
-
if (fs.existsSync(readsPath)) {
|
|
41
|
-
try {
|
|
42
|
-
reads = JSON.parse(fs.readFileSync(readsPath, 'utf8'));
|
|
43
|
-
} catch (_err) {
|
|
44
|
-
reads = {};
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
const normalizedPath = path.resolve(filePath);
|
|
49
|
-
const existing = reads[normalizedPath];
|
|
50
|
-
|
|
51
|
-
// Support both old format (number) and new format (object with tokens)
|
|
52
|
-
let count, tokens;
|
|
53
|
-
if (typeof existing === 'number') {
|
|
54
|
-
count = existing + 1;
|
|
55
|
-
tokens = 0;
|
|
56
|
-
reads[normalizedPath] = { count, tokens: 0, chars: 0 };
|
|
57
|
-
} else if (existing && typeof existing === 'object') {
|
|
58
|
-
count = (existing.count || 0) + 1;
|
|
59
|
-
tokens = existing.tokens || 0;
|
|
60
|
-
existing.count = count;
|
|
61
|
-
reads[normalizedPath] = existing;
|
|
62
|
-
} else {
|
|
63
|
-
count = 1;
|
|
64
|
-
tokens = 0;
|
|
65
|
-
reads[normalizedPath] = { count, tokens: 0, chars: 0 };
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
// Write updated reads
|
|
69
|
-
fs.writeFileSync(readsPath, JSON.stringify(reads, null, 2), 'utf8');
|
|
70
|
-
|
|
71
|
-
const basename = path.basename(filePath);
|
|
72
|
-
const tokenInfo = tokens > 0 ? ` (~${tokens} token)` : '';
|
|
73
|
-
|
|
74
|
-
// Block on 3+ repeated reads (exit 2 = block tool call)
|
|
75
|
-
if (count >= 3) {
|
|
76
|
-
const totalWaste = tokens > 0 ? ` Toplam israf: ~${tokens * (count - 1)} token.` : '';
|
|
77
|
-
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
|
-
process.exit(2);
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
// Warn on 2nd read (exit 0 = allow but warn)
|
|
82
|
-
if (count > 1) {
|
|
83
|
-
const totalWaste = tokens > 0 ? ` Toplam tekrar: ~${tokens * (count - 1)} token.` : '';
|
|
84
|
-
let skeletonSection = '';
|
|
85
|
-
try {
|
|
86
|
-
const ext = path.extname(filePath).slice(1);
|
|
87
|
-
const fileContent = fs.readFileSync(filePath, 'utf8');
|
|
88
|
-
if (fileContent.length < 500_000) {
|
|
89
|
-
const skeleton = extractSkeleton(fileContent, ext);
|
|
90
|
-
if (skeleton !== fileContent) {
|
|
91
|
-
const truncated = skeleton.length > 2000 ? skeleton.slice(0, 2000) + '\n...[truncated]' : skeleton;
|
|
92
|
-
skeletonSection = '\n\n' + truncated;
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
} catch (_e) { /* unreadable/binary — skip */ }
|
|
96
|
-
process.stdout.write(JSON.stringify({
|
|
97
|
-
hookSpecificOutput: {
|
|
98
|
-
hookEventName: 'PreToolUse',
|
|
99
|
-
additionalContext: `[Mindlore: ${basename}${tokenInfo} bu session'da ${count}. kez okunuyor.${totalWaste} Bir sonraki okuma engellenecek — Edit gerekiyorsa simdi yap.]${skeletonSection}`
|
|
100
|
-
}
|
|
101
|
-
}));
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
try { withTelemetrySync('mindlore-read-guard', main); } catch (err) { hookLog('read-guard', 'error', err?.message ?? String(err)); }
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
'use strict';
|
|
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');
|
|
18
|
+
|
|
19
|
+
function main() {
|
|
20
|
+
const baseDir = findMindloreDir();
|
|
21
|
+
if (!baseDir) return;
|
|
22
|
+
|
|
23
|
+
const filePath = readHookStdin(['file_path', 'path']);
|
|
24
|
+
if (!filePath) return;
|
|
25
|
+
|
|
26
|
+
// Only track CWD-relative files, skip .mindlore/ internals
|
|
27
|
+
const cwd = process.cwd();
|
|
28
|
+
const resolved = path.resolve(filePath);
|
|
29
|
+
if (!resolved.startsWith(cwd)) return;
|
|
30
|
+
if (resolved.startsWith(path.resolve(baseDir))) return;
|
|
31
|
+
|
|
32
|
+
// Load or create session reads tracker
|
|
33
|
+
const diaryDir = path.join(baseDir, 'diary');
|
|
34
|
+
if (!fs.existsSync(diaryDir)) {
|
|
35
|
+
fs.mkdirSync(diaryDir, { recursive: true });
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const readsPath = path.join(diaryDir, `_session-reads-${getProjectName()}.json`);
|
|
39
|
+
let reads = {};
|
|
40
|
+
if (fs.existsSync(readsPath)) {
|
|
41
|
+
try {
|
|
42
|
+
reads = JSON.parse(fs.readFileSync(readsPath, 'utf8'));
|
|
43
|
+
} catch (_err) {
|
|
44
|
+
reads = {};
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const normalizedPath = path.resolve(filePath);
|
|
49
|
+
const existing = reads[normalizedPath];
|
|
50
|
+
|
|
51
|
+
// Support both old format (number) and new format (object with tokens)
|
|
52
|
+
let count, tokens;
|
|
53
|
+
if (typeof existing === 'number') {
|
|
54
|
+
count = existing + 1;
|
|
55
|
+
tokens = 0;
|
|
56
|
+
reads[normalizedPath] = { count, tokens: 0, chars: 0 };
|
|
57
|
+
} else if (existing && typeof existing === 'object') {
|
|
58
|
+
count = (existing.count || 0) + 1;
|
|
59
|
+
tokens = existing.tokens || 0;
|
|
60
|
+
existing.count = count;
|
|
61
|
+
reads[normalizedPath] = existing;
|
|
62
|
+
} else {
|
|
63
|
+
count = 1;
|
|
64
|
+
tokens = 0;
|
|
65
|
+
reads[normalizedPath] = { count, tokens: 0, chars: 0 };
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// Write updated reads
|
|
69
|
+
fs.writeFileSync(readsPath, JSON.stringify(reads, null, 2), 'utf8');
|
|
70
|
+
|
|
71
|
+
const basename = path.basename(filePath);
|
|
72
|
+
const tokenInfo = tokens > 0 ? ` (~${tokens} token)` : '';
|
|
73
|
+
|
|
74
|
+
// Block on 3+ repeated reads (exit 2 = block tool call)
|
|
75
|
+
if (count >= 3) {
|
|
76
|
+
const totalWaste = tokens > 0 ? ` Toplam israf: ~${tokens * (count - 1)} token.` : '';
|
|
77
|
+
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
|
+
process.exit(2);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// Warn on 2nd read (exit 0 = allow but warn)
|
|
82
|
+
if (count > 1) {
|
|
83
|
+
const totalWaste = tokens > 0 ? ` Toplam tekrar: ~${tokens * (count - 1)} token.` : '';
|
|
84
|
+
let skeletonSection = '';
|
|
85
|
+
try {
|
|
86
|
+
const ext = path.extname(filePath).slice(1);
|
|
87
|
+
const fileContent = fs.readFileSync(filePath, 'utf8');
|
|
88
|
+
if (fileContent.length < 500_000) {
|
|
89
|
+
const skeleton = extractSkeleton(fileContent, ext);
|
|
90
|
+
if (skeleton !== fileContent) {
|
|
91
|
+
const truncated = skeleton.length > 2000 ? skeleton.slice(0, 2000) + '\n...[truncated]' : skeleton;
|
|
92
|
+
skeletonSection = '\n\n' + truncated;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
} catch (_e) { /* unreadable/binary — skip */ }
|
|
96
|
+
process.stdout.write(JSON.stringify({
|
|
97
|
+
hookSpecificOutput: {
|
|
98
|
+
hookEventName: 'PreToolUse',
|
|
99
|
+
additionalContext: `[Mindlore: ${basename}${tokenInfo} bu session'da ${count}. kez okunuyor.${totalWaste} Bir sonraki okuma engellenecek — Edit gerekiyorsa simdi yap.]${skeletonSection}`
|
|
100
|
+
}
|
|
101
|
+
}));
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
try { withTelemetrySync('mindlore-read-guard', main); } catch (err) { hookLog('read-guard', 'error', err?.message ?? String(err)); }
|