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.
@@ -0,0 +1,48 @@
1
+ #!/usr/bin/env node
2
+ 'use strict';
3
+
4
+ /**
5
+ * mindlore-pre-compact — PreCompact hook
6
+ *
7
+ * Before context compaction:
8
+ * 1. Write a delta snapshot to diary/ (if not already written this session)
9
+ * 2. Ensure FTS5 index is up to date
10
+ */
11
+
12
+ const fs = require('fs');
13
+ const path = require('path');
14
+ const { findMindloreDir } = require('./lib/mindlore-common.cjs');
15
+
16
+ function main() {
17
+ const baseDir = findMindloreDir();
18
+ if (!baseDir) return;
19
+
20
+ // Trigger FTS5 sync via the index script
21
+ const indexScript = path.join(__dirname, '..', 'scripts', 'mindlore-fts5-index.cjs');
22
+ if (fs.existsSync(indexScript)) {
23
+ try {
24
+ const { execSync } = require('child_process');
25
+ execSync(`node "${indexScript}" "${baseDir}"`, {
26
+ timeout: 10000,
27
+ stdio: 'pipe',
28
+ });
29
+ } catch (_err) {
30
+ // Non-fatal — index might fail if better-sqlite3 not available
31
+ }
32
+ }
33
+
34
+ // Write a pre-compact marker to diary
35
+ const diaryDir = path.join(baseDir, 'diary');
36
+ if (!fs.existsSync(diaryDir)) return;
37
+
38
+ const logPath = path.join(baseDir, 'log.md');
39
+ if (fs.existsSync(logPath)) {
40
+ const now = new Date().toISOString();
41
+ const entry = `| ${now.slice(0, 10)} | pre-compact | FTS5 flush before compaction |\n`;
42
+ fs.appendFileSync(logPath, entry, 'utf8');
43
+ }
44
+
45
+ process.stderr.write('[Mindlore: pre-compact FTS5 flush complete]\n');
46
+ }
47
+
48
+ main();
@@ -0,0 +1,133 @@
1
+ #!/usr/bin/env node
2
+ 'use strict';
3
+
4
+ /**
5
+ * mindlore-search — UserPromptSubmit hook
6
+ *
7
+ * Extracts keywords from user prompt, searches FTS5, injects top 3 results.
8
+ * Results: file path + first 2 headings via stderr additionalContext.
9
+ */
10
+
11
+ const fs = require('fs');
12
+ const path = require('path');
13
+ const { findMindloreDir, DB_NAME, requireDatabase } = require('./lib/mindlore-common.cjs');
14
+
15
+ const MAX_RESULTS = 3;
16
+ const MIN_QUERY_LENGTH = 3;
17
+
18
+ function extractKeywords(text) {
19
+ // Remove common stop words and short words
20
+ const stopWords = new Set([
21
+ 'the', 'a', 'an', 'is', 'are', 'was', 'were', 'be', 'been', 'being',
22
+ 'have', 'has', 'had', 'do', 'does', 'did', 'will', 'would', 'could',
23
+ 'should', 'may', 'might', 'can', 'shall', 'to', 'of', 'in', 'for',
24
+ 'on', 'with', 'at', 'by', 'from', 'as', 'into', 'about', 'between',
25
+ 'through', 'after', 'before', 'above', 'below', 'up', 'down', 'out',
26
+ 'and', 'but', 'or', 'nor', 'not', 'so', 'yet', 'both', 'either',
27
+ 'neither', 'each', 'every', 'all', 'any', 'few', 'more', 'most',
28
+ 'other', 'some', 'such', 'no', 'only', 'own', 'same', 'than', 'too',
29
+ 'very', 'just', 'also', 'now', 'then', 'here', 'there', 'when',
30
+ 'where', 'why', 'how', 'what', 'which', 'who', 'whom', 'this',
31
+ 'that', 'these', 'those', 'it', 'its', 'my', 'your', 'his', 'her',
32
+ 'our', 'their', 'me', 'him', 'us', 'them', 'i', 'you', 'he', 'she',
33
+ 'we', 'they', 'bu', 'su', 'bir', 'de', 'da', 've', 'ile', 'icin',
34
+ 'var', 'mi', 'ne', 'nasil', 'nedir', 'evet', 'hayir',
35
+ ]);
36
+
37
+ const words = text
38
+ .toLowerCase()
39
+ .replace(/[^a-z0-9\u00e7\u011f\u0131\u00f6\u015f\u00fc\s-]/g, ' ')
40
+ .split(/\s+/)
41
+ .filter((w) => w.length >= MIN_QUERY_LENGTH && !stopWords.has(w));
42
+
43
+ // Deduplicate and limit
44
+ return [...new Set(words)].slice(0, 5);
45
+ }
46
+
47
+ function extractHeadings(content, max) {
48
+ const headings = [];
49
+ for (const line of content.split('\n')) {
50
+ if (line.startsWith('#')) {
51
+ headings.push(line.replace(/^#+\s*/, '').trim());
52
+ if (headings.length >= max) break;
53
+ }
54
+ }
55
+ return headings;
56
+ }
57
+
58
+ function main() {
59
+ // Read user prompt from stdin
60
+ let input = '';
61
+ try {
62
+ input = fs.readFileSync(0, 'utf8');
63
+ } catch (_err) {
64
+ return;
65
+ }
66
+
67
+ let userMessage = '';
68
+ try {
69
+ const parsed = JSON.parse(input);
70
+ userMessage = parsed.content || parsed.message || parsed.query || input;
71
+ } catch (_err) {
72
+ userMessage = input;
73
+ }
74
+
75
+ if (!userMessage || userMessage.length < MIN_QUERY_LENGTH) return;
76
+
77
+ const baseDir = findMindloreDir();
78
+ if (!baseDir) return;
79
+
80
+ const dbPath = path.join(baseDir, DB_NAME);
81
+ if (!fs.existsSync(dbPath)) return;
82
+
83
+ const keywords = extractKeywords(userMessage);
84
+ if (keywords.length === 0) return;
85
+
86
+ const Database = requireDatabase();
87
+ if (!Database) return;
88
+
89
+ const db = new Database(dbPath, { readonly: true });
90
+
91
+ try {
92
+ // Build FTS5 query — OR between keywords
93
+ const ftsQuery = keywords.join(' OR ');
94
+
95
+ const results = db
96
+ .prepare(
97
+ `SELECT path, rank
98
+ FROM mindlore_fts
99
+ WHERE mindlore_fts MATCH ?
100
+ ORDER BY rank
101
+ LIMIT ?`
102
+ )
103
+ .all(ftsQuery, MAX_RESULTS);
104
+
105
+ if (results.length === 0) return;
106
+
107
+ const output = [];
108
+ for (const r of results) {
109
+ const relativePath = path.relative(baseDir, r.path);
110
+ let headings = [];
111
+
112
+ if (fs.existsSync(r.path)) {
113
+ const content = fs.readFileSync(r.path, 'utf8');
114
+ headings = extractHeadings(content, 2);
115
+ }
116
+
117
+ const headingStr = headings.length > 0 ? ` — ${headings.join(', ')}` : '';
118
+ output.push(`${relativePath}${headingStr}`);
119
+ }
120
+
121
+ if (output.length > 0) {
122
+ process.stderr.write(
123
+ `[Mindlore Search: ${keywords.join(', ')}]\n${output.join('\n')}\n`
124
+ );
125
+ }
126
+ } catch (_err) {
127
+ // FTS5 query error — silently skip
128
+ } finally {
129
+ db.close();
130
+ }
131
+ }
132
+
133
+ main();
@@ -0,0 +1,68 @@
1
+ #!/usr/bin/env node
2
+ 'use strict';
3
+
4
+ /**
5
+ * mindlore-session-end — SessionEnd hook
6
+ *
7
+ * Writes a basic delta file to diary/ with session timestamp.
8
+ * v0.1: minimal delta (timestamp + marker)
9
+ * v0.2: structured delta with stats, decisions, learnings
10
+ */
11
+
12
+ const fs = require('fs');
13
+ const path = require('path');
14
+ const { findMindloreDir } = require('./lib/mindlore-common.cjs');
15
+
16
+ function formatDate(date) {
17
+ const y = date.getFullYear();
18
+ const m = String(date.getMonth() + 1).padStart(2, '0');
19
+ const d = String(date.getDate()).padStart(2, '0');
20
+ const h = String(date.getHours()).padStart(2, '0');
21
+ const min = String(date.getMinutes()).padStart(2, '0');
22
+ return `${y}-${m}-${d}-${h}${min}`;
23
+ }
24
+
25
+ function main() {
26
+ const baseDir = findMindloreDir();
27
+ if (!baseDir) return;
28
+
29
+ const diaryDir = path.join(baseDir, 'diary');
30
+ if (!fs.existsSync(diaryDir)) {
31
+ fs.mkdirSync(diaryDir, { recursive: true });
32
+ }
33
+
34
+ const now = new Date();
35
+ const dateStr = formatDate(now);
36
+ const deltaPath = path.join(diaryDir, `delta-${dateStr}.md`);
37
+
38
+ // Don't overwrite existing delta (idempotent)
39
+ if (fs.existsSync(deltaPath)) return;
40
+
41
+ const content = [
42
+ '---',
43
+ `slug: delta-${dateStr}`,
44
+ 'type: diary',
45
+ `date: ${now.toISOString().slice(0, 10)}`,
46
+ '---',
47
+ '',
48
+ `# Session Delta — ${dateStr}`,
49
+ '',
50
+ `Session ended: ${now.toISOString()}`,
51
+ '',
52
+ '## Changes',
53
+ '',
54
+ '_No structured changes tracked in v0.1. Upgrade to v0.2 for detailed deltas._',
55
+ '',
56
+ ].join('\n');
57
+
58
+ fs.writeFileSync(deltaPath, content, 'utf8');
59
+
60
+ // Append to log.md
61
+ const logPath = path.join(baseDir, 'log.md');
62
+ if (fs.existsSync(logPath)) {
63
+ const logEntry = `| ${now.toISOString().slice(0, 10)} | session-end | delta-${dateStr}.md |\n`;
64
+ fs.appendFileSync(logPath, logEntry, 'utf8');
65
+ }
66
+ }
67
+
68
+ main();
@@ -0,0 +1,42 @@
1
+ #!/usr/bin/env node
2
+ 'use strict';
3
+
4
+ /**
5
+ * mindlore-session-focus — SessionStart hook
6
+ *
7
+ * Injects last delta file content + INDEX.md into session context.
8
+ * Fires once at session start via stderr additionalContext.
9
+ */
10
+
11
+ const fs = require('fs');
12
+ const path = require('path');
13
+ const { findMindloreDir, getLatestDelta } = require('./lib/mindlore-common.cjs');
14
+
15
+ function main() {
16
+ const baseDir = findMindloreDir();
17
+ if (!baseDir) return; // No .mindlore/ found, silently skip
18
+
19
+ const output = [];
20
+
21
+ // Inject INDEX.md
22
+ const indexPath = path.join(baseDir, 'INDEX.md');
23
+ if (fs.existsSync(indexPath)) {
24
+ const content = fs.readFileSync(indexPath, 'utf8').trim();
25
+ output.push(`[Mindlore INDEX]\n${content}`);
26
+ }
27
+
28
+ // Inject latest delta
29
+ const diaryDir = path.join(baseDir, 'diary');
30
+ const latestDelta = getLatestDelta(diaryDir);
31
+ if (latestDelta) {
32
+ const deltaContent = fs.readFileSync(latestDelta, 'utf8').trim();
33
+ const deltaName = path.basename(latestDelta);
34
+ output.push(`[Mindlore Delta: ${deltaName}]\n${deltaContent}`);
35
+ }
36
+
37
+ if (output.length > 0) {
38
+ process.stderr.write(output.join('\n\n') + '\n');
39
+ }
40
+ }
41
+
42
+ main();
package/package.json ADDED
@@ -0,0 +1,55 @@
1
+ {
2
+ "name": "mindlore",
3
+ "version": "0.0.1",
4
+ "description": "AI-native knowledge system for Claude Code",
5
+ "type": "commonjs",
6
+ "bin": {
7
+ "mindlore": "scripts/init.cjs"
8
+ },
9
+ "scripts": {
10
+ "test": "jest --config jest.config.cjs",
11
+ "lint": "eslint -c eslint.config.cjs scripts/ hooks/ tests/",
12
+ "health": "node scripts/mindlore-health-check.cjs",
13
+ "index": "node scripts/mindlore-fts5-index.cjs",
14
+ "search": "node scripts/mindlore-fts5-search.cjs"
15
+ },
16
+ "keywords": [
17
+ "claude-code",
18
+ "knowledge-base",
19
+ "second-brain",
20
+ "ai-native",
21
+ "fts5",
22
+ "karpathy",
23
+ "llm",
24
+ "wiki"
25
+ ],
26
+ "author": "omrfc",
27
+ "license": "MIT",
28
+ "repository": {
29
+ "type": "git",
30
+ "url": "git+https://github.com/mindlore/mindlore.git"
31
+ },
32
+ "bugs": {
33
+ "url": "https://github.com/mindlore/mindlore/issues"
34
+ },
35
+ "homepage": "https://github.com/mindlore/mindlore#readme",
36
+ "engines": {
37
+ "node": ">=20.0.0"
38
+ },
39
+ "dependencies": {
40
+ "better-sqlite3": "^11.0.0"
41
+ },
42
+ "devDependencies": {
43
+ "eslint": "^9.0.0",
44
+ "jest": "^29.7.0",
45
+ "globals": "^15.0.0"
46
+ },
47
+ "files": [
48
+ "scripts/",
49
+ "hooks/",
50
+ "skills/",
51
+ "templates/",
52
+ "SCHEMA.md",
53
+ "plugin.json"
54
+ ]
55
+ }
package/plugin.json ADDED
@@ -0,0 +1,47 @@
1
+ {
2
+ "name": "mindlore",
3
+ "description": "AI-native knowledge system for Claude Code. Persistent, searchable, evolving knowledge base with FTS5.",
4
+ "version": "0.0.1",
5
+ "skills": [
6
+ {
7
+ "name": "mindlore-ingest",
8
+ "path": "skills/mindlore-ingest/SKILL.md",
9
+ "description": "Add new knowledge sources (URL, text, file, PDF, GitHub repo)"
10
+ },
11
+ {
12
+ "name": "mindlore-health",
13
+ "path": "skills/mindlore-health/SKILL.md",
14
+ "description": "Run 16-point structural health check on .mindlore/ knowledge base"
15
+ }
16
+ ],
17
+ "hooks": [
18
+ {
19
+ "event": "SessionStart",
20
+ "script": "hooks/mindlore-session-focus.cjs"
21
+ },
22
+ {
23
+ "event": "UserPromptSubmit",
24
+ "script": "hooks/mindlore-search.cjs"
25
+ },
26
+ {
27
+ "event": "FileChanged",
28
+ "script": "hooks/mindlore-index.cjs"
29
+ },
30
+ {
31
+ "event": "FileChanged",
32
+ "script": "hooks/mindlore-fts5-sync.cjs"
33
+ },
34
+ {
35
+ "event": "SessionEnd",
36
+ "script": "hooks/mindlore-session-end.cjs"
37
+ },
38
+ {
39
+ "event": "PreCompact",
40
+ "script": "hooks/mindlore-pre-compact.cjs"
41
+ },
42
+ {
43
+ "event": "PostCompact",
44
+ "script": "hooks/mindlore-post-compact.cjs"
45
+ }
46
+ ]
47
+ }