mindlore 0.0.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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 mindlore
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,128 @@
1
+ # Mindlore
2
+
3
+ AI-native knowledge system for [Claude Code](https://claude.ai/claude-code).
4
+
5
+ Persistent, searchable, evolving knowledge base that compounds across sessions.
6
+
7
+ [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE)
8
+ [![Node.js](https://img.shields.io/badge/Node.js-20%2B-green.svg)](https://nodejs.org)
9
+
10
+ ## Why
11
+
12
+ Claude Code forgets everything between sessions. Your corrections, discoveries, and decisions vanish. Mindlore gives Claude a persistent memory:
13
+
14
+ - **Knowledge persists** across sessions via FTS5-indexed Markdown files
15
+ - **Search happens automatically** — hooks inject relevant context as you work
16
+ - **Knowledge compounds** — query answers become searchable for future sessions
17
+
18
+ ## Why Mindlore?
19
+
20
+ | Feature | Mindlore | Typical KB CLI | Wiki Compilers | Multi-Agent Memory |
21
+ |---------|----------|---------------|----------------|-------------------|
22
+ | Zero workflow change | Hook-based, invisible | Manual commands | Manual compile | Agent orchestration |
23
+ | Persistent search | FTS5 auto-indexed | Manual index | No search | Vector DB overhead |
24
+ | Knowledge compounding | Writeback loop | No | No | Partial |
25
+ | Token efficient | Progressive disclosure | Full dump | Full dump | Varies |
26
+ | Setup time | `npx mindlore init` | Config + setup | Complex | Complex |
27
+
28
+ ## Quick Start
29
+
30
+ ```bash
31
+ npx mindlore init
32
+ ```
33
+
34
+ That's it. Mindlore creates a `.mindlore/` directory, sets up hooks, and starts working.
35
+
36
+ To add your first source:
37
+
38
+ ```
39
+ /mindlore-ingest https://example.com/article
40
+ ```
41
+
42
+ ## Features
43
+
44
+ | Skill | Version | Description |
45
+ |-------|---------|-------------|
46
+ | `/mindlore-ingest` | v0.1 | Add knowledge sources (URL, text, file, PDF) |
47
+ | `/mindlore-health` | v0.1 | 16-point structural health check |
48
+ | `/mindlore-query` | v0.2 | Search and retrieve knowledge (4 modes) |
49
+ | `/mindlore-log` | v0.2 | Session logging with reflect and status |
50
+ | `/mindlore-decide` | v0.2 | Decision records with supersedes chain |
51
+ | `/mindlore-evolve` | v0.3 | Schema co-evolution and structural updates |
52
+ | `/mindlore-explore` | v0.3 | Cross-reference discovery between sources |
53
+
54
+ ## Architecture
55
+
56
+ Knowledge flows through a compiler-like pipeline:
57
+
58
+ ```
59
+ raw/ Immutable source captures (URL dumps, pasted text)
60
+ |
61
+ sources/ Processed summaries (one per ingested source)
62
+ |
63
+ domains/ Topic wiki pages (accumulated knowledge by subject)
64
+ |
65
+ insights/ Query writebacks (answers that become searchable)
66
+ ```
67
+
68
+ Nine directories, each mapping to a frontmatter `type`:
69
+
70
+ ```
71
+ .mindlore/
72
+ ├── raw/ # Immutable captures
73
+ ├── sources/ # Processed summaries
74
+ ├── domains/ # Topic wikis
75
+ ├── analyses/ # Large syntheses (3+ sources)
76
+ ├── insights/ # Short Q&A writebacks
77
+ ├── connections/ # Cross-cutting links
78
+ ├── learnings/ # Persistent rules from reflect
79
+ ├── diary/ # Session deltas
80
+ ├── decisions/ # Decision records
81
+ ├── INDEX.md # Navigation map (~15 lines)
82
+ ├── SCHEMA.md # LLM specification
83
+ └── mindlore.db # FTS5 search database
84
+ ```
85
+
86
+ ## Installation
87
+
88
+ ### Minimal (default)
89
+
90
+ ```bash
91
+ npx mindlore init
92
+ ```
93
+
94
+ Requires: Node.js 20+, `better-sqlite3` (installed automatically).
95
+
96
+ ### Recommended
97
+
98
+ ```bash
99
+ npx mindlore init --recommended
100
+ ```
101
+
102
+ Also suggests installing:
103
+ - **markitdown** — better web/document extraction (URL, DOCX, YouTube)
104
+ - **context-mode** — token savings for large sessions
105
+
106
+ ## Hooks
107
+
108
+ Mindlore works through 7 Claude Code lifecycle hooks (v0.1):
109
+
110
+ | Event | Hook | What it does |
111
+ |-------|------|-------------|
112
+ | SessionStart | session-focus | Injects last delta + INDEX |
113
+ | UserPromptSubmit | search | FTS5 search, top 3 results |
114
+ | FileChanged | index | Sync changed files to FTS5 |
115
+ | FileChanged | fts5-sync | Incremental batch re-index |
116
+ | SessionEnd | session-end | Write delta to diary/ |
117
+ | PreCompact | pre-compact | FTS5 flush before compaction |
118
+ | PostCompact | post-compact | Re-inject context |
119
+
120
+ ## Inspired By
121
+
122
+ - [Andrej Karpathy](https://gist.github.com/karpathy/442a6bf555914893e9891c11519de94f) — LLM Knowledge Bases concept
123
+ - [Spisak](https://github.com/nickspisak) — Practical second brain implementations
124
+ - [Letta](https://github.com/letta-ai/letta-code) — Context repository pattern validation
125
+
126
+ ## License
127
+
128
+ [MIT](LICENSE)
package/SCHEMA.md ADDED
@@ -0,0 +1,221 @@
1
+ # Mindlore Schema
2
+
3
+ This file is the single source of truth for how the `.mindlore/` knowledge base works.
4
+ It is written for LLM agents — not humans. Every rule here must be followed exactly.
5
+
6
+ ## 1. Identity
7
+
8
+ Mindlore is an AI-native knowledge system. It stores, indexes, and evolves knowledge
9
+ across Claude Code sessions. Knowledge lives in `.mindlore/` as plain Markdown files
10
+ with YAML frontmatter. Search is powered by FTS5 (SQLite full-text search).
11
+
12
+ ## 2. Directory Structure
13
+
14
+ ```
15
+ .mindlore/
16
+ ├── raw/ # Immutable source captures (URL dumps, pasted text)
17
+ ├── sources/ # Processed source summaries (one per ingested source)
18
+ ├── domains/ # Topic wiki pages (entities + concepts)
19
+ ├── analyses/ # Large syntheses (200+ lines, 3+ sources)
20
+ ├── insights/ # Short Q&A writebacks (<200 lines, 1-2 sources)
21
+ ├── connections/ # Cross-cutting links between 2+ sources
22
+ ├── learnings/ # Persistent rules extracted from reflect (topic-based)
23
+ ├── diary/ # Session logs, delta files (delta-YYYY-MM-DD-HHmm.md)
24
+ ├── decisions/ # Decision records with context and rationale
25
+ ├── INDEX.md # Minimal navigation map (~15-20 lines, fixed size)
26
+ ├── SCHEMA.md # This file (copied from repo template)
27
+ ├── log.md # Operation log (append-only)
28
+ └── mindlore.db # FTS5 search database (SQLite)
29
+ ```
30
+
31
+ ### Directory Rules
32
+
33
+ - Each directory corresponds to exactly one frontmatter `type` value
34
+ - `type: raw` → `raw/`, `type: source` → `sources/`, etc.
35
+ - Files MUST live in the directory matching their `type`
36
+ - The health check script validates this cross-reference
37
+
38
+ ## 3. Frontmatter
39
+
40
+ Every `.md` file in `.mindlore/` MUST have YAML frontmatter. Format:
41
+
42
+ ```yaml
43
+ ---
44
+ slug: kebab-case-unique-identifier
45
+ type: raw|source|domain|analysis|insight|connection|learning|decision|diary
46
+ title: Human-readable title
47
+ tags: [tag1, tag2]
48
+ ---
49
+ ```
50
+
51
+ ### Required Fields (all types)
52
+
53
+ | Field | Format | Rule |
54
+ |-------|--------|------|
55
+ | `slug` | kebab-case | Unique within directory. Used as filename: `{slug}.md` |
56
+ | `type` | enum | Must match the parent directory (see Section 2) |
57
+
58
+ ### Type-Specific Fields
59
+
60
+ | Type | Required Fields | Optional |
61
+ |------|----------------|----------|
62
+ | `raw` | slug, type, source_url | tags |
63
+ | `source` | slug, type, title, tags, quality, source_url, ingested | date_captured |
64
+ | `domain` | slug, type, title, tags | — |
65
+ | `analysis` | slug, type, title, tags, confidence, sources_used | — |
66
+ | `insight` | slug, type, title, tags | sources_used |
67
+ | `connection` | slug, type, title, tags | sources_used |
68
+ | `learning` | slug, type, title, tags | — |
69
+ | `decision` | slug, type, title, tags | supersedes, status |
70
+ | `diary` | slug, type, date | — (hook adds automatically) |
71
+
72
+ ### Field Value Rules
73
+
74
+ - `quality`: `high` | `medium` | `low`
75
+ - `confidence`: `high` | `medium` | `low` (how certain is this analysis)
76
+ - `ingested`: `true` | `false` (has this source been processed into domains)
77
+ - `sources_used`: list of slugs referenced in the analysis
78
+ - `supersedes`: slug of the decision this one replaces
79
+ - `date`: ISO 8601 date (YYYY-MM-DD)
80
+
81
+ ## 4. Seven Operations
82
+
83
+ ### 4.1 Ingest (skill: /mindlore-ingest)
84
+
85
+ Add new knowledge. Flow: capture → raw/ → process → sources/ → update domains/ → FTS5.
86
+
87
+ - URL mode: markitdown CLI (if available) or WebFetch → raw/ → Sonnet summarizes → sources/
88
+ - Text mode: user paste → raw/ → summarize → sources/
89
+ - PDF mode: CC Read tool (max 20 pages/request) → raw/ → summarize → sources/
90
+ - **markitdown is NOT used for PDF** — quality is poor. Use CC Read tool or Marker/Chandra (v0.3+)
91
+
92
+ ### 4.2 Query (skill: /mindlore-query, v0.2 — PLANNED, not yet implemented)
93
+
94
+ Search and retrieve knowledge. Four modes:
95
+ - `search`: FTS5 keyword search, return top 3 matches with snippets
96
+ - `ask`: Natural language question → FTS5 → read relevant files → synthesize answer
97
+ - `stats`: Knowledge base statistics (counts by type, recent activity)
98
+ - `brief`: Quick context on a topic (read domain page if exists)
99
+
100
+ ### 4.3 Health (skill: /mindlore-health)
101
+
102
+ Run 16-point structural check:
103
+ - 9 directory existence checks
104
+ - SCHEMA.md parse validation
105
+ - INDEX.md format check (~15-20 lines)
106
+ - mindlore.db FTS5 integrity
107
+ - Orphan file detection (files not in FTS5)
108
+ - Frontmatter validation (type-directory cross-reference)
109
+
110
+ ### 4.4 Log (skill: /mindlore-log, v0.2 — PLANNED, not yet implemented)
111
+
112
+ Session logging with four modes:
113
+ - `log`: Write session/task record to diary/
114
+ - `reflect`: Scan old deltas, extract patterns, move to learnings/
115
+ - `status`: Recent N sessions summary, trends, open items
116
+ - `save`: Structured delta + log.md append + wiki update
117
+
118
+ ### 4.5 Decide (skill: /mindlore-decide, v0.2 — PLANNED, not yet implemented)
119
+
120
+ Record decisions with context, options considered, rationale, and outcome.
121
+ Supports `supersedes` chain for decision evolution.
122
+
123
+ ### 4.6 Evolve (skill: /mindlore-evolve, v0.3 — PLANNED, not yet implemented)
124
+
125
+ Schema co-evolution. Scan domains + sources, suggest structural updates.
126
+ Run monthly or after major changes.
127
+
128
+ ### 4.7 Explore (skill: /mindlore-explore, v0.3 — PLANNED, not yet implemented)
129
+
130
+ Discover unexpected connections between sources. Cross-reference analysis.
131
+
132
+ ## 5. Search Behavior
133
+
134
+ ### FTS5 Search (hooks + scripts)
135
+
136
+ - Database: `.mindlore/mindlore.db`
137
+ - Table: `mindlore_fts` (columns: path, content)
138
+ - Dedup: `file_hashes` table with SHA256 content-hash
139
+ - Tokenizer: `unicode61`
140
+ - Max results: 3 per query (BM25 ranking)
141
+ - Hook injects: file path + first 2 headings
142
+
143
+ ### Search Flow (UserPromptSubmit hook)
144
+
145
+ 1. Extract keywords from user prompt
146
+ 2. Query FTS5 with BM25 ranking
147
+ 3. Return max 3 results as stderr additionalContext
148
+ 4. Agent reads full file only if needed (progressive disclosure)
149
+
150
+ ## 6. Compounding
151
+
152
+ Knowledge compounds when outputs become inputs:
153
+
154
+ ```
155
+ Query answer → writeback to insights/ → FTS5 indexes → next query finds it
156
+ Reflect → patterns to learnings/ → session-focus injects → agent applies
157
+ ```
158
+
159
+ ### Writeback Triggers
160
+
161
+ Offer to save when:
162
+ - Comparison table generated (X vs Y)
163
+ - Architectural decision or evaluation made
164
+ - 3+ sources synthesized
165
+ - User says "save this" or "remember this"
166
+
167
+ ### Writeback Rules
168
+
169
+ - Short answer (<200 lines) → insights/
170
+ - Large synthesis (200+ lines, 3+ sources) → analyses/
171
+ - Cross-cutting link → connections/
172
+
173
+ ## 7. Learnings
174
+
175
+ Persistent rules extracted from reflect operations.
176
+ Organized by topic: `git.md`, `testing.md`, `security.md`, etc.
177
+
178
+ ### Format
179
+
180
+ ```markdown
181
+ ---
182
+ slug: testing
183
+ type: learning
184
+ title: Testing Learnings
185
+ tags: [testing, jest, mock]
186
+ ---
187
+
188
+ # Testing Learnings
189
+
190
+ - YAPMA: Error handling testinde mock'u dolayli tetikleme
191
+ - BEST PRACTICE: Side-effect'li moduller eklerken TUM test'lerde mock ekle
192
+ ```
193
+
194
+ ### Rules
195
+
196
+ - One file per topic (not per lesson)
197
+ - Append new learnings to existing topic file
198
+ - Use `YAPMA:` / `BEST PRACTICE:` / `KRITIK:` prefixes
199
+ - Reflect skill proposes, user approves before writing
200
+
201
+ ## 8. Naming Conventions
202
+
203
+ ### Files
204
+
205
+ - Filename = `{slug}.md` (kebab-case)
206
+ - Diary: `delta-YYYY-MM-DD-HHmm.md`
207
+ - No spaces, no uppercase in filenames
208
+
209
+ ### Slugs
210
+
211
+ - kebab-case only: `my-analysis-topic`
212
+ - Unique within directory
213
+ - Descriptive but concise (3-5 words max)
214
+
215
+ ### INDEX.md
216
+
217
+ - Fixed size: ~15-20 lines
218
+ - Domain list (entities/concepts headings)
219
+ - Stats line: "N source, N analysis, N total"
220
+ - Last 5 added (initially empty)
221
+ - NO full file listing — discovery via FTS5
@@ -0,0 +1,90 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * Shared utilities for mindlore hooks.
5
+ * Eliminates duplication of findMindloreDir, getLatestDelta, sha256, etc.
6
+ */
7
+
8
+ const fs = require('fs');
9
+ const path = require('path');
10
+ const crypto = require('crypto');
11
+ const os = require('os');
12
+
13
+ const MINDLORE_DIR = '.mindlore';
14
+ const DB_NAME = 'mindlore.db';
15
+ const SKIP_FILES = new Set(['INDEX.md', 'SCHEMA.md', 'log.md']);
16
+
17
+ function findMindloreDir() {
18
+ const projectDir = path.join(process.cwd(), MINDLORE_DIR);
19
+ if (fs.existsSync(projectDir)) return projectDir;
20
+
21
+ const globalDir = path.join(os.homedir(), MINDLORE_DIR);
22
+ if (fs.existsSync(globalDir)) return globalDir;
23
+
24
+ return null;
25
+ }
26
+
27
+ function getLatestDelta(diaryDir) {
28
+ if (!fs.existsSync(diaryDir)) return null;
29
+
30
+ const deltas = fs
31
+ .readdirSync(diaryDir)
32
+ .filter((f) => f.startsWith('delta-') && f.endsWith('.md'))
33
+ .sort()
34
+ .reverse();
35
+
36
+ if (deltas.length === 0) return null;
37
+ return path.join(diaryDir, deltas[0]);
38
+ }
39
+
40
+ function sha256(content) {
41
+ return crypto.createHash('sha256').update(content, 'utf8').digest('hex');
42
+ }
43
+
44
+ function requireDatabase() {
45
+ try {
46
+ return require('better-sqlite3');
47
+ } catch (_err) {
48
+ return null;
49
+ }
50
+ }
51
+
52
+ function openDatabase(dbPath, opts) {
53
+ const Database = requireDatabase();
54
+ if (!Database) return null;
55
+
56
+ const db = new Database(dbPath, opts);
57
+ if (!opts || !opts.readonly) {
58
+ db.pragma('journal_mode = WAL');
59
+ }
60
+ return db;
61
+ }
62
+
63
+ function getAllMdFiles(dir, skip) {
64
+ const skipSet = skip || SKIP_FILES;
65
+ const results = [];
66
+ if (!fs.existsSync(dir)) return results;
67
+
68
+ const entries = fs.readdirSync(dir, { withFileTypes: true });
69
+ for (const entry of entries) {
70
+ const fullPath = path.join(dir, entry.name);
71
+ if (entry.isDirectory()) {
72
+ results.push(...getAllMdFiles(fullPath, skipSet));
73
+ } else if (entry.name.endsWith('.md') && !skipSet.has(entry.name)) {
74
+ results.push(fullPath);
75
+ }
76
+ }
77
+ return results;
78
+ }
79
+
80
+ module.exports = {
81
+ MINDLORE_DIR,
82
+ DB_NAME,
83
+ SKIP_FILES,
84
+ findMindloreDir,
85
+ getLatestDelta,
86
+ sha256,
87
+ requireDatabase,
88
+ openDatabase,
89
+ getAllMdFiles,
90
+ };
@@ -0,0 +1,91 @@
1
+ #!/usr/bin/env node
2
+ 'use strict';
3
+
4
+ /**
5
+ * mindlore-fts5-sync — FileChanged hook (incremental re-index)
6
+ *
7
+ * Handles bulk file changes by checking all .mindlore/ .md files
8
+ * against their content hashes and re-indexing only changed ones.
9
+ *
10
+ * Lightweight complement to mindlore-index.cjs which handles single files.
11
+ * This hook catches cases where multiple files change at once (e.g., git pull).
12
+ */
13
+
14
+ const fs = require('fs');
15
+ const path = require('path');
16
+ const { MINDLORE_DIR, DB_NAME, sha256, requireDatabase, getAllMdFiles } = require('./lib/mindlore-common.cjs');
17
+
18
+ function main() {
19
+ // Read stdin to check if this is a .mindlore/ file change
20
+ let input = '';
21
+ try {
22
+ input = fs.readFileSync(0, 'utf8').trim();
23
+ } catch (_err) {
24
+ // No stdin — skip
25
+ }
26
+
27
+ let filePath = '';
28
+ try {
29
+ const parsed = JSON.parse(input);
30
+ filePath = parsed.path || parsed.file_path || '';
31
+ } catch (_err) {
32
+ filePath = input;
33
+ }
34
+
35
+ // Only trigger on .mindlore/ changes (empty filePath = skip)
36
+ if (!filePath || !filePath.includes(MINDLORE_DIR)) return;
37
+
38
+ const baseDir = path.join(process.cwd(), MINDLORE_DIR);
39
+ if (!fs.existsSync(baseDir)) return;
40
+
41
+ const dbPath = path.join(baseDir, DB_NAME);
42
+ if (!fs.existsSync(dbPath)) return;
43
+
44
+ const Database = requireDatabase();
45
+ if (!Database) return;
46
+
47
+ const db = new Database(dbPath);
48
+ db.pragma('journal_mode = WAL');
49
+
50
+ const mdFiles = getAllMdFiles(baseDir);
51
+ let synced = 0;
52
+
53
+ const getHash = db.prepare('SELECT content_hash FROM file_hashes WHERE path = ?');
54
+ const deleteFts = db.prepare('DELETE FROM mindlore_fts WHERE path = ?');
55
+ const insertFts = db.prepare('INSERT INTO mindlore_fts (path, content) VALUES (?, ?)');
56
+ const upsertHash = db.prepare(`
57
+ INSERT INTO file_hashes (path, content_hash, last_indexed)
58
+ VALUES (?, ?, ?)
59
+ ON CONFLICT(path) DO UPDATE SET
60
+ content_hash = excluded.content_hash,
61
+ last_indexed = excluded.last_indexed
62
+ `);
63
+
64
+ const now = new Date().toISOString();
65
+
66
+ try {
67
+ const transaction = db.transaction(() => {
68
+ for (const file of mdFiles) {
69
+ const content = fs.readFileSync(file, 'utf8').replace(/\r\n/g, '\n');
70
+ const hash = sha256(content);
71
+
72
+ const existing = getHash.get(file);
73
+ if (existing && existing.content_hash === hash) continue;
74
+
75
+ deleteFts.run(file);
76
+ insertFts.run(file, content);
77
+ upsertHash.run(file, hash, now);
78
+ synced++;
79
+ }
80
+ });
81
+ transaction();
82
+ } finally {
83
+ db.close();
84
+ }
85
+
86
+ if (synced > 0) {
87
+ process.stderr.write(`[Mindlore FTS5 Sync: ${synced} files re-indexed]\n`);
88
+ }
89
+ }
90
+
91
+ main();
@@ -0,0 +1,96 @@
1
+ #!/usr/bin/env node
2
+ 'use strict';
3
+
4
+ /**
5
+ * mindlore-index — FileChanged hook
6
+ *
7
+ * When a .md file in .mindlore/ changes, update its FTS5 entry.
8
+ * Reads changed file path from stdin (CC FileChanged event).
9
+ */
10
+
11
+ const fs = require('fs');
12
+ const path = require('path');
13
+ const { MINDLORE_DIR, DB_NAME, SKIP_FILES, sha256, requireDatabase } = require('./lib/mindlore-common.cjs');
14
+
15
+ function main() {
16
+ let input = '';
17
+ try {
18
+ input = fs.readFileSync(0, 'utf8').trim();
19
+ } catch (_err) {
20
+ return;
21
+ }
22
+
23
+ let filePath = '';
24
+ try {
25
+ const parsed = JSON.parse(input);
26
+ filePath = parsed.path || parsed.file_path || '';
27
+ } catch (_err) {
28
+ filePath = input;
29
+ }
30
+
31
+ if (!filePath) return;
32
+
33
+ // Only process .md files inside .mindlore/
34
+ if (!filePath.includes(MINDLORE_DIR) || !filePath.endsWith('.md')) return;
35
+
36
+ const fileName = path.basename(filePath);
37
+ if (SKIP_FILES.has(fileName)) return;
38
+
39
+ // Find the .mindlore dir from the file path
40
+ const mindloreIdx = filePath.indexOf(MINDLORE_DIR);
41
+ const baseDir = filePath.slice(0, mindloreIdx + MINDLORE_DIR.length);
42
+ const dbPath = path.join(baseDir, DB_NAME);
43
+
44
+ if (!fs.existsSync(dbPath)) return;
45
+
46
+ const Database = requireDatabase();
47
+ if (!Database) return;
48
+
49
+ if (!fs.existsSync(filePath)) {
50
+ // File was deleted — remove from index
51
+ const db = new Database(dbPath);
52
+ db.pragma('journal_mode = WAL');
53
+ try {
54
+ db.prepare('DELETE FROM mindlore_fts WHERE path = ?').run(filePath);
55
+ db.prepare('DELETE FROM file_hashes WHERE path = ?').run(filePath);
56
+ } finally {
57
+ db.close();
58
+ }
59
+ return;
60
+ }
61
+
62
+ const content = fs.readFileSync(filePath, 'utf8').replace(/\r\n/g, '\n');
63
+ const hash = sha256(content);
64
+
65
+ const db = new Database(dbPath);
66
+ db.pragma('journal_mode = WAL');
67
+
68
+ try {
69
+ // Check if content changed
70
+ const existing = db
71
+ .prepare('SELECT content_hash FROM file_hashes WHERE path = ?')
72
+ .get(filePath);
73
+
74
+ if (existing && existing.content_hash === hash) return; // Unchanged
75
+
76
+ // Update FTS5
77
+ db.prepare('DELETE FROM mindlore_fts WHERE path = ?').run(filePath);
78
+ db.prepare('INSERT INTO mindlore_fts (path, content) VALUES (?, ?)').run(
79
+ filePath,
80
+ content
81
+ );
82
+
83
+ // Update hash
84
+ db.prepare(
85
+ `INSERT INTO file_hashes (path, content_hash, last_indexed)
86
+ VALUES (?, ?, ?)
87
+ ON CONFLICT(path) DO UPDATE SET
88
+ content_hash = excluded.content_hash,
89
+ last_indexed = excluded.last_indexed`
90
+ ).run(filePath, hash, new Date().toISOString());
91
+ } finally {
92
+ db.close();
93
+ }
94
+ }
95
+
96
+ main();
@@ -0,0 +1,46 @@
1
+ #!/usr/bin/env node
2
+ 'use strict';
3
+
4
+ /**
5
+ * mindlore-post-compact — PostCompact hook
6
+ *
7
+ * After context compaction, re-inject session context:
8
+ * 1. Read INDEX.md
9
+ * 2. Read latest delta
10
+ * 3. Inject via stderr (same as session-focus)
11
+ *
12
+ * This ensures the agent has knowledge context after compaction.
13
+ */
14
+
15
+ const fs = require('fs');
16
+ const path = require('path');
17
+ const { findMindloreDir, getLatestDelta } = require('./lib/mindlore-common.cjs');
18
+
19
+ function main() {
20
+ const baseDir = findMindloreDir();
21
+ if (!baseDir) return;
22
+
23
+ const output = [];
24
+
25
+ // Re-inject INDEX.md
26
+ const indexPath = path.join(baseDir, 'INDEX.md');
27
+ if (fs.existsSync(indexPath)) {
28
+ const content = fs.readFileSync(indexPath, 'utf8').trim();
29
+ output.push(`[Mindlore INDEX (post-compact)]\n${content}`);
30
+ }
31
+
32
+ // Re-inject latest delta
33
+ const diaryDir = path.join(baseDir, 'diary');
34
+ const latestDelta = getLatestDelta(diaryDir);
35
+ if (latestDelta) {
36
+ const deltaContent = fs.readFileSync(latestDelta, 'utf8').trim();
37
+ const deltaName = path.basename(latestDelta);
38
+ output.push(`[Mindlore Delta (post-compact): ${deltaName}]\n${deltaContent}`);
39
+ }
40
+
41
+ if (output.length > 0) {
42
+ process.stderr.write(output.join('\n\n') + '\n');
43
+ }
44
+ }
45
+
46
+ main();