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 +21 -0
- package/README.md +128 -0
- package/SCHEMA.md +221 -0
- package/hooks/lib/mindlore-common.cjs +90 -0
- package/hooks/mindlore-fts5-sync.cjs +91 -0
- package/hooks/mindlore-index.cjs +96 -0
- package/hooks/mindlore-post-compact.cjs +46 -0
- package/hooks/mindlore-pre-compact.cjs +48 -0
- package/hooks/mindlore-search.cjs +133 -0
- package/hooks/mindlore-session-end.cjs +68 -0
- package/hooks/mindlore-session-focus.cjs +42 -0
- package/package.json +55 -0
- package/plugin.json +47 -0
- package/scripts/init.cjs +335 -0
- package/scripts/lib/constants.cjs +49 -0
- package/scripts/mindlore-fts5-index.cjs +115 -0
- package/scripts/mindlore-fts5-search.cjs +122 -0
- package/scripts/mindlore-health-check.cjs +320 -0
- package/skills/mindlore-health/SKILL.md +55 -0
- package/skills/mindlore-ingest/SKILL.md +102 -0
- package/templates/INDEX.md +12 -0
- package/templates/log.md +4 -0
|
@@ -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
|
+
}
|