mindlore 0.6.5 → 0.6.6
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 +1 -1
- package/dist/scripts/init.js +34 -16
- package/dist/scripts/init.js.map +1 -1
- package/dist/scripts/lib/all-migrations.d.ts +6 -0
- package/dist/scripts/lib/all-migrations.d.ts.map +1 -0
- package/dist/scripts/lib/all-migrations.js +32 -0
- package/dist/scripts/lib/all-migrations.js.map +1 -0
- package/dist/scripts/lib/migrations-v066.d.ts +4 -0
- package/dist/scripts/lib/migrations-v066.d.ts.map +1 -0
- package/dist/scripts/lib/migrations-v066.js +14 -0
- package/dist/scripts/lib/migrations-v066.js.map +1 -0
- package/dist/scripts/lib/session-payload.d.ts +1 -1
- package/dist/scripts/lib/session-payload.d.ts.map +1 -1
- package/dist/scripts/lib/session-payload.js +24 -6
- package/dist/scripts/lib/session-payload.js.map +1 -1
- package/dist/scripts/mindlore-fts5-index.js +2 -5
- package/dist/scripts/mindlore-fts5-index.js.map +1 -1
- package/dist/tests/episodes-inject.test.js +71 -0
- package/dist/tests/episodes-inject.test.js.map +1 -1
- package/dist/tests/fts5.test.js +23 -0
- package/dist/tests/fts5.test.js.map +1 -1
- package/dist/tests/helpers/db.d.ts +1 -0
- package/dist/tests/helpers/db.d.ts.map +1 -1
- package/dist/tests/helpers/db.js +9 -1
- package/dist/tests/helpers/db.js.map +1 -1
- package/dist/tests/init.test.js +26 -0
- package/dist/tests/init.test.js.map +1 -1
- package/dist/tests/migrations-v063.test.js +1 -1
- package/dist/tests/migrations-v066.test.d.ts +2 -0
- package/dist/tests/migrations-v066.test.d.ts.map +1 -0
- package/dist/tests/migrations-v066.test.js +54 -0
- package/dist/tests/migrations-v066.test.js.map +1 -0
- package/dist/tests/obsidian.test.js +2 -1
- package/dist/tests/obsidian.test.js.map +1 -1
- package/dist/tests/session-focus-helpers.test.d.ts +2 -0
- package/dist/tests/session-focus-helpers.test.d.ts.map +1 -0
- package/dist/tests/session-focus-helpers.test.js +80 -0
- package/dist/tests/session-focus-helpers.test.js.map +1 -0
- package/dist/tests/session-focus.test.js +19 -0
- package/dist/tests/session-focus.test.js.map +1 -1
- package/dist/tests/stats-skill.test.d.ts +2 -0
- package/dist/tests/stats-skill.test.d.ts.map +1 -0
- package/dist/tests/stats-skill.test.js +55 -0
- package/dist/tests/stats-skill.test.js.map +1 -0
- package/hooks/mindlore-search.cjs +2 -1
- package/hooks/mindlore-session-focus.cjs +79 -36
- package/package.json +1 -1
- package/plugin.json +6 -1
- package/skills/mindlore-ingest/SKILL.md +11 -0
- package/skills/mindlore-stats/SKILL.md +106 -0
- package/templates/config.json +1 -1
- package/templates/extraction/article.md +15 -0
- package/templates/extraction/changelog.md +15 -0
- package/templates/extraction/default.md +15 -0
- package/templates/extraction/docs.md +15 -0
- package/templates/extraction/github-repo.md +17 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stats-skill.test.d.ts","sourceRoot":"","sources":["../../tests/stats-skill.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
const fs_1 = __importDefault(require("fs"));
|
|
7
|
+
const path_1 = __importDefault(require("path"));
|
|
8
|
+
const SKILL_DIR = path_1.default.join(__dirname, '..', 'skills', 'mindlore-stats');
|
|
9
|
+
const SKILL_FILE = path_1.default.join(SKILL_DIR, 'SKILL.md');
|
|
10
|
+
const PLUGIN_JSON = path_1.default.join(__dirname, '..', 'plugin.json');
|
|
11
|
+
describe('/mindlore-stats skill', () => {
|
|
12
|
+
let skillContent;
|
|
13
|
+
let pluginData;
|
|
14
|
+
beforeAll(() => {
|
|
15
|
+
skillContent = fs_1.default.readFileSync(SKILL_FILE, 'utf8');
|
|
16
|
+
pluginData = JSON.parse(fs_1.default.readFileSync(PLUGIN_JSON, 'utf8'));
|
|
17
|
+
});
|
|
18
|
+
test('skill directory exists', () => {
|
|
19
|
+
expect(fs_1.default.existsSync(SKILL_DIR)).toBe(true);
|
|
20
|
+
});
|
|
21
|
+
test('SKILL.md exists and is non-empty', () => {
|
|
22
|
+
expect(skillContent.length).toBeGreaterThan(100);
|
|
23
|
+
});
|
|
24
|
+
test('SKILL.md has valid frontmatter', () => {
|
|
25
|
+
expect(skillContent.startsWith('---')).toBe(true);
|
|
26
|
+
const endIdx = skillContent.indexOf('---', 3);
|
|
27
|
+
expect(endIdx).toBeGreaterThan(3);
|
|
28
|
+
const frontmatter = skillContent.slice(3, endIdx);
|
|
29
|
+
expect(frontmatter).toContain('name: mindlore-stats');
|
|
30
|
+
expect(frontmatter).toContain('description:');
|
|
31
|
+
});
|
|
32
|
+
test('SKILL.md specifies fork context', () => {
|
|
33
|
+
expect(skillContent).toContain('context: fork');
|
|
34
|
+
});
|
|
35
|
+
test('SKILL.md references telemetry.jsonl', () => {
|
|
36
|
+
expect(skillContent).toContain('telemetry.jsonl');
|
|
37
|
+
});
|
|
38
|
+
test('SKILL.md references mindlore.db', () => {
|
|
39
|
+
expect(skillContent).toContain('mindlore.db');
|
|
40
|
+
});
|
|
41
|
+
test('registered in plugin.json', () => {
|
|
42
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion -- pluginData parsed from JSON
|
|
43
|
+
const skills = pluginData.skills;
|
|
44
|
+
const statsSkill = skills.find(s => s.name === 'mindlore-stats' || s.path?.includes('mindlore-stats'));
|
|
45
|
+
expect(statsSkill).toBeDefined();
|
|
46
|
+
});
|
|
47
|
+
test('allowed-tools are restricted to read-only', () => {
|
|
48
|
+
const toolsMatch = skillContent.match(/allowed-tools:\s*\[([^\]]+)\]/);
|
|
49
|
+
expect(toolsMatch).toBeTruthy();
|
|
50
|
+
const tools = toolsMatch[1].split(',').map(t => t.trim());
|
|
51
|
+
expect(tools).not.toContain('Write');
|
|
52
|
+
expect(tools).not.toContain('Edit');
|
|
53
|
+
});
|
|
54
|
+
});
|
|
55
|
+
//# sourceMappingURL=stats-skill.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stats-skill.test.js","sourceRoot":"","sources":["../../tests/stats-skill.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -151,7 +151,8 @@ function main() {
|
|
|
151
151
|
|
|
152
152
|
const headingStr = headings.length > 0 ? `\nBasliklar: ${headings.join(', ')}` : '';
|
|
153
153
|
const tagsStr = r.tags ? `\nTags: ${r.tags}` : '';
|
|
154
|
-
const
|
|
154
|
+
const snippetOrDesc = r.snippet || description;
|
|
155
|
+
const entry = `[Mindlore: ${category}/${title}] ${snippetOrDesc}\nDosya: ${relativePath}${tagsStr}${headingStr}`;
|
|
155
156
|
const truncated = entry.slice(0, perResultChars);
|
|
156
157
|
totalUsed += truncated.length;
|
|
157
158
|
output.push(truncated);
|
|
@@ -12,28 +12,63 @@ const fs = require('fs');
|
|
|
12
12
|
const path = require('path');
|
|
13
13
|
const { findMindloreDir, readConfig, openDatabase, hasEpisodesTable, querySupersededChains, formatSupersededChains, hookLog, getProjectName, parseFrontmatter, withTelemetry, withTimeoutDb, listSnapshots, isCorruptionError, recoverCorruptDb } = require('./lib/mindlore-common.cjs');
|
|
14
14
|
|
|
15
|
+
function truncateSection(content, sectionRegex, keepCount, label) {
|
|
16
|
+
const match = content.match(sectionRegex);
|
|
17
|
+
if (!match) return content;
|
|
18
|
+
const lines = match[2].trim().split('\n');
|
|
19
|
+
if (lines.length <= keepCount) return content;
|
|
20
|
+
const kept = lines.slice(0, keepCount).join('\n');
|
|
21
|
+
return content.replace(match[2].trim(), kept + `\n- ...ve ${lines.length - keepCount} ${label} daha`);
|
|
22
|
+
}
|
|
23
|
+
|
|
15
24
|
function truncateCommits(content) {
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
const truncated = kept + `\n- ...ve ${lines.length - 5} commit daha`;
|
|
22
|
-
return content.replace(commitMatch[2].trim(), truncated);
|
|
25
|
+
return truncateSection(content, /(## Commits\n)((?:- [^\n]+\n?)+)/, 5, 'commit');
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function truncateChangedFiles(content) {
|
|
29
|
+
return truncateSection(content, /(## Changed Files\n)((?:- [^\n]+\n?)+)/, 10, 'dosya');
|
|
23
30
|
}
|
|
24
31
|
|
|
25
32
|
function tryOpenDb(dbPath) {
|
|
26
33
|
return openDatabase(dbPath, { readonly: true });
|
|
27
34
|
}
|
|
28
35
|
|
|
29
|
-
function
|
|
36
|
+
function getEpisodeStats(db, config, project) {
|
|
37
|
+
const chains = querySupersededChains(db, { project, days: 7, limit: 5 });
|
|
38
|
+
let consolidationMsg = null;
|
|
39
|
+
try {
|
|
40
|
+
const rawCount = withTimeoutDb(db,
|
|
41
|
+
"SELECT COUNT(*) as cnt FROM episodes WHERE consolidation_status = 'raw' OR consolidation_status IS NULL",
|
|
42
|
+
[], { mode: 'get' });
|
|
43
|
+
const cnt = rawCount?.cnt ?? 0;
|
|
44
|
+
const consolThreshold = config?.consolidation?.threshold ?? 50;
|
|
45
|
+
if (cnt >= consolThreshold) {
|
|
46
|
+
consolidationMsg = `[Mindlore] ${cnt} raw episode birikti — \`/mindlore-maintain consolidate\` ile birleştirmeyi düşün.`;
|
|
47
|
+
}
|
|
48
|
+
} catch (_err) { /* consolidation_status column may not exist yet */ }
|
|
49
|
+
return { chains, consolidationMsg };
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function checkStaleContent(db) {
|
|
53
|
+
try {
|
|
54
|
+
const thirtyDaysAgo = new Date(Date.now() - (30 * 24 * 60 * 60 * 1000)).toISOString();
|
|
55
|
+
const row = withTimeoutDb(db, 'SELECT COUNT(*) as cnt FROM file_hashes WHERE last_indexed < ?', [thirtyDaysAgo], { mode: 'get' });
|
|
56
|
+
const staleCount = row?.cnt ?? 0;
|
|
57
|
+
if (staleCount > 3) {
|
|
58
|
+
return `[Mindlore: ${staleCount} dosya 30+ gundur guncellenmemis — \`/mindlore-evolve\` dusun]`;
|
|
59
|
+
}
|
|
60
|
+
} catch (_staleErr) { /* file_hashes may not exist */ }
|
|
61
|
+
return null;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function loadDbContent(db, baseDir, config, output, timings, latestDeltaContent, sessionId) {
|
|
65
|
+
const project = path.basename(process.cwd());
|
|
30
66
|
// Session payload: Session summary, Decisions, Friction, Learnings
|
|
31
67
|
const tPayload = Date.now();
|
|
32
68
|
try {
|
|
33
69
|
const { buildSessionPayload } = require('../dist/scripts/lib/session-payload.js');
|
|
34
|
-
const project = path.basename(process.cwd());
|
|
35
70
|
const payloadBudget = config?.tokenBudget?.sessionInject ?? 2000;
|
|
36
|
-
const payload = buildSessionPayload(db, baseDir, project, payloadBudget, latestDeltaContent);
|
|
71
|
+
const payload = buildSessionPayload(db, baseDir, project, payloadBudget, latestDeltaContent, sessionId);
|
|
37
72
|
for (const section of payload.sections) {
|
|
38
73
|
output.push(`[Mindlore ${section.label}]\n${section.content}`);
|
|
39
74
|
}
|
|
@@ -42,40 +77,25 @@ function loadDbContent(db, baseDir, config, output, timings, latestDeltaContent)
|
|
|
42
77
|
}
|
|
43
78
|
timings.db_payload = Date.now() - tPayload;
|
|
44
79
|
|
|
45
|
-
// Supersedes chain display
|
|
80
|
+
// Supersedes chain display + episode consolidation reminder
|
|
46
81
|
const tSuperseded = Date.now();
|
|
47
82
|
if (hasEpisodesTable(db)) {
|
|
48
|
-
const
|
|
49
|
-
|
|
50
|
-
const chains = querySupersededChains(db, { project, days: 7, limit: 5 });
|
|
83
|
+
const { chains, consolidationMsg } = getEpisodeStats(db, config, project);
|
|
51
84
|
if (chains.length > 0) {
|
|
52
85
|
output.push(`[Mindlore Supersedes]\n${formatSupersededChains(chains)}`);
|
|
53
86
|
}
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
const rawCount = withTimeoutDb(db,
|
|
58
|
-
"SELECT COUNT(*) as cnt FROM episodes WHERE consolidation_status = 'raw' OR consolidation_status IS NULL",
|
|
59
|
-
[], { mode: 'get' });
|
|
60
|
-
const cnt = rawCount?.cnt ?? 0;
|
|
61
|
-
const consolThreshold = config?.consolidation?.threshold ?? 50;
|
|
62
|
-
if (cnt >= consolThreshold) {
|
|
63
|
-
output.push(`[Mindlore] ${cnt} raw episode birikti — \`/mindlore-maintain consolidate\` ile birleştirmeyi düşün.`);
|
|
64
|
-
}
|
|
65
|
-
} catch (_err) { /* consolidation_status column may not exist yet */ }
|
|
87
|
+
if (consolidationMsg) {
|
|
88
|
+
output.push(consolidationMsg);
|
|
89
|
+
}
|
|
66
90
|
}
|
|
67
91
|
timings.db_episodes = Date.now() - tSuperseded;
|
|
68
92
|
|
|
69
93
|
// Stale content check
|
|
70
94
|
const tStale = Date.now();
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
if (staleCount > 3) {
|
|
76
|
-
output.push(`[Mindlore: ${staleCount} dosya 30+ gundur guncellenmemis — \`/mindlore-evolve\` dusun]`);
|
|
77
|
-
}
|
|
78
|
-
} catch (_staleErr) { /* file_hashes may not exist */ }
|
|
95
|
+
const staleMsg = checkStaleContent(db);
|
|
96
|
+
if (staleMsg) {
|
|
97
|
+
output.push(staleMsg);
|
|
98
|
+
}
|
|
79
99
|
timings.db_stale = Date.now() - tStale;
|
|
80
100
|
}
|
|
81
101
|
|
|
@@ -84,6 +104,13 @@ function main() {
|
|
|
84
104
|
const baseDir = findMindloreDir();
|
|
85
105
|
if (!baseDir) return; // No .mindlore/ found, silently skip
|
|
86
106
|
|
|
107
|
+
// Read session_id from stdin (Claude Code passes { session_id } to SessionStart hooks)
|
|
108
|
+
let sessionId;
|
|
109
|
+
try {
|
|
110
|
+
const stdinData = JSON.parse(fs.readFileSync(0, 'utf8') || '{}');
|
|
111
|
+
sessionId = stdinData.session_id || undefined;
|
|
112
|
+
} catch { sessionId = undefined; }
|
|
113
|
+
|
|
87
114
|
const output = [];
|
|
88
115
|
const config = readConfig(baseDir);
|
|
89
116
|
const timings = {};
|
|
@@ -117,7 +144,7 @@ function main() {
|
|
|
117
144
|
const deltaProject = meta.project || null;
|
|
118
145
|
const currentProject = getProjectName();
|
|
119
146
|
if (!deltaProject || deltaProject.toLowerCase() === currentProject.toLowerCase()) {
|
|
120
|
-
output.push(`[Mindlore Delta: ${latestName}]\n${truncateCommits(deltaContent)}`);
|
|
147
|
+
output.push(`[Mindlore Delta: ${latestName}]\n${truncateChangedFiles(truncateCommits(deltaContent))}`);
|
|
121
148
|
}
|
|
122
149
|
}
|
|
123
150
|
|
|
@@ -158,7 +185,19 @@ function main() {
|
|
|
158
185
|
|
|
159
186
|
if (db) {
|
|
160
187
|
try {
|
|
161
|
-
|
|
188
|
+
// Schema version check: warn if DB is behind expected version
|
|
189
|
+
const tSchema = Date.now();
|
|
190
|
+
try {
|
|
191
|
+
const { EXPECTED_SCHEMA_VERSION } = require('../dist/scripts/lib/all-migrations.js');
|
|
192
|
+
const row = db.prepare('SELECT MAX(version) as v FROM schema_versions').get();
|
|
193
|
+
const current = row?.v ?? 0;
|
|
194
|
+
if (current < EXPECTED_SCHEMA_VERSION) {
|
|
195
|
+
output.push(`[Mindlore: schema güncel değil (v${current} → v${EXPECTED_SCHEMA_VERSION}). \`npx mindlore upgrade\` çalıştır.]`);
|
|
196
|
+
}
|
|
197
|
+
} catch (_schemaErr) { /* schema_versions may not exist yet */ }
|
|
198
|
+
timings.schema_check = Date.now() - tSchema;
|
|
199
|
+
|
|
200
|
+
loadDbContent(db, baseDir, config, output, timings, latestDeltaContent, sessionId);
|
|
162
201
|
} catch (err) {
|
|
163
202
|
if (isCorruptionError(err)) {
|
|
164
203
|
recoverCorruptDb(db, dbPath, 'session-focus');
|
|
@@ -200,3 +239,7 @@ withTelemetry('mindlore-session-focus', main).catch(err => {
|
|
|
200
239
|
hookLog('mindlore-session-focus', 'error', err?.message ?? String(err));
|
|
201
240
|
process.exit(0);
|
|
202
241
|
});
|
|
242
|
+
|
|
243
|
+
if (typeof module !== 'undefined') {
|
|
244
|
+
module.exports = { truncateCommits, truncateChangedFiles, getEpisodeStats, checkStaleContent };
|
|
245
|
+
}
|
package/package.json
CHANGED
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.6.
|
|
4
|
+
"version": "0.6.6",
|
|
5
5
|
"skills": [
|
|
6
6
|
{
|
|
7
7
|
"name": "mindlore-ingest",
|
|
@@ -52,6 +52,11 @@
|
|
|
52
52
|
"name": "mindlore-maintain",
|
|
53
53
|
"path": "skills/mindlore-maintain/SKILL.md",
|
|
54
54
|
"description": "KB maintenance — decay/archive stale documents, consolidate 50+ episodes into learnings/insights, detect contradictions. Modes: decay (stale doc archive with git snapshot), consolidate (episode→file promotion), contradictions (wiki-lint + semantic), restore (undo archive). Run without flags for full report."
|
|
55
|
+
},
|
|
56
|
+
{
|
|
57
|
+
"name": "mindlore-stats",
|
|
58
|
+
"path": "skills/mindlore-stats/SKILL.md",
|
|
59
|
+
"description": "Show context contribution and cost per session — hook calls, durations, DB stats."
|
|
55
60
|
}
|
|
56
61
|
],
|
|
57
62
|
"agents": [
|
|
@@ -135,6 +135,17 @@ Required frontmatter fields include `source_type` — auto-detected:
|
|
|
135
135
|
- `pdf` for PDF Mode
|
|
136
136
|
- `file` for File Mode
|
|
137
137
|
|
|
138
|
+
### Extraction Template
|
|
139
|
+
|
|
140
|
+
After detecting `source_type`, check if a matching extraction template exists:
|
|
141
|
+
|
|
142
|
+
1. Read `~/.mindlore/templates/extraction/{source_type}.md`
|
|
143
|
+
2. If found, follow the "Extraction Instructions" section for source analysis
|
|
144
|
+
3. If not found, Read `~/.mindlore/templates/extraction/default.md` and follow that
|
|
145
|
+
4. User can override with `--type <type>` argument (e.g., `/mindlore-ingest https://example.com --type changelog`)
|
|
146
|
+
|
|
147
|
+
The template guides WHAT to extract from the source. Apply its instructions when writing the source summary in `sources/{slug}.md`.
|
|
148
|
+
|
|
138
149
|
## Quality Assessment
|
|
139
150
|
|
|
140
151
|
Assign quality automatically during ingest using this heuristic:
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: mindlore-stats
|
|
3
|
+
description: Show context contribution and cost per session — hook calls, durations, DB stats.
|
|
4
|
+
effort: low
|
|
5
|
+
context: fork
|
|
6
|
+
allowed-tools: [Bash, Read]
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Script Resolution
|
|
10
|
+
|
|
11
|
+
Resolve `MINDLORE_DIR` (active knowledge base directory) using:
|
|
12
|
+
1. If project `.mindlore/` exists → use it
|
|
13
|
+
2. Else → use `~/.mindlore/`
|
|
14
|
+
|
|
15
|
+
# /mindlore-stats
|
|
16
|
+
|
|
17
|
+
Show context contribution and cost per session — hook calls, durations, DB stats.
|
|
18
|
+
|
|
19
|
+
## Trigger
|
|
20
|
+
|
|
21
|
+
User says "mindlore stats", "stats", "context cost", "hook performance", "ne kadar yer kaplıyor".
|
|
22
|
+
|
|
23
|
+
## Execution
|
|
24
|
+
|
|
25
|
+
### 1. Telemetry Analysis
|
|
26
|
+
|
|
27
|
+
Read `$MINDLORE_DIR/telemetry.jsonl` (each line is a JSON object):
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
# Count lines and read last 500 for session estimate
|
|
31
|
+
wc -l "$MINDLORE_DIR/telemetry.jsonl"
|
|
32
|
+
tail -500 "$MINDLORE_DIR/telemetry.jsonl"
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
Parse each line as JSON with fields: `hook` (string), `duration` (number, ms), `ts` (ISO timestamp), optionally `injectSize` (number, chars).
|
|
36
|
+
|
|
37
|
+
Calculate:
|
|
38
|
+
- **Total hook calls (all time)**: total line count
|
|
39
|
+
- **Hook calls (session)**: lines where `ts` is within last 8 hours
|
|
40
|
+
- **Avg duration per hook**: group by `hook`, compute mean `duration`
|
|
41
|
+
- **Session inject avg**: from events where `hook === "mindlore-session-focus"`, average `injectSize` (chars ÷ 4 ≈ tokens)
|
|
42
|
+
- **Search inject avg**: from events where `hook === "mindlore-search"`, average `injectSize` (chars ÷ 4 ≈ tokens)
|
|
43
|
+
|
|
44
|
+
### 2. DB Stats
|
|
45
|
+
|
|
46
|
+
Query `$MINDLORE_DIR/mindlore.db`:
|
|
47
|
+
|
|
48
|
+
```bash
|
|
49
|
+
# Indexed docs
|
|
50
|
+
node -e "
|
|
51
|
+
const DB = require('better-sqlite3');
|
|
52
|
+
const db = new DB('$MINDLORE_DIR/mindlore.db', {readonly:true});
|
|
53
|
+
const total = db.prepare('SELECT COUNT(*) as n FROM file_hashes').get().n;
|
|
54
|
+
const stale = db.prepare(\"SELECT COUNT(*) as n FROM file_hashes WHERE last_indexed < datetime('now','-30 days')\").get().n;
|
|
55
|
+
const epRows = db.prepare('SELECT kind, COUNT(*) as n FROM episodes GROUP BY kind').all();
|
|
56
|
+
const sessions = db.prepare('SELECT COUNT(*) as n FROM mindlore_fts_sessions').get()?.n ?? 'N/A';
|
|
57
|
+
console.log(JSON.stringify({total, stale, epRows, sessions}));
|
|
58
|
+
db.close();
|
|
59
|
+
"
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
Get DB file size:
|
|
63
|
+
|
|
64
|
+
```bash
|
|
65
|
+
node -e "const fs=require('fs'); const s=fs.statSync('$MINDLORE_DIR/mindlore.db'); console.log((s.size/1024/1024).toFixed(2)+' MB');"
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
### 3. Format Output
|
|
69
|
+
|
|
70
|
+
Print a compact stats table:
|
|
71
|
+
|
|
72
|
+
```
|
|
73
|
+
## Mindlore Stats
|
|
74
|
+
|
|
75
|
+
| Metric | Value |
|
|
76
|
+
|--------|-------|
|
|
77
|
+
| DB Size | X MB |
|
|
78
|
+
| Indexed Docs | N |
|
|
79
|
+
| Stale Docs (30d+) | N |
|
|
80
|
+
| Episodes (active) | N |
|
|
81
|
+
| Hook Calls (session) | N |
|
|
82
|
+
| Hook Calls (all time) | N |
|
|
83
|
+
| Avg Hook Duration | Xms |
|
|
84
|
+
| Session Inject Avg | ~N tokens |
|
|
85
|
+
| Search Inject Avg | ~N tokens |
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
Then add a per-hook breakdown if hook calls > 0:
|
|
89
|
+
|
|
90
|
+
```
|
|
91
|
+
### Hook Breakdown (session)
|
|
92
|
+
|
|
93
|
+
| Hook | Calls | Avg Duration |
|
|
94
|
+
|------|-------|-------------|
|
|
95
|
+
| mindlore-session-focus | N | Xms |
|
|
96
|
+
| mindlore-search | N | Xms |
|
|
97
|
+
| ... | | |
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
## Notes
|
|
101
|
+
|
|
102
|
+
- If `telemetry.jsonl` does not exist: report "No telemetry data yet"
|
|
103
|
+
- If `mindlore.db` does not exist: report "DB not found — run `npx mindlore init`"
|
|
104
|
+
- If `episodes` table missing: skip episode count, show "—"
|
|
105
|
+
- Inject size in tokens is approximate (chars ÷ 4)
|
|
106
|
+
- "Session" = last 8 hours from current time
|
package/templates/config.json
CHANGED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
---
|
|
2
|
+
source_type: article
|
|
3
|
+
description: Article/blog post analysis template
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
## Extraction Instructions
|
|
7
|
+
|
|
8
|
+
Bu source bir makale veya blog yazisidir. Su bilgileri cikar:
|
|
9
|
+
|
|
10
|
+
1. **Tez:** Yazarin ana argumani veya iddiasi (1-2 cumle)
|
|
11
|
+
2. **Destekleyici Argumanlar:** Tezi destekleyen kanit ve ornekler
|
|
12
|
+
3. **Sonuc:** Yazarin vardigi sonuc veya onerisi
|
|
13
|
+
4. **Elestiri:** Zayif noktalar veya eksik perspektifler (varsa)
|
|
14
|
+
|
|
15
|
+
Yazarin uslubunu veya hikaye anlatimini tekrarlama — sadece bilgi ozu.
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
---
|
|
2
|
+
source_type: changelog
|
|
3
|
+
description: Changelog/release notes analysis template
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
## Extraction Instructions
|
|
7
|
+
|
|
8
|
+
Bu source bir changelog veya release notes dosyasidir. Su bilgileri cikar:
|
|
9
|
+
|
|
10
|
+
1. **Breaking Changes:** Geriye uyumsuz degisiklikler ve migration adimlari
|
|
11
|
+
2. **Yeni Ozellikler:** Eklenen ozellikler ve kullanim sekli
|
|
12
|
+
3. **Bug Fix'ler:** Duzeltilen hatalar (sadece onemli olanlar)
|
|
13
|
+
4. **Deprecation:** Kaldirilacak/kaldirilan ozellikler
|
|
14
|
+
|
|
15
|
+
Her maddeyi versiyon numarasiyla iliskilendir. Minor fix'leri atla, sadece etkili degisiklikleri al.
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
---
|
|
2
|
+
source_type: default
|
|
3
|
+
description: Generic extraction template (fallback)
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
## Extraction Instructions
|
|
7
|
+
|
|
8
|
+
Bu source icin ozel bir extraction template bulunamadi. Genel ozet cikar:
|
|
9
|
+
|
|
10
|
+
1. **Ozet:** Kaynagin ne hakkinda oldugu (2-3 cumle)
|
|
11
|
+
2. **Temel Bilgiler:** En onemli noktalar ve kavramlar
|
|
12
|
+
3. **Iliskiler:** Diger bilinen konularla baglantilari
|
|
13
|
+
4. **Eyleme Donuk:** Pratikte nasil kullanilabilir
|
|
14
|
+
|
|
15
|
+
Kisa ve oz tut — max 500 kelime.
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
---
|
|
2
|
+
source_type: docs
|
|
3
|
+
description: Technical documentation analysis template
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
## Extraction Instructions
|
|
7
|
+
|
|
8
|
+
Bu source teknik dokumantasyondur. Su bilgileri cikar:
|
|
9
|
+
|
|
10
|
+
1. **API Referans:** Fonksiyonlar, parametreler, donus tipleri
|
|
11
|
+
2. **Ornekler:** Kod ornekleri ve kullanim pattern'leri
|
|
12
|
+
3. **Kisitlamalar:** Bilinen limitasyonlar, uyumsuzluklar, uyarilar
|
|
13
|
+
4. **Versiyon:** Hangi versiyona ait oldugu (belirtilmisse)
|
|
14
|
+
|
|
15
|
+
Tam API imzalarini koru — kisaltma yapma. Ornekleri oldugu gibi al.
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
---
|
|
2
|
+
source_type: github-repo
|
|
3
|
+
description: GitHub repository analysis template
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
## Extraction Instructions
|
|
7
|
+
|
|
8
|
+
Bu source bir GitHub reposudur. Su bilgileri cikar:
|
|
9
|
+
|
|
10
|
+
1. **Mimari:** Temel tasarim kararlari, kullanilan pattern'ler, katman yapisi
|
|
11
|
+
2. **Kurulum:** Gereksinimler, adimlar, environment degiskenleri
|
|
12
|
+
3. **API Surface:** Public fonksiyonlar, CLI komutlari, tool'lar, hook'lar
|
|
13
|
+
4. **Dependencies:** Kritik bagimliliklar ve versiyonlari
|
|
14
|
+
5. **Farklilastirici:** Rakiplerden/alternatiflerden ne farki var
|
|
15
|
+
|
|
16
|
+
Gereksiz README boilerplate'i (badge'ler, sponsor listesi, lisans metni) atla.
|
|
17
|
+
Sadece teknik ozeti yaz.
|