mindlore 0.3.5 → 0.4.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 +9 -6
- package/dist/scripts/init.js +9 -1
- package/dist/scripts/init.js.map +1 -1
- package/dist/scripts/lib/episodes.d.ts +66 -0
- package/dist/scripts/lib/episodes.d.ts.map +1 -0
- package/dist/scripts/lib/episodes.js +180 -0
- package/dist/scripts/lib/episodes.js.map +1 -0
- package/dist/scripts/mindlore-episodes.d.ts +12 -0
- package/dist/scripts/mindlore-episodes.d.ts.map +1 -0
- package/dist/scripts/mindlore-episodes.js +193 -0
- package/dist/scripts/mindlore-episodes.js.map +1 -0
- package/dist/tests/diary.test.d.ts +6 -0
- package/dist/tests/diary.test.d.ts.map +1 -0
- package/dist/tests/diary.test.js +169 -0
- package/dist/tests/diary.test.js.map +1 -0
- package/dist/tests/episodes-inject.test.d.ts +6 -0
- package/dist/tests/episodes-inject.test.d.ts.map +1 -0
- package/dist/tests/episodes-inject.test.js +161 -0
- package/dist/tests/episodes-inject.test.js.map +1 -0
- package/dist/tests/episodes.test.d.ts +5 -0
- package/dist/tests/episodes.test.d.ts.map +1 -0
- package/dist/tests/episodes.test.js +254 -0
- package/dist/tests/episodes.test.js.map +1 -0
- package/dist/tests/helpers/db.d.ts +7 -0
- package/dist/tests/helpers/db.d.ts.map +1 -1
- package/dist/tests/helpers/db.js +20 -1
- package/dist/tests/helpers/db.js.map +1 -1
- package/dist/tests/nomination.test.d.ts +2 -0
- package/dist/tests/nomination.test.d.ts.map +1 -0
- package/dist/tests/nomination.test.js +94 -0
- package/dist/tests/nomination.test.js.map +1 -0
- package/dist/tests/session-focus.test.js +82 -0
- package/dist/tests/session-focus.test.js.map +1 -1
- package/dist/tests/supersedes-chain.test.d.ts +2 -0
- package/dist/tests/supersedes-chain.test.d.ts.map +1 -0
- package/dist/tests/supersedes-chain.test.js +109 -0
- package/dist/tests/supersedes-chain.test.js.map +1 -0
- package/hooks/lib/mindlore-common.cjs +260 -1
- package/hooks/mindlore-index.cjs +17 -14
- package/hooks/mindlore-post-read.cjs +11 -2
- package/hooks/mindlore-pre-compact.cjs +2 -2
- package/hooks/mindlore-read-guard.cjs +12 -4
- package/hooks/mindlore-search.cjs +39 -1
- package/hooks/mindlore-session-end.cjs +105 -22
- package/hooks/mindlore-session-focus.cjs +56 -2
- package/package.json +1 -1
- package/plugin.json +8 -8
- package/skills/mindlore-evolve/SKILL.md +8 -4
- package/skills/mindlore-explore/SKILL.md +8 -4
- package/skills/mindlore-log/SKILL.md +134 -17
- package/skills/mindlore-query/SKILL.md +8 -5
- package/templates/config.json +5 -1
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
const fs = require('fs');
|
|
13
13
|
const path = require('path');
|
|
14
14
|
const { execSync } = require('child_process');
|
|
15
|
-
const { findMindloreDir, globalDir, getProjectName } = require('./lib/mindlore-common.cjs');
|
|
15
|
+
const { findMindloreDir, globalDir, getProjectName, openDatabase, ensureEpisodesTable, hasEpisodesTable, insertBareEpisode, insertFtsRow } = require('./lib/mindlore-common.cjs');
|
|
16
16
|
|
|
17
17
|
function formatDate(date) {
|
|
18
18
|
const y = date.getFullYear();
|
|
@@ -23,29 +23,33 @@ function formatDate(date) {
|
|
|
23
23
|
return `${y}-${m}-${d}-${h}${min}`;
|
|
24
24
|
}
|
|
25
25
|
|
|
26
|
-
|
|
26
|
+
/**
|
|
27
|
+
* Get recent commits and changed files in a single git call.
|
|
28
|
+
* Returns { commits: string[], changedFiles: string[] }
|
|
29
|
+
*/
|
|
30
|
+
function getRecentGitInfo() {
|
|
27
31
|
try {
|
|
28
|
-
|
|
32
|
+
// --name-only includes file names after each commit entry
|
|
33
|
+
const raw = execSync('git log --oneline -5 --name-only 2>/dev/null', {
|
|
29
34
|
encoding: 'utf8',
|
|
30
35
|
timeout: 5000,
|
|
31
36
|
}).trim();
|
|
32
|
-
if (!raw) return [];
|
|
33
|
-
return raw.split('\n').filter(Boolean).slice(0, 20);
|
|
34
|
-
} catch (_err) {
|
|
35
|
-
return [];
|
|
36
|
-
}
|
|
37
|
-
}
|
|
37
|
+
if (!raw) return { commits: [], changedFiles: [] };
|
|
38
38
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
const
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
39
|
+
const commits = [];
|
|
40
|
+
const fileSet = new Set();
|
|
41
|
+
for (const line of raw.split('\n')) {
|
|
42
|
+
if (!line) continue;
|
|
43
|
+
// Commit lines start with a short hash (7+ hex chars)
|
|
44
|
+
if (/^[0-9a-f]{7,}\s/.test(line)) {
|
|
45
|
+
commits.push(line);
|
|
46
|
+
} else {
|
|
47
|
+
fileSet.add(line);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
return { commits, changedFiles: [...fileSet].slice(0, 20) };
|
|
47
51
|
} catch (_err) {
|
|
48
|
-
return [];
|
|
52
|
+
return { commits: [], changedFiles: [] };
|
|
49
53
|
}
|
|
50
54
|
}
|
|
51
55
|
|
|
@@ -55,7 +59,11 @@ function getSessionReads(baseDir) {
|
|
|
55
59
|
try {
|
|
56
60
|
const data = JSON.parse(fs.readFileSync(readsPath, 'utf8'));
|
|
57
61
|
const count = Object.keys(data).length;
|
|
58
|
-
const repeats = Object.values(data).filter((v) =>
|
|
62
|
+
const repeats = Object.values(data).filter((v) => {
|
|
63
|
+
if (typeof v === 'number') return v > 1;
|
|
64
|
+
if (v && typeof v === 'object') return (v.count || 0) > 1;
|
|
65
|
+
return false;
|
|
66
|
+
}).length;
|
|
59
67
|
// Clean up session file
|
|
60
68
|
fs.unlinkSync(readsPath);
|
|
61
69
|
return { count, repeats };
|
|
@@ -80,9 +88,8 @@ function main() {
|
|
|
80
88
|
// Don't overwrite existing delta (idempotent)
|
|
81
89
|
if (fs.existsSync(deltaPath)) return;
|
|
82
90
|
|
|
83
|
-
// Gather structured data
|
|
84
|
-
const commits =
|
|
85
|
-
const changedFiles = getRecentGitChanges();
|
|
91
|
+
// Gather structured data (single git call)
|
|
92
|
+
const { commits, changedFiles } = getRecentGitInfo();
|
|
86
93
|
const reads = getSessionReads(baseDir);
|
|
87
94
|
|
|
88
95
|
const project = getProjectName();
|
|
@@ -133,10 +140,86 @@ function main() {
|
|
|
133
140
|
fs.appendFileSync(logPath, logEntry, 'utf8');
|
|
134
141
|
}
|
|
135
142
|
|
|
143
|
+
// v0.4.0: Write bare episode to episodes table
|
|
144
|
+
writeBareEpisode(baseDir, project, commits, changedFiles, reads);
|
|
145
|
+
|
|
136
146
|
// Git auto-commit + push for global ~/.mindlore/ only
|
|
137
147
|
syncGlobalRepo();
|
|
138
148
|
}
|
|
139
149
|
|
|
150
|
+
/**
|
|
151
|
+
* Write a bare session episode to the episodes table.
|
|
152
|
+
* Deterministic — no LLM needed. Captures commits, files, read stats.
|
|
153
|
+
*/
|
|
154
|
+
function writeBareEpisode(baseDir, project, commits, changedFiles, reads) {
|
|
155
|
+
try {
|
|
156
|
+
const dbPath = path.join(baseDir, 'mindlore.db');
|
|
157
|
+
const db = openDatabase(dbPath);
|
|
158
|
+
if (!db) return;
|
|
159
|
+
|
|
160
|
+
if (!hasEpisodesTable(db)) {
|
|
161
|
+
ensureEpisodesTable(db);
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
const commitList = commits.length > 0 ? commits.join(', ') : 'no commits';
|
|
165
|
+
const fileCount = changedFiles.length;
|
|
166
|
+
const summary = `Session: ${commitList} (${fileCount} files)`;
|
|
167
|
+
|
|
168
|
+
const bodyParts = [];
|
|
169
|
+
if (commits.length > 0) {
|
|
170
|
+
bodyParts.push('## Commits\n' + commits.map(c => `- ${c}`).join('\n'));
|
|
171
|
+
}
|
|
172
|
+
if (changedFiles.length > 0) {
|
|
173
|
+
bodyParts.push('## Changed Files\n' + changedFiles.map(f => `- ${f}`).join('\n'));
|
|
174
|
+
}
|
|
175
|
+
if (reads) {
|
|
176
|
+
bodyParts.push(`## Read Stats\n- ${reads.count} files read, ${reads.repeats} repeated`);
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
const entities = changedFiles.slice(0, 10);
|
|
180
|
+
const body = bodyParts.join('\n\n') || null;
|
|
181
|
+
const truncatedSummary = summary.slice(0, 300);
|
|
182
|
+
|
|
183
|
+
// Atomic: episode + FTS5 mirror in single transaction
|
|
184
|
+
const writeBoth = db.transaction(() => {
|
|
185
|
+
const epId = insertBareEpisode(db, {
|
|
186
|
+
kind: 'session',
|
|
187
|
+
scope: 'project',
|
|
188
|
+
project: project,
|
|
189
|
+
summary: truncatedSummary,
|
|
190
|
+
body: body,
|
|
191
|
+
tags: 'session',
|
|
192
|
+
entities: entities.length > 0 ? entities : null,
|
|
193
|
+
source: 'hook',
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
// FTS5 mirror — episode searchable via mindlore-search hook
|
|
197
|
+
try {
|
|
198
|
+
insertFtsRow(db, {
|
|
199
|
+
path: `episodes/${epId}`,
|
|
200
|
+
slug: `ep-${epId}`,
|
|
201
|
+
description: truncatedSummary,
|
|
202
|
+
type: 'episode',
|
|
203
|
+
category: 'episodes',
|
|
204
|
+
title: truncatedSummary.slice(0, 100),
|
|
205
|
+
content: [truncatedSummary, body ?? ''].join('\n').trim(),
|
|
206
|
+
tags: 'session',
|
|
207
|
+
quality: null,
|
|
208
|
+
dateCaptured: new Date().toISOString().slice(0, 10),
|
|
209
|
+
project: project,
|
|
210
|
+
});
|
|
211
|
+
} catch (_ftsErr) {
|
|
212
|
+
// FTS5 mirror optional — don't break the transaction
|
|
213
|
+
}
|
|
214
|
+
});
|
|
215
|
+
writeBoth();
|
|
216
|
+
|
|
217
|
+
db.close();
|
|
218
|
+
} catch (_err) {
|
|
219
|
+
// Graceful fail — never break session end
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
|
|
140
223
|
/**
|
|
141
224
|
* Auto-commit and push ~/.mindlore/ if it has a .git directory.
|
|
142
225
|
* Only runs for the global scope — project .mindlore/ is in the project's own git.
|
|
@@ -10,13 +10,14 @@
|
|
|
10
10
|
|
|
11
11
|
const fs = require('fs');
|
|
12
12
|
const path = require('path');
|
|
13
|
-
const { findMindloreDir, readConfig } = require('./lib/mindlore-common.cjs');
|
|
13
|
+
const { findMindloreDir, readConfig, openDatabase, hasEpisodesTable, queryRecentEpisodes, querySupersededChains, formatSupersededChains, queryMultiSessionEpisodes, formatMultiSessionEpisodes, getAllMdFiles } = require('./lib/mindlore-common.cjs');
|
|
14
14
|
|
|
15
15
|
function main() {
|
|
16
16
|
const baseDir = findMindloreDir();
|
|
17
17
|
if (!baseDir) return; // No .mindlore/ found, silently skip
|
|
18
18
|
|
|
19
19
|
const output = [];
|
|
20
|
+
const config = readConfig(baseDir);
|
|
20
21
|
|
|
21
22
|
// Inject INDEX.md
|
|
22
23
|
const indexPath = path.join(baseDir, 'INDEX.md');
|
|
@@ -41,7 +42,6 @@ function main() {
|
|
|
41
42
|
}
|
|
42
43
|
|
|
43
44
|
// Reflect trigger
|
|
44
|
-
const config = readConfig(baseDir);
|
|
45
45
|
const threshold = config?.reflect?.threshold ?? 5;
|
|
46
46
|
if (diaryFiles.length >= threshold) {
|
|
47
47
|
output.push(`[Mindlore] ${diaryFiles.length} diary entry birikti — \`/mindlore-log reflect\` calistirmayi dusun.`);
|
|
@@ -63,6 +63,60 @@ function main() {
|
|
|
63
63
|
}
|
|
64
64
|
} catch (_err) { /* skip */ }
|
|
65
65
|
|
|
66
|
+
// v0.4.0: Inject recent episodes
|
|
67
|
+
try {
|
|
68
|
+
const dbPath = path.join(baseDir, 'mindlore.db');
|
|
69
|
+
const db = openDatabase(dbPath, { readonly: true });
|
|
70
|
+
if (db) {
|
|
71
|
+
try {
|
|
72
|
+
if (hasEpisodesTable(db)) {
|
|
73
|
+
const maxEpisodes = config?.session_focus?.max_episodes ?? 3;
|
|
74
|
+
const project = path.basename(process.cwd());
|
|
75
|
+
const episodes = queryRecentEpisodes(db, { project, limit: maxEpisodes });
|
|
76
|
+
|
|
77
|
+
if (episodes.length > 0) {
|
|
78
|
+
const lines = episodes.map(ep => {
|
|
79
|
+
const date = (ep.created_at || '').slice(0, 10);
|
|
80
|
+
const summary = String(ep.summary || '').slice(0, 100);
|
|
81
|
+
return `- [${date}] ${ep.kind}: ${summary}`;
|
|
82
|
+
});
|
|
83
|
+
output.push(`[Mindlore Episodes]\n${lines.join('\n')}`);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// v0.4.1: Enriched multi-session episodes
|
|
87
|
+
const multiDays = config?.session_focus?.multi_session_days ?? 3;
|
|
88
|
+
const enriched = queryMultiSessionEpisodes(db, { project, days: multiDays, limit: 20 });
|
|
89
|
+
if (enriched.length > 0) {
|
|
90
|
+
const formatted = formatMultiSessionEpisodes(enriched);
|
|
91
|
+
if (formatted) {
|
|
92
|
+
output.push(`[Mindlore Recent Activity]\n${formatted}`);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// v0.4.1: Supersedes chain display
|
|
97
|
+
const chains = querySupersededChains(db, { project, days: 7, limit: 5 });
|
|
98
|
+
if (chains.length > 0) {
|
|
99
|
+
output.push(`[Mindlore Supersedes]\n${formatSupersededChains(chains)}`);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
} finally {
|
|
103
|
+
db.close();
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
} catch (_err) { /* graceful skip */ }
|
|
107
|
+
|
|
108
|
+
// v0.4.1: Lightweight stale content check (monitors fallback)
|
|
109
|
+
try {
|
|
110
|
+
const allFiles = getAllMdFiles(baseDir);
|
|
111
|
+
const thirtyDaysAgo = Date.now() - (30 * 24 * 60 * 60 * 1000);
|
|
112
|
+
const staleCount = allFiles.reduce((count, f) => {
|
|
113
|
+
try { return fs.statSync(f).mtimeMs < thirtyDaysAgo ? count + 1 : count; } catch { return count; }
|
|
114
|
+
}, 0);
|
|
115
|
+
if (staleCount > 3) {
|
|
116
|
+
output.push(`[Mindlore: ${staleCount} dosya 30+ gundur guncellenmemis — \`/mindlore-evolve\` dusun]`);
|
|
117
|
+
}
|
|
118
|
+
} catch (_healthErr) { /* skip */ }
|
|
119
|
+
|
|
66
120
|
if (output.length > 0) {
|
|
67
121
|
process.stdout.write(output.join('\n\n') + '\n');
|
|
68
122
|
}
|
package/package.json
CHANGED
package/plugin.json
CHANGED
|
@@ -1,42 +1,42 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "mindlore",
|
|
3
3
|
"description": "AI-native knowledge system for Claude Code. Persistent, searchable, evolving knowledge base with FTS5.",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.4.1",
|
|
5
5
|
"skills": [
|
|
6
6
|
{
|
|
7
7
|
"name": "mindlore-ingest",
|
|
8
8
|
"path": "skills/mindlore-ingest/SKILL.md",
|
|
9
|
-
"description": "Add new knowledge sources (
|
|
9
|
+
"description": "Add new knowledge sources to .mindlore/ knowledge base. Supports: URL (web page to markdown), text (inline paste), file (local .md/.txt/.pdf), PDF (markitdown extraction), GitHub repo (README + structure analysis). Includes 6-point quality gate: frontmatter validation, slug uniqueness, content length, source_type tagging, quality heuristic assignment, and FTS5 indexing. Auto-generates frontmatter with type, slug, date, tags, and quality fields."
|
|
10
10
|
},
|
|
11
11
|
{
|
|
12
12
|
"name": "mindlore-health",
|
|
13
13
|
"path": "skills/mindlore-health/SKILL.md",
|
|
14
|
-
"description": "Run 16-point structural health check on .mindlore/ knowledge base"
|
|
14
|
+
"description": "Run 16-point structural health check on .mindlore/ knowledge base. 12 deterministic script checks: INDEX.md sync, FTS5 row count, frontmatter validation, orphan file detection, slug uniqueness, directory structure, log.md integrity, config.json schema, episodes table health, content-hash dedup status, cross-reference consistency, file naming conventions. 4 LLM-powered checks: domain coverage gaps, stale content detection, tag taxonomy coherence, knowledge graph connectivity."
|
|
15
15
|
},
|
|
16
16
|
{
|
|
17
17
|
"name": "mindlore-query",
|
|
18
18
|
"path": "skills/mindlore-query/SKILL.md",
|
|
19
|
-
"description": "Search, ask, stats, brief — compounding knowledge pipeline"
|
|
19
|
+
"description": "Search, ask, stats, brief — compounding knowledge pipeline. Modes: search (FTS5 full-text + episodes recall), ask (LLM-powered Q&A grounded in knowledge base), stats (episode counts, domain coverage, freshness metrics), brief (executive summary of recent activity). Combines FTS5 text search with episodic memory for richer recall. Supports project-scoped and global queries."
|
|
20
20
|
},
|
|
21
21
|
{
|
|
22
22
|
"name": "mindlore-log",
|
|
23
23
|
"path": "skills/mindlore-log/SKILL.md",
|
|
24
|
-
"description": "Session logging, pattern extraction, wiki updates"
|
|
24
|
+
"description": "Session logging, pattern extraction, and wiki updates. Modes: diary (LLM session analysis to enriched episodes: decisions, discoveries, frictions, learnings, preferences, events), reflect (scan episodes for recurring patterns, 3-tier confidence assessment, nomination pipeline for rule proposals), status (recent sessions summary with trends), save (structured delta + log.md append + domain wiki update). Episodes-powered since v0.4.0."
|
|
25
25
|
},
|
|
26
26
|
{
|
|
27
27
|
"name": "mindlore-decide",
|
|
28
28
|
"path": "skills/mindlore-decide/SKILL.md",
|
|
29
|
-
"description": "Record and list decisions with context
|
|
29
|
+
"description": "Record and list architectural, tool, format, and strategy decisions with full context. Captures: alternatives considered, rationale, trade-offs, and outcome. Supports supersedes chain — when a decision replaces an older one, links them with reason. Episodes integration: decisions stored as kind:decision episodes with structured body. List mode shows active decisions with supersedes history."
|
|
30
30
|
},
|
|
31
31
|
{
|
|
32
32
|
"name": "mindlore-evolve",
|
|
33
33
|
"path": "skills/mindlore-evolve/SKILL.md",
|
|
34
|
-
"description": "Knowledge schema co-evolution — scan domains
|
|
34
|
+
"description": "Knowledge schema co-evolution — scan domains and sources for inconsistencies, suggest structural updates. Detects: orphaned references, outdated cross-links, missing domain coverage, INDEX.md drift, stale source summaries. Proposes: new domain pages, source updates, INDEX.md reorganization, CLAUDE.md convention changes. Default scope: project + global (--scope project|global|all)."
|
|
35
35
|
},
|
|
36
36
|
{
|
|
37
37
|
"name": "mindlore-explore",
|
|
38
38
|
"path": "skills/mindlore-explore/SKILL.md",
|
|
39
|
-
"description": "Discover unexpected connections between knowledge sources"
|
|
39
|
+
"description": "Discover unexpected connections between knowledge sources — undirected exploration mode. Cross-references domains, sources, analyses, and episodes to find non-obvious relationships. Identifies: shared entities across sources, pattern convergence between domains, episodic evidence supporting or contradicting domain claims. Default scope: project + global (--scope project|global|all)."
|
|
40
40
|
}
|
|
41
41
|
],
|
|
42
42
|
"hooks": [
|
|
@@ -11,10 +11,14 @@ Knowledge schema co-evolution. Karpathy's 4th operation (ingest/query/health/**e
|
|
|
11
11
|
|
|
12
12
|
## Scope
|
|
13
13
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
14
|
+
Default: `--scope all` (project + global birlikte taranır).
|
|
15
|
+
Options: `--scope project` | `--scope global` | `--scope all`
|
|
16
|
+
|
|
17
|
+
1. Determine scope from argument (default: all)
|
|
18
|
+
2. If `all`: scan both project `.mindlore/` and global `~/.mindlore/`
|
|
19
|
+
3. If `project`: scan only project-scoped content (current CWD's `.mindlore/`)
|
|
20
|
+
4. If `global`: scan only global `~/.mindlore/` content
|
|
21
|
+
- Never hardcode `.mindlore/` path — always resolve dynamically via `getActiveMindloreDir()`
|
|
18
22
|
|
|
19
23
|
## Trigger
|
|
20
24
|
|
|
@@ -11,10 +11,14 @@ Discover unexpected connections between knowledge sources. Undirected exploratio
|
|
|
11
11
|
|
|
12
12
|
## Scope
|
|
13
13
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
14
|
+
Default: `--scope all` (project + global birlikte taranır — en değerli mod).
|
|
15
|
+
Options: `--scope project` | `--scope global` | `--scope all`
|
|
16
|
+
|
|
17
|
+
1. Determine scope from argument (default: all)
|
|
18
|
+
2. If `all`: explore both project `.mindlore/` and global `~/.mindlore/` + cross-reference
|
|
19
|
+
3. If `project`: explore only project-scoped content (current CWD's `.mindlore/`)
|
|
20
|
+
4. If `global`: explore only global `~/.mindlore/` content
|
|
21
|
+
- Never hardcode `.mindlore/` path — always resolve dynamically via `getActiveMindloreDir()`
|
|
18
22
|
|
|
19
23
|
## Trigger
|
|
20
24
|
|
|
@@ -12,7 +12,7 @@ Determine target using `getActiveMindloreDir()` logic:
|
|
|
12
12
|
|
|
13
13
|
## Trigger
|
|
14
14
|
|
|
15
|
-
`/mindlore-log <mode>` where mode is `log`, `reflect`, `status`, or `save`.
|
|
15
|
+
`/mindlore-log <mode>` where mode is `log`, `diary`, `reflect`, `status`, or `save`.
|
|
16
16
|
|
|
17
17
|
## Modes
|
|
18
18
|
|
|
@@ -36,30 +36,147 @@ date: 2026-04-11
|
|
|
36
36
|
4. Body: user's note as-is
|
|
37
37
|
5. Append to `log.md`: `| {date} | log | {slug}.md |`
|
|
38
38
|
|
|
39
|
+
### diary
|
|
40
|
+
|
|
41
|
+
LLM-driven session analysis → enriched episodes in the episodes table.
|
|
42
|
+
|
|
43
|
+
**Trigger:** User runs `/mindlore-log diary` or Stop hook asks "Diary analizi yapayım mı?"
|
|
44
|
+
|
|
45
|
+
**Model:** `[mindlore:diary]` marker → sonnet (analysis needed)
|
|
46
|
+
|
|
47
|
+
**Flow:**
|
|
48
|
+
1. Open `~/.mindlore/mindlore.db`, ensure episodes table exists
|
|
49
|
+
2. Find the latest bare session episode for current project: `WHERE kind = 'session' AND project = ? AND source = 'hook' ORDER BY created_at DESC LIMIT 1`
|
|
50
|
+
3. Gather context:
|
|
51
|
+
- The bare episode's body (commits, files, read stats)
|
|
52
|
+
- Git log last 10 commits
|
|
53
|
+
- Decision-detector captures (if any in session)
|
|
54
|
+
4. LLM analyzes and extracts structured episodes:
|
|
55
|
+
- **Decisions** → `kind: 'decision'` — architectural/tool/format choices
|
|
56
|
+
- **Discoveries** → `kind: 'discovery'` — assumption vs reality findings
|
|
57
|
+
- **Frictions** → `kind: 'friction'` — tool errors, blockers, recurring issues
|
|
58
|
+
- **Learnings** → `kind: 'learning'` — reusable knowledge
|
|
59
|
+
- **Preferences** → `kind: 'preference'` — user behavioral preferences
|
|
60
|
+
- **Events** → `kind: 'event'` — releases, incidents, milestones
|
|
61
|
+
5. **Deduplication rule:** Each finding belongs to exactly ONE kind. Priority: `decision > discovery > friction > learning > preference > event`. Never write the same finding to multiple kinds.
|
|
62
|
+
6. Present to user, get approval
|
|
63
|
+
7. Write approved episodes to DB:
|
|
64
|
+
- `source: 'diary'`
|
|
65
|
+
- `parent_id: {bare_session_episode_id}` — links enriched episodes to source session
|
|
66
|
+
- `scope: 'project'` (default) or `'global'` if cross-project
|
|
67
|
+
8. Optionally mirror to FTS5 for text search
|
|
68
|
+
9. Append to `log.md`: `| {date} | diary | {N} episodes extracted from session |`
|
|
69
|
+
|
|
70
|
+
**Rules:**
|
|
71
|
+
- NEVER write episodes without user approval
|
|
72
|
+
- parent_id always points to the source session episode
|
|
73
|
+
- Each episode gets its own summary (max 100 chars) and body (markdown, unbounded)
|
|
74
|
+
- entities field: JSON array of relevant file paths (max 10)
|
|
75
|
+
|
|
39
76
|
### reflect
|
|
40
77
|
|
|
41
|
-
LLM-driven pattern extraction from
|
|
78
|
+
LLM-driven pattern extraction from episodes → persistent learnings.
|
|
42
79
|
|
|
43
|
-
**Flow (v0.
|
|
44
|
-
1. Read
|
|
45
|
-
2.
|
|
46
|
-
3.
|
|
47
|
-
|
|
48
|
-
- Repeated decisions (same choice
|
|
49
|
-
-
|
|
80
|
+
**Flow (v0.4 — episodes-powered):**
|
|
81
|
+
1. Read active episodes: `WHERE status = 'active' AND source IN ('hook', 'diary')`
|
|
82
|
+
2. Optionally filter by time: `--days 7` (default 7), `--days 30`
|
|
83
|
+
3. Present summary: "Found N episodes spanning DATE1 to DATE2"
|
|
84
|
+
4. LLM analyzes episodes (not deltas) for patterns:
|
|
85
|
+
- Repeated decisions (same choice 2+ times)
|
|
86
|
+
- Recurring frictions (same blocker/error)
|
|
87
|
+
- Discovery patterns (assumptions that keep breaking)
|
|
50
88
|
- Workflow patterns that worked well
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
89
|
+
5. **3-Tier Confidence Assessment:**
|
|
90
|
+
For each detected pattern, count occurrences across episodes:
|
|
91
|
+
|
|
92
|
+
| Tekrar | Tier | Aksiyon |
|
|
93
|
+
|--------|------|---------|
|
|
94
|
+
| 1x | Note | Sessiz — episode olarak kalır, raporda göster |
|
|
95
|
+
| 2x | Learning | `kind: learning` episode oluştur, learnings/ dosyasına yaz |
|
|
96
|
+
| 3x+ | Nomination | `kind: nomination, status: staged, source: reflect` episode oluştur |
|
|
97
|
+
|
|
98
|
+
6. **Structured report output:**
|
|
99
|
+
|
|
100
|
+
```
|
|
101
|
+
── Reflect Raporu (son {days} gün, {N} episode) ──
|
|
102
|
+
|
|
103
|
+
Friction ({count}):
|
|
104
|
+
- {summary} — {repeat_count}x tekrar
|
|
105
|
+
|
|
106
|
+
Discoveries ({count}):
|
|
107
|
+
- {summary}
|
|
108
|
+
|
|
109
|
+
Decisions ({count}):
|
|
110
|
+
- {summary}
|
|
111
|
+
|
|
112
|
+
Patterns:
|
|
113
|
+
- "CO-EVOLUTION sync hatası" → 3x tekrar → NOMINATION (staged)
|
|
114
|
+
- "ESM import sorunu" → 2x tekrar → LEARNING
|
|
115
|
+
- "Test mock karmaşıklığı" → 1x → NOTE
|
|
116
|
+
|
|
117
|
+
Önerilen:
|
|
118
|
+
[ ] {rule} ({repeat_count}x, {confidence} confidence)
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
7. **Nomination oluşturma (3x+ tekrar):**
|
|
122
|
+
- `kind: nomination`, `status: staged`, `source: reflect`
|
|
123
|
+
- Body formatı:
|
|
124
|
+
```markdown
|
|
125
|
+
## Target: learnings
|
|
126
|
+
## Rule
|
|
127
|
+
YAPMA: Schema değişikliğinde tek dosyayı güncelleme — CO-EVOLUTION sync zorunlu
|
|
128
|
+
## Evidence
|
|
129
|
+
- ep-xxx: episodes.ts güncellendi ama common.cjs unutuldu (2026-04-10)
|
|
130
|
+
- ep-yyy: Aynı hata tekrar (2026-04-12)
|
|
131
|
+
- ep-zzz: Test'te yakalandı (2026-04-13)
|
|
132
|
+
## Confidence
|
|
133
|
+
3x tekrar, 3 gün içinde
|
|
134
|
+
```
|
|
135
|
+
- Target options: `learnings` | `claude.md` | `domain:{slug}`
|
|
136
|
+
|
|
137
|
+
8. **Pending nominations check:**
|
|
138
|
+
Reflect başlarken staged nomination'ları kontrol et:
|
|
139
|
+
```sql
|
|
140
|
+
SELECT id, summary, body, created_at FROM episodes
|
|
141
|
+
WHERE kind = 'nomination' AND status = 'staged' AND project = ?
|
|
142
|
+
ORDER BY created_at ASC
|
|
143
|
+
```
|
|
144
|
+
Varsa kullanıcıya sun:
|
|
145
|
+
```
|
|
146
|
+
── Bekleyen Nomination'lar ({N} adet) ──
|
|
147
|
+
1. "CO-EVOLUTION sync zorunlu" (staged 2 gün önce)
|
|
148
|
+
Target: learnings | Confidence: 3x
|
|
149
|
+
2. "Test before commit" (staged 5 gün önce)
|
|
150
|
+
Target: claude.md | Confidence: 4x
|
|
151
|
+
|
|
152
|
+
Onaylamak istediğin numara(lar)ı seç, veya 'skip' de:
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
9. **Nomination approval flow:**
|
|
156
|
+
Kullanıcı onaylarsa:
|
|
157
|
+
- `status: staged → approved`
|
|
158
|
+
- Target'a göre yaz:
|
|
159
|
+
- `learnings` → ilgili `learnings/{topic}.md` dosyasına YAPMA/BEST PRACTICE ekle
|
|
160
|
+
- `claude.md` → ilgili projenin CLAUDE.md'sine kural ekle (kullanıcıya göster, onay al)
|
|
161
|
+
- `domain:{slug}` → ilgili domain sayfasına ekle
|
|
162
|
+
Kullanıcı reddederse:
|
|
163
|
+
- `status: staged → rejected`
|
|
164
|
+
- Body'ye `## Rejection Reason\n{kullanıcı açıklaması}` ekle
|
|
165
|
+
|
|
166
|
+
10. User approves new learnings → write to `learnings/{topic}.md`
|
|
167
|
+
11. Format: `YAPMA:` / `BEST PRACTICE:` / `KRITIK:` prefixed rules
|
|
168
|
+
12. Update relevant domain page if pattern relates to an existing domain
|
|
169
|
+
13. Mark processed episodes: future reflect skips already-processed timeranges
|
|
170
|
+
14. Append to `log.md`: `| {date} | reflect | {N} episodes processed, {M} learnings written |`
|
|
171
|
+
|
|
172
|
+
**Fallback:** Also reads non-archived delta files if episodes table is empty (backward compat with v0.3 deltas).
|
|
58
173
|
|
|
59
174
|
**Rules:**
|
|
60
|
-
- NEVER write learnings without user approval
|
|
175
|
+
- NEVER write learnings or nominations without user approval
|
|
61
176
|
- Group related patterns into existing topic files (don't create one file per pattern)
|
|
62
177
|
- Reflect scans both project + global diary/ in `--all` mode
|
|
178
|
+
- Deduplication: same pattern found in both episodes and deltas → episodes win
|
|
179
|
+
- Nominations with `status: staged` are hidden from default queries — only reflect sees them
|
|
63
180
|
|
|
64
181
|
### status
|
|
65
182
|
|
|
@@ -18,14 +18,17 @@ Determine search scope using `getActiveMindloreDir()` / `getAllDbs()` logic:
|
|
|
18
18
|
|
|
19
19
|
### search
|
|
20
20
|
|
|
21
|
-
FTS5 keyword search with
|
|
21
|
+
FTS5 keyword search + episodes recall with unified results.
|
|
22
22
|
|
|
23
23
|
**Flow:**
|
|
24
24
|
1. Parse user query into keywords (strip stop words)
|
|
25
|
-
2. Run FTS5 MATCH on `mindlore_fts` table
|
|
26
|
-
3.
|
|
27
|
-
4.
|
|
28
|
-
5.
|
|
25
|
+
2. Run FTS5 MATCH on `mindlore_fts` table (knowledge: "ne biliyorum")
|
|
26
|
+
3. Run LIKE search on `episodes` table (memory: "ne oldu, ne karar aldım")
|
|
27
|
+
4. Merge results: FTS5 results first, then matching episodes
|
|
28
|
+
5. Return top 5 knowledge results + top 3 episode results
|
|
29
|
+
6. Display knowledge as table with snippet preview, episodes as timeline
|
|
30
|
+
7. If `--tags <tag>` flag provided: `WHERE tags MATCH '<tag>'` filter (FTS5 only)
|
|
31
|
+
8. If `--episodes-only` flag: skip FTS5, show only episode matches
|
|
29
32
|
|
|
30
33
|
**Output format:**
|
|
31
34
|
```
|
package/templates/config.json
CHANGED