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,320 @@
1
+ #!/usr/bin/env node
2
+ 'use strict';
3
+
4
+ /**
5
+ * mindlore-health-check — 16-point structural health check for .mindlore/
6
+ *
7
+ * Usage: node scripts/mindlore-health-check.cjs [path-to-mindlore-dir]
8
+ *
9
+ * Exit codes: 0 = healthy, 1 = issues found
10
+ */
11
+
12
+ const fs = require('fs');
13
+ const path = require('path');
14
+
15
+ // ── Constants ──────────────────────────────────────────────────────────
16
+
17
+ const { DIRECTORIES, TYPE_TO_DIR } = require('./lib/constants.cjs');
18
+
19
+ // ── Helpers ────────────────────────────────────────────────────────────
20
+
21
+ function parseFrontmatter(content) {
22
+ const match = content.match(/^---\r?\n([\s\S]*?)\r?\n---/);
23
+ if (!match) return null;
24
+
25
+ const fm = {};
26
+ const lines = match[1].split('\n');
27
+ for (const line of lines) {
28
+ const colonIdx = line.indexOf(':');
29
+ if (colonIdx === -1) continue;
30
+ const key = line.slice(0, colonIdx).trim();
31
+ let value = line.slice(colonIdx + 1).trim();
32
+ // Handle arrays
33
+ if (value.startsWith('[') && value.endsWith(']')) {
34
+ value = value
35
+ .slice(1, -1)
36
+ .split(',')
37
+ .map((s) => s.trim());
38
+ }
39
+ fm[key] = value;
40
+ }
41
+ return fm;
42
+ }
43
+
44
+ // Health check needs ALL .md files (no skip), so pass empty set
45
+ function getAllMdFiles(dir) {
46
+ return require('../hooks/lib/mindlore-common.cjs').getAllMdFiles(dir, new Set());
47
+ }
48
+
49
+ // ── Checks ─────────────────────────────────────────────────────────────
50
+
51
+ class HealthChecker {
52
+ constructor(baseDir) {
53
+ this.baseDir = baseDir;
54
+ this.results = [];
55
+ this.passed = 0;
56
+ this.failed = 0;
57
+ this.warnings = 0;
58
+ }
59
+
60
+ check(name, fn) {
61
+ try {
62
+ const result = fn();
63
+ if (result.ok) {
64
+ this.passed++;
65
+ this.results.push({ name, status: 'PASS', detail: result.detail });
66
+ } else if (result.warn) {
67
+ this.warnings++;
68
+ this.results.push({ name, status: 'WARN', detail: result.detail });
69
+ } else {
70
+ this.failed++;
71
+ this.results.push({ name, status: 'FAIL', detail: result.detail });
72
+ }
73
+ } catch (err) {
74
+ this.failed++;
75
+ this.results.push({ name, status: 'FAIL', detail: err.message });
76
+ }
77
+ }
78
+
79
+ // Check 1-9: Directory existence
80
+ checkDirectories() {
81
+ for (const dir of DIRECTORIES) {
82
+ this.check(`Directory: ${dir}/`, () => {
83
+ const dirPath = path.join(this.baseDir, dir);
84
+ if (fs.existsSync(dirPath)) {
85
+ return { ok: true, detail: 'exists' };
86
+ }
87
+ return { ok: false, detail: 'missing' };
88
+ });
89
+ }
90
+ }
91
+
92
+ // Check 10: SCHEMA.md parseable
93
+ checkSchema() {
94
+ this.check('SCHEMA.md', () => {
95
+ const schemaPath = path.join(this.baseDir, 'SCHEMA.md');
96
+ if (!fs.existsSync(schemaPath)) {
97
+ return { ok: false, detail: 'missing' };
98
+ }
99
+ const content = fs.readFileSync(schemaPath, 'utf8');
100
+ if (content.length < 100) {
101
+ return { ok: false, detail: 'too short (corrupted?)' };
102
+ }
103
+ if (!content.includes('## 1. Identity')) {
104
+ return { warn: true, detail: 'may be outdated (missing Identity section)' };
105
+ }
106
+ return { ok: true, detail: `${content.split('\n').length} lines` };
107
+ });
108
+ }
109
+
110
+ // Check 11: INDEX.md format
111
+ checkIndex() {
112
+ this.check('INDEX.md format', () => {
113
+ const indexPath = path.join(this.baseDir, 'INDEX.md');
114
+ if (!fs.existsSync(indexPath)) {
115
+ return { ok: false, detail: 'missing' };
116
+ }
117
+ const content = fs.readFileSync(indexPath, 'utf8');
118
+ const lines = content.trim().split('\n');
119
+ if (lines.length > 30) {
120
+ return {
121
+ warn: true,
122
+ detail: `${lines.length} lines (should be ~15-20, consider trimming)`,
123
+ };
124
+ }
125
+ return { ok: true, detail: `${lines.length} lines` };
126
+ });
127
+ }
128
+
129
+ // Check 12: Database integrity
130
+ checkDatabase() {
131
+ this.check('mindlore.db FTS5', () => {
132
+ const dbPath = path.join(this.baseDir, 'mindlore.db');
133
+ if (!fs.existsSync(dbPath)) {
134
+ return { ok: false, detail: 'database missing' };
135
+ }
136
+
137
+ let Database;
138
+ try {
139
+ Database = require('better-sqlite3');
140
+ } catch (_err) {
141
+ return { warn: true, detail: 'better-sqlite3 not available, cannot verify' };
142
+ }
143
+
144
+ const db = new Database(dbPath, { readonly: true });
145
+ try {
146
+ const result = db.prepare('SELECT count(*) as cnt FROM mindlore_fts').get();
147
+ const hashResult = db
148
+ .prepare('SELECT count(*) as cnt FROM file_hashes')
149
+ .get();
150
+ return {
151
+ ok: true,
152
+ detail: `${result.cnt} indexed, ${hashResult.cnt} hashes`,
153
+ };
154
+ } catch (err) {
155
+ return { ok: false, detail: `FTS5 error: ${err.message}` };
156
+ } finally {
157
+ db.close();
158
+ }
159
+ });
160
+ }
161
+
162
+ // Check 13: Orphan files (in .mindlore/ but not in FTS5)
163
+ checkOrphans() {
164
+ this.check('Orphan files', () => {
165
+ const dbPath = path.join(this.baseDir, 'mindlore.db');
166
+ if (!fs.existsSync(dbPath)) {
167
+ return { warn: true, detail: 'no database, cannot check' };
168
+ }
169
+
170
+ let Database;
171
+ try {
172
+ Database = require('better-sqlite3');
173
+ } catch (_err) {
174
+ return { warn: true, detail: 'better-sqlite3 not available' };
175
+ }
176
+
177
+ const mdFiles = getAllMdFiles(this.baseDir).filter(
178
+ (f) =>
179
+ !f.endsWith('INDEX.md') &&
180
+ !f.endsWith('SCHEMA.md') &&
181
+ !f.endsWith('log.md')
182
+ );
183
+
184
+ const db = new Database(dbPath, { readonly: true });
185
+ try {
186
+ const indexed = new Set();
187
+ const rows = db.prepare('SELECT path FROM file_hashes').all();
188
+ for (const row of rows) {
189
+ indexed.add(path.resolve(row.path));
190
+ }
191
+
192
+ const orphans = mdFiles.filter(
193
+ (f) => !indexed.has(path.resolve(f))
194
+ );
195
+
196
+ if (orphans.length === 0) {
197
+ return { ok: true, detail: 'no orphans' };
198
+ }
199
+ if (orphans.length <= 3) {
200
+ return {
201
+ warn: true,
202
+ detail: `${orphans.length} unindexed: ${orphans.map((f) => path.basename(f)).join(', ')}`,
203
+ };
204
+ }
205
+ return {
206
+ ok: false,
207
+ detail: `${orphans.length} unindexed files — run: npm run index`,
208
+ };
209
+ } finally {
210
+ db.close();
211
+ }
212
+ });
213
+ }
214
+
215
+ // Check 14-16: Frontmatter validation
216
+ checkFrontmatter() {
217
+ this.check('Frontmatter: slug + type', () => {
218
+ const mdFiles = getAllMdFiles(this.baseDir).filter(
219
+ (f) =>
220
+ !f.endsWith('INDEX.md') &&
221
+ !f.endsWith('SCHEMA.md') &&
222
+ !f.endsWith('log.md')
223
+ );
224
+
225
+ let missingSlug = 0;
226
+ let missingType = 0;
227
+ let wrongDir = 0;
228
+
229
+ for (const file of mdFiles) {
230
+ const content = fs.readFileSync(file, 'utf8').replace(/\r\n/g, '\n');
231
+ const fm = parseFrontmatter(content);
232
+
233
+ if (!fm) {
234
+ missingSlug++;
235
+ missingType++;
236
+ continue;
237
+ }
238
+
239
+ if (!fm.slug) missingSlug++;
240
+ if (!fm.type) {
241
+ missingType++;
242
+ continue;
243
+ }
244
+
245
+ // Check type-directory match
246
+ const expectedDir = TYPE_TO_DIR[fm.type];
247
+ if (expectedDir) {
248
+ const parentDir = path.basename(path.dirname(file));
249
+ if (parentDir !== expectedDir) {
250
+ wrongDir++;
251
+ }
252
+ }
253
+ }
254
+
255
+ const issues = [];
256
+ if (missingSlug > 0) issues.push(`${missingSlug} missing slug`);
257
+ if (missingType > 0) issues.push(`${missingType} missing type`);
258
+ if (wrongDir > 0) issues.push(`${wrongDir} type-dir mismatch`);
259
+
260
+ if (issues.length === 0) {
261
+ return {
262
+ ok: true,
263
+ detail: `${mdFiles.length} files validated`,
264
+ };
265
+ }
266
+ return {
267
+ ok: wrongDir > 0 ? false : undefined,
268
+ warn: wrongDir === 0,
269
+ detail: issues.join(', '),
270
+ };
271
+ });
272
+ }
273
+
274
+ // ── Run all ────────────────────────────────────────────────────────
275
+
276
+ run() {
277
+ this.checkDirectories();
278
+ this.checkSchema();
279
+ this.checkIndex();
280
+ this.checkDatabase();
281
+ this.checkOrphans();
282
+ this.checkFrontmatter();
283
+ return this;
284
+ }
285
+
286
+ report() {
287
+ console.log('\n Mindlore Health Check\n');
288
+
289
+ for (const r of this.results) {
290
+ const icon =
291
+ r.status === 'PASS' ? '+' : r.status === 'WARN' ? '~' : '-';
292
+ console.log(` [${icon}] ${r.name}: ${r.detail}`);
293
+ }
294
+
295
+ const total = this.passed + this.failed + this.warnings;
296
+ console.log(
297
+ `\n Score: ${this.passed}/${total} passed, ${this.warnings} warnings, ${this.failed} failed\n`
298
+ );
299
+
300
+ return this.failed === 0;
301
+ }
302
+ }
303
+
304
+ // ── Main ───────────────────────────────────────────────────────────────
305
+
306
+ function main() {
307
+ const baseDir = process.argv[2] || path.join(process.cwd(), '.mindlore');
308
+
309
+ if (!fs.existsSync(baseDir)) {
310
+ console.error(` .mindlore/ not found at: ${baseDir}`);
311
+ console.error(' Run: npx mindlore init');
312
+ process.exit(1);
313
+ }
314
+
315
+ const checker = new HealthChecker(baseDir);
316
+ const healthy = checker.run().report();
317
+ process.exit(healthy ? 0 : 1);
318
+ }
319
+
320
+ main();
@@ -0,0 +1,55 @@
1
+ ---
2
+ name: mindlore-health
3
+ description: Run 16-point structural health check on .mindlore/ knowledge base
4
+ effort: low
5
+ allowed-tools: [Bash, Read]
6
+ ---
7
+
8
+ # /mindlore-health
9
+
10
+ Run the 16-point structural health check on the `.mindlore/` knowledge base.
11
+
12
+ ## Trigger
13
+
14
+ User says "health check", "mindlore health", "bilgi sistemi kontrol", "saglik kontrolu".
15
+
16
+ ## Execution
17
+
18
+ 1. Run the health check script:
19
+ ```bash
20
+ node scripts/mindlore-health-check.cjs
21
+ ```
22
+
23
+ 2. Read the output and provide LLM interpretation:
24
+ - Explain any failures or warnings
25
+ - Suggest specific fixes for each issue
26
+ - Prioritize: FAIL > WARN > PASS
27
+
28
+ ## 16 Checks
29
+
30
+ | # | Check | What It Validates |
31
+ |---|-------|-------------------|
32
+ | 1-9 | Directory existence | All 9 directories under .mindlore/ exist |
33
+ | 10 | SCHEMA.md | File exists and is parseable |
34
+ | 11 | INDEX.md format | File exists, ~15-20 lines (not bloated) |
35
+ | 12 | mindlore.db FTS5 | Database exists, FTS5 table queryable |
36
+ | 13 | Orphan files | All .md files are indexed in FTS5 |
37
+ | 14-16 | Frontmatter | slug + type present, type matches directory |
38
+
39
+ ## Common Fixes
40
+
41
+ | Issue | Fix |
42
+ |-------|-----|
43
+ | Missing directory | `npx mindlore init` (idempotent) |
44
+ | Database missing | `npx mindlore init` |
45
+ | Orphan files | `npm run index` (full re-index) |
46
+ | INDEX.md too long | Trim to ~15-20 lines, move details to domains/ |
47
+ | Type-dir mismatch | Move file to correct directory or fix frontmatter type |
48
+ | Missing frontmatter | Add YAML frontmatter with slug and type |
49
+
50
+ ## Output Format
51
+
52
+ Report results clearly:
53
+ - Total score: X/16 passed
54
+ - List any FAIL or WARN items with specific fix commands
55
+ - If all pass: "Knowledge base is healthy"
@@ -0,0 +1,102 @@
1
+ ---
2
+ name: mindlore-ingest
3
+ description: Add new knowledge sources to .mindlore/ (URL, text, file, PDF, GitHub repo)
4
+ effort: medium
5
+ allowed-tools: [Read, Write, Edit, Bash, Grep, Glob, Agent, WebFetch]
6
+ ---
7
+
8
+ # /mindlore-ingest
9
+
10
+ Add a new knowledge source to the `.mindlore/` knowledge base.
11
+
12
+ ## Trigger
13
+
14
+ User shares a URL, text, file, or says "kaynak ekle", "source ingest", "bu linki kaydet", "knowledge ingest".
15
+
16
+ ## Modes
17
+
18
+ ### URL Mode
19
+ 1. Extract content from URL:
20
+ - If `markitdown` is available: `markitdown <url>` (best quality, zero tokens)
21
+ - Else: use `WebFetch` or `ctx_fetch_and_index`
22
+ 2. Save raw capture to `.mindlore/raw/` with frontmatter:
23
+ ```yaml
24
+ ---
25
+ slug: source-name-kebab
26
+ type: raw
27
+ source_url: https://...
28
+ date_captured: YYYY-MM-DD
29
+ tags: [tag1, tag2]
30
+ ---
31
+ ```
32
+ 3. Summarize into `.mindlore/sources/` with full frontmatter:
33
+ ```yaml
34
+ ---
35
+ slug: source-name-kebab
36
+ type: source
37
+ title: Human Readable Title
38
+ source_url: https://...
39
+ source_type: github-repo|blog|docs|video|x-thread
40
+ date_captured: YYYY-MM-DD
41
+ tags: [tag1, tag2]
42
+ quality: high|medium|low
43
+ ingested: true
44
+ ---
45
+ ```
46
+ 4. Update relevant domain page(s) in `.mindlore/domains/` (max 2)
47
+ 5. Update `.mindlore/INDEX.md` stats line
48
+ 6. Append entry to `.mindlore/log.md`
49
+ 7. Run FTS5 re-index: `npm run index`
50
+
51
+ ### Text Mode
52
+ 1. User pastes text directly
53
+ 2. Save to `.mindlore/raw/` with `source_type: text-paste`
54
+ 3. Follow steps 3-7 from URL mode
55
+
56
+ ### PDF Mode
57
+ 1. Read PDF with CC Read tool: `Read(file_path, pages: "1-5")` (max 20 pages/request)
58
+ 2. **Do NOT use markitdown for PDF** — quality is poor
59
+ 3. Save extracted text to `.mindlore/raw/`
60
+ 4. Follow steps 3-7 from URL mode
61
+ 5. For v0.3+: Marker CLI or Chandra as optional alternatives
62
+
63
+ ### File Mode
64
+ 1. Read the file with `Read` tool
65
+ 2. Save to `.mindlore/raw/`
66
+ 3. Follow steps 3-7 from URL mode
67
+
68
+ ## Source Summary Format
69
+
70
+ The sources/ file should contain:
71
+ - **1-paragraph summary** of what the source is about
72
+ - **Key takeaways** (3-7 bullet points)
73
+ - **Relevance to project** (why this matters)
74
+ - **Related** links to other sources/domains in .mindlore/
75
+
76
+ ## Quality Assessment
77
+
78
+ - `high`: Primary source, authoritative, detailed, directly relevant
79
+ - `medium`: Useful but secondary, partial coverage, or tangentially relevant
80
+ - `low`: Reference only, outdated, or low signal-to-noise
81
+
82
+ ## Domain Update Rules
83
+
84
+ - Read the relevant domain page first
85
+ - Add new information under the appropriate section
86
+ - Add backlink to the source in the domain's references
87
+ - Update max 2 domain pages per ingest (prevent scope creep)
88
+ - If no relevant domain exists, note it — don't create one during ingest
89
+
90
+ ## INDEX.md Update
91
+
92
+ Only update the stats line: increment source count and total count.
93
+ ```
94
+ N source, N analysis, N total
95
+ ```
96
+
97
+ ## Post-Ingest Verification
98
+
99
+ After ingest, run health check:
100
+ ```bash
101
+ node scripts/mindlore-health-check.cjs
102
+ ```
@@ -0,0 +1,12 @@
1
+ # Mindlore Index
2
+
3
+ ## Domains
4
+
5
+ ### Entities
6
+
7
+ ### Concepts
8
+
9
+ ## Recent
10
+
11
+ ## Stats
12
+ 0 source, 0 analysis, 0 total
@@ -0,0 +1,4 @@
1
+ # Mindlore Operation Log
2
+
3
+ | Date | Operation | Details |
4
+ |------|-----------|---------|