mindlore 0.1.0 → 0.2.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 +13 -5
- package/SCHEMA.md +60 -4
- package/dist/scripts/init.d.ts +10 -0
- package/dist/scripts/init.d.ts.map +1 -0
- package/dist/scripts/init.js +375 -0
- package/dist/scripts/init.js.map +1 -0
- package/dist/scripts/lib/constants.d.ts +29 -0
- package/dist/scripts/lib/constants.d.ts.map +1 -0
- package/dist/scripts/lib/constants.js +58 -0
- package/dist/scripts/lib/constants.js.map +1 -0
- package/dist/scripts/mindlore-fts5-index.d.ts +9 -0
- package/dist/scripts/mindlore-fts5-index.d.ts.map +1 -0
- package/dist/scripts/mindlore-fts5-index.js +89 -0
- package/dist/scripts/mindlore-fts5-index.js.map +1 -0
- package/dist/scripts/mindlore-fts5-search.d.ts +10 -0
- package/dist/scripts/mindlore-fts5-search.d.ts.map +1 -0
- package/dist/scripts/mindlore-fts5-search.js +108 -0
- package/dist/scripts/mindlore-fts5-search.js.map +1 -0
- package/dist/scripts/mindlore-health-check.d.ts +10 -0
- package/dist/scripts/mindlore-health-check.d.ts.map +1 -0
- package/dist/scripts/mindlore-health-check.js +337 -0
- package/dist/scripts/mindlore-health-check.js.map +1 -0
- package/dist/scripts/uninstall.d.ts +10 -0
- package/dist/scripts/uninstall.d.ts.map +1 -0
- package/dist/scripts/uninstall.js +143 -0
- package/dist/scripts/uninstall.js.map +1 -0
- package/dist/tests/compounding.test.d.ts +8 -0
- package/dist/tests/compounding.test.d.ts.map +1 -0
- package/dist/tests/compounding.test.js +51 -0
- package/dist/tests/compounding.test.js.map +1 -0
- package/dist/tests/decision.test.d.ts +2 -0
- package/dist/tests/decision.test.d.ts.map +1 -0
- package/dist/tests/decision.test.js +61 -0
- package/dist/tests/decision.test.js.map +1 -0
- package/dist/tests/dedup.test.d.ts +2 -0
- package/dist/tests/dedup.test.d.ts.map +1 -0
- package/dist/tests/dedup.test.js +74 -0
- package/dist/tests/dedup.test.js.map +1 -0
- package/dist/tests/frontmatter.test.d.ts +2 -0
- package/dist/tests/frontmatter.test.d.ts.map +1 -0
- package/dist/tests/frontmatter.test.js +90 -0
- package/dist/tests/frontmatter.test.js.map +1 -0
- package/dist/tests/fts5.test.d.ts +2 -0
- package/dist/tests/fts5.test.d.ts.map +1 -0
- package/dist/tests/fts5.test.js +95 -0
- package/dist/tests/fts5.test.js.map +1 -0
- package/dist/tests/helpers/db.d.ts +7 -0
- package/dist/tests/helpers/db.d.ts.map +1 -0
- package/dist/tests/helpers/db.js +46 -0
- package/dist/tests/helpers/db.js.map +1 -0
- package/dist/tests/hook-smoke.test.d.ts +2 -0
- package/dist/tests/hook-smoke.test.d.ts.map +1 -0
- package/dist/tests/hook-smoke.test.js +58 -0
- package/dist/tests/hook-smoke.test.js.map +1 -0
- package/dist/tests/init.test.d.ts +2 -0
- package/dist/tests/init.test.d.ts.map +1 -0
- package/dist/tests/init.test.js +85 -0
- package/dist/tests/init.test.js.map +1 -0
- package/dist/tests/log.test.d.ts +2 -0
- package/dist/tests/log.test.d.ts.map +1 -0
- package/dist/tests/log.test.js +68 -0
- package/dist/tests/log.test.js.map +1 -0
- package/dist/tests/read-guard.test.d.ts +2 -0
- package/dist/tests/read-guard.test.d.ts.map +1 -0
- package/dist/tests/read-guard.test.js +69 -0
- package/dist/tests/read-guard.test.js.map +1 -0
- package/dist/tests/search-hook.test.d.ts +2 -0
- package/dist/tests/search-hook.test.d.ts.map +1 -0
- package/dist/tests/search-hook.test.js +108 -0
- package/dist/tests/search-hook.test.js.map +1 -0
- package/dist/tests/session-focus.test.d.ts +2 -0
- package/dist/tests/session-focus.test.d.ts.map +1 -0
- package/dist/tests/session-focus.test.js +71 -0
- package/dist/tests/session-focus.test.js.map +1 -0
- package/dist/tests/uninstall.test.d.ts +2 -0
- package/dist/tests/uninstall.test.d.ts.map +1 -0
- package/dist/tests/uninstall.test.js +98 -0
- package/dist/tests/uninstall.test.js.map +1 -0
- package/hooks/lib/mindlore-common.cjs +36 -2
- package/hooks/mindlore-decision-detector.cjs +51 -0
- package/hooks/mindlore-fts5-sync.cjs +4 -18
- package/hooks/mindlore-index.cjs +4 -18
- package/hooks/mindlore-read-guard.cjs +62 -0
- package/hooks/mindlore-search.cjs +5 -18
- package/hooks/mindlore-session-end.cjs +74 -8
- package/package.json +19 -7
- package/plugin.json +26 -2
- package/skills/mindlore-decide/SKILL.md +71 -0
- package/skills/mindlore-ingest/SKILL.md +13 -2
- package/skills/mindlore-log/SKILL.md +79 -0
- package/skills/mindlore-query/SKILL.md +125 -0
- package/templates/SCHEMA.md +60 -4
- package/scripts/init.cjs +0 -450
- package/scripts/lib/constants.cjs +0 -49
- package/scripts/mindlore-fts5-index.cjs +0 -112
- package/scripts/mindlore-fts5-search.cjs +0 -119
- package/scripts/mindlore-health-check.cjs +0 -336
- package/scripts/uninstall.cjs +0 -186
|
@@ -1,119 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
'use strict';
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* mindlore-fts5-search — Search .mindlore/ knowledge base via FTS5.
|
|
6
|
-
*
|
|
7
|
-
* Usage: node scripts/mindlore-fts5-search.cjs "query" [path-to-mindlore-dir]
|
|
8
|
-
*
|
|
9
|
-
* Returns top 3 results ranked by BM25 with file path and snippet.
|
|
10
|
-
*/
|
|
11
|
-
|
|
12
|
-
const fs = require('fs');
|
|
13
|
-
const path = require('path');
|
|
14
|
-
|
|
15
|
-
const { DB_NAME } = require('./lib/constants.cjs');
|
|
16
|
-
const { openDatabase } = require('../hooks/lib/mindlore-common.cjs');
|
|
17
|
-
const MAX_RESULTS = 3;
|
|
18
|
-
|
|
19
|
-
// ── Helpers ────────────────────────────────────────────────────────────
|
|
20
|
-
|
|
21
|
-
function extractHeadings(content, maxHeadings) {
|
|
22
|
-
const lines = content.split('\n');
|
|
23
|
-
const headings = [];
|
|
24
|
-
for (const line of lines) {
|
|
25
|
-
if (line.startsWith('#')) {
|
|
26
|
-
headings.push(line.replace(/^#+\s*/, '').trim());
|
|
27
|
-
if (headings.length >= maxHeadings) break;
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
return headings;
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
// ── Main ───────────────────────────────────────────────────────────────
|
|
34
|
-
|
|
35
|
-
function main() {
|
|
36
|
-
const query = process.argv[2];
|
|
37
|
-
if (!query) {
|
|
38
|
-
console.error('Usage: node mindlore-fts5-search.cjs "query" [mindlore-dir]');
|
|
39
|
-
process.exit(1);
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
const baseDir = process.argv[3] || path.join(process.cwd(), '.mindlore');
|
|
43
|
-
const dbPath = path.join(baseDir, DB_NAME);
|
|
44
|
-
|
|
45
|
-
if (!fs.existsSync(dbPath)) {
|
|
46
|
-
console.error(' Database not found. Run: npx mindlore init && npm run index');
|
|
47
|
-
process.exit(1);
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
const db = openDatabase(dbPath, { readonly: true });
|
|
51
|
-
if (!db) {
|
|
52
|
-
console.error(' better-sqlite3 not installed.');
|
|
53
|
-
process.exit(1);
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
try {
|
|
57
|
-
// Sanitize query for FTS5 (escape special chars)
|
|
58
|
-
const sanitized = query.replace(/['"(){}[\]*:^~!]/g, ' ').trim();
|
|
59
|
-
if (!sanitized) {
|
|
60
|
-
console.log(' No valid search terms.');
|
|
61
|
-
process.exit(0);
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
const results = db
|
|
65
|
-
.prepare(
|
|
66
|
-
`SELECT path, snippet(mindlore_fts, 1, '>>>', '<<<', '...', 40) as snippet,
|
|
67
|
-
rank
|
|
68
|
-
FROM mindlore_fts
|
|
69
|
-
WHERE mindlore_fts MATCH ?
|
|
70
|
-
ORDER BY rank
|
|
71
|
-
LIMIT ?`
|
|
72
|
-
)
|
|
73
|
-
.all(sanitized, MAX_RESULTS);
|
|
74
|
-
|
|
75
|
-
if (results.length === 0) {
|
|
76
|
-
console.log(` No results for: "${query}"`);
|
|
77
|
-
process.exit(0);
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
console.log(`\n Mindlore Search: "${query}" (${results.length} results)\n`);
|
|
81
|
-
|
|
82
|
-
for (let i = 0; i < results.length; i++) {
|
|
83
|
-
const r = results[i];
|
|
84
|
-
const relativePath = path.relative(baseDir, r.path);
|
|
85
|
-
const fileName = path.basename(r.path, '.md');
|
|
86
|
-
|
|
87
|
-
// Try to get headings from the actual file
|
|
88
|
-
let headings = [];
|
|
89
|
-
if (fs.existsSync(r.path)) {
|
|
90
|
-
const content = fs.readFileSync(r.path, 'utf8');
|
|
91
|
-
headings = extractHeadings(content, 2);
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
console.log(` ${i + 1}. ${relativePath}`);
|
|
95
|
-
if (headings.length > 0) {
|
|
96
|
-
console.log(` ${headings.join(' > ')}`);
|
|
97
|
-
}
|
|
98
|
-
console.log(` ${r.snippet || fileName}`);
|
|
99
|
-
console.log('');
|
|
100
|
-
}
|
|
101
|
-
} catch (err) {
|
|
102
|
-
// FTS5 query syntax error — try simpler query
|
|
103
|
-
if (err.message.includes('fts5')) {
|
|
104
|
-
const words = query.split(/\s+/).filter((w) => w.length >= 2);
|
|
105
|
-
if (words.length > 0) {
|
|
106
|
-
console.error(` Search syntax error. Try simpler terms: ${words.join(' ')}`);
|
|
107
|
-
} else {
|
|
108
|
-
console.error(` Search error: ${err.message}`);
|
|
109
|
-
}
|
|
110
|
-
} else {
|
|
111
|
-
console.error(` Error: ${err.message}`);
|
|
112
|
-
}
|
|
113
|
-
process.exit(1);
|
|
114
|
-
} finally {
|
|
115
|
-
db.close();
|
|
116
|
-
}
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
main();
|
|
@@ -1,336 +0,0 @@
|
|
|
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 > 60) {
|
|
120
|
-
return {
|
|
121
|
-
warn: true,
|
|
122
|
-
detail: `${lines.length} lines (should be ~15-60, 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
|
-
|
|
151
|
-
// Verify 7-column schema (slug, description, type, category, title, content + path)
|
|
152
|
-
let schemaOk = true;
|
|
153
|
-
try {
|
|
154
|
-
db.prepare('SELECT slug, description, category, title FROM mindlore_fts LIMIT 0').run();
|
|
155
|
-
} catch (_err) {
|
|
156
|
-
schemaOk = false;
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
if (!schemaOk) {
|
|
160
|
-
return {
|
|
161
|
-
warn: true,
|
|
162
|
-
detail: `${result.cnt} indexed, ${hashResult.cnt} hashes — OLD SCHEMA (run: npx mindlore init to upgrade)`,
|
|
163
|
-
};
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
return {
|
|
167
|
-
ok: true,
|
|
168
|
-
detail: `${result.cnt} indexed, ${hashResult.cnt} hashes, 7-col schema`,
|
|
169
|
-
};
|
|
170
|
-
} catch (err) {
|
|
171
|
-
return { ok: false, detail: `FTS5 error: ${err.message}` };
|
|
172
|
-
} finally {
|
|
173
|
-
db.close();
|
|
174
|
-
}
|
|
175
|
-
});
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
// Check 13: Orphan files (in .mindlore/ but not in FTS5)
|
|
179
|
-
checkOrphans() {
|
|
180
|
-
this.check('Orphan files', () => {
|
|
181
|
-
const dbPath = path.join(this.baseDir, 'mindlore.db');
|
|
182
|
-
if (!fs.existsSync(dbPath)) {
|
|
183
|
-
return { warn: true, detail: 'no database, cannot check' };
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
let Database;
|
|
187
|
-
try {
|
|
188
|
-
Database = require('better-sqlite3');
|
|
189
|
-
} catch (_err) {
|
|
190
|
-
return { warn: true, detail: 'better-sqlite3 not available' };
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
const mdFiles = getAllMdFiles(this.baseDir).filter(
|
|
194
|
-
(f) =>
|
|
195
|
-
!f.endsWith('INDEX.md') &&
|
|
196
|
-
!f.endsWith('SCHEMA.md') &&
|
|
197
|
-
!f.endsWith('log.md')
|
|
198
|
-
);
|
|
199
|
-
|
|
200
|
-
const db = new Database(dbPath, { readonly: true });
|
|
201
|
-
try {
|
|
202
|
-
const indexed = new Set();
|
|
203
|
-
const rows = db.prepare('SELECT path FROM file_hashes').all();
|
|
204
|
-
for (const row of rows) {
|
|
205
|
-
indexed.add(path.resolve(row.path));
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
const orphans = mdFiles.filter(
|
|
209
|
-
(f) => !indexed.has(path.resolve(f))
|
|
210
|
-
);
|
|
211
|
-
|
|
212
|
-
if (orphans.length === 0) {
|
|
213
|
-
return { ok: true, detail: 'no orphans' };
|
|
214
|
-
}
|
|
215
|
-
if (orphans.length <= 3) {
|
|
216
|
-
return {
|
|
217
|
-
warn: true,
|
|
218
|
-
detail: `${orphans.length} unindexed: ${orphans.map((f) => path.basename(f)).join(', ')}`,
|
|
219
|
-
};
|
|
220
|
-
}
|
|
221
|
-
return {
|
|
222
|
-
ok: false,
|
|
223
|
-
detail: `${orphans.length} unindexed files — run: npm run index`,
|
|
224
|
-
};
|
|
225
|
-
} finally {
|
|
226
|
-
db.close();
|
|
227
|
-
}
|
|
228
|
-
});
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
// Check 14-16: Frontmatter validation
|
|
232
|
-
checkFrontmatter() {
|
|
233
|
-
this.check('Frontmatter: slug + type', () => {
|
|
234
|
-
const mdFiles = getAllMdFiles(this.baseDir).filter(
|
|
235
|
-
(f) =>
|
|
236
|
-
!f.endsWith('INDEX.md') &&
|
|
237
|
-
!f.endsWith('SCHEMA.md') &&
|
|
238
|
-
!f.endsWith('log.md')
|
|
239
|
-
);
|
|
240
|
-
|
|
241
|
-
let missingSlug = 0;
|
|
242
|
-
let missingType = 0;
|
|
243
|
-
let wrongDir = 0;
|
|
244
|
-
|
|
245
|
-
for (const file of mdFiles) {
|
|
246
|
-
const content = fs.readFileSync(file, 'utf8').replace(/\r\n/g, '\n');
|
|
247
|
-
const fm = parseFrontmatter(content);
|
|
248
|
-
|
|
249
|
-
if (!fm) {
|
|
250
|
-
missingSlug++;
|
|
251
|
-
missingType++;
|
|
252
|
-
continue;
|
|
253
|
-
}
|
|
254
|
-
|
|
255
|
-
if (!fm.slug) missingSlug++;
|
|
256
|
-
if (!fm.type) {
|
|
257
|
-
missingType++;
|
|
258
|
-
continue;
|
|
259
|
-
}
|
|
260
|
-
|
|
261
|
-
// Check type-directory match
|
|
262
|
-
const expectedDir = TYPE_TO_DIR[fm.type];
|
|
263
|
-
if (expectedDir) {
|
|
264
|
-
const parentDir = path.basename(path.dirname(file));
|
|
265
|
-
if (parentDir !== expectedDir) {
|
|
266
|
-
wrongDir++;
|
|
267
|
-
}
|
|
268
|
-
}
|
|
269
|
-
}
|
|
270
|
-
|
|
271
|
-
const issues = [];
|
|
272
|
-
if (missingSlug > 0) issues.push(`${missingSlug} missing slug`);
|
|
273
|
-
if (missingType > 0) issues.push(`${missingType} missing type`);
|
|
274
|
-
if (wrongDir > 0) issues.push(`${wrongDir} type-dir mismatch`);
|
|
275
|
-
|
|
276
|
-
if (issues.length === 0) {
|
|
277
|
-
return {
|
|
278
|
-
ok: true,
|
|
279
|
-
detail: `${mdFiles.length} files validated`,
|
|
280
|
-
};
|
|
281
|
-
}
|
|
282
|
-
return {
|
|
283
|
-
ok: wrongDir > 0 ? false : undefined,
|
|
284
|
-
warn: wrongDir === 0,
|
|
285
|
-
detail: issues.join(', '),
|
|
286
|
-
};
|
|
287
|
-
});
|
|
288
|
-
}
|
|
289
|
-
|
|
290
|
-
// ── Run all ────────────────────────────────────────────────────────
|
|
291
|
-
|
|
292
|
-
run() {
|
|
293
|
-
this.checkDirectories();
|
|
294
|
-
this.checkSchema();
|
|
295
|
-
this.checkIndex();
|
|
296
|
-
this.checkDatabase();
|
|
297
|
-
this.checkOrphans();
|
|
298
|
-
this.checkFrontmatter();
|
|
299
|
-
return this;
|
|
300
|
-
}
|
|
301
|
-
|
|
302
|
-
report() {
|
|
303
|
-
console.log('\n Mindlore Health Check\n');
|
|
304
|
-
|
|
305
|
-
for (const r of this.results) {
|
|
306
|
-
const icon =
|
|
307
|
-
r.status === 'PASS' ? '+' : r.status === 'WARN' ? '~' : '-';
|
|
308
|
-
console.log(` [${icon}] ${r.name}: ${r.detail}`);
|
|
309
|
-
}
|
|
310
|
-
|
|
311
|
-
const total = this.passed + this.failed + this.warnings;
|
|
312
|
-
console.log(
|
|
313
|
-
`\n Score: ${this.passed}/${total} passed, ${this.warnings} warnings, ${this.failed} failed\n`
|
|
314
|
-
);
|
|
315
|
-
|
|
316
|
-
return this.failed === 0;
|
|
317
|
-
}
|
|
318
|
-
}
|
|
319
|
-
|
|
320
|
-
// ── Main ───────────────────────────────────────────────────────────────
|
|
321
|
-
|
|
322
|
-
function main() {
|
|
323
|
-
const baseDir = process.argv[2] || path.join(process.cwd(), '.mindlore');
|
|
324
|
-
|
|
325
|
-
if (!fs.existsSync(baseDir)) {
|
|
326
|
-
console.error(` .mindlore/ not found at: ${baseDir}`);
|
|
327
|
-
console.error(' Run: npx mindlore init');
|
|
328
|
-
process.exit(1);
|
|
329
|
-
}
|
|
330
|
-
|
|
331
|
-
const checker = new HealthChecker(baseDir);
|
|
332
|
-
const healthy = checker.run().report();
|
|
333
|
-
process.exit(healthy ? 0 : 1);
|
|
334
|
-
}
|
|
335
|
-
|
|
336
|
-
main();
|
package/scripts/uninstall.cjs
DELETED
|
@@ -1,186 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
'use strict';
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* mindlore uninstall — Remove Mindlore hooks, skills, and optionally project data.
|
|
6
|
-
*
|
|
7
|
-
* Usage: npx mindlore uninstall [--all]
|
|
8
|
-
*
|
|
9
|
-
* --all: also remove .mindlore/ project data (without flag, only hooks + skills)
|
|
10
|
-
*/
|
|
11
|
-
|
|
12
|
-
const fs = require('fs');
|
|
13
|
-
const path = require('path');
|
|
14
|
-
const { homedir } = require('./lib/constants.cjs');
|
|
15
|
-
|
|
16
|
-
function log(msg) {
|
|
17
|
-
console.log(` ${msg}`);
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
// ── Remove hooks from settings.json ────────────────────────────────────
|
|
21
|
-
|
|
22
|
-
function removeHooks() {
|
|
23
|
-
const settingsPath = path.join(homedir(), '.claude', 'settings.json');
|
|
24
|
-
|
|
25
|
-
if (!fs.existsSync(settingsPath)) {
|
|
26
|
-
log('No settings.json found, skipping hooks');
|
|
27
|
-
return 0;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
let settings;
|
|
31
|
-
try {
|
|
32
|
-
settings = JSON.parse(fs.readFileSync(settingsPath, 'utf8'));
|
|
33
|
-
} catch (_err) {
|
|
34
|
-
log('Could not parse settings.json, skipping hooks');
|
|
35
|
-
return 0;
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
if (!settings.hooks) return 0;
|
|
39
|
-
|
|
40
|
-
let removed = 0;
|
|
41
|
-
for (const event of Object.keys(settings.hooks)) {
|
|
42
|
-
const entries = settings.hooks[event];
|
|
43
|
-
if (!Array.isArray(entries)) continue;
|
|
44
|
-
|
|
45
|
-
const filtered = entries.filter((entry) => {
|
|
46
|
-
const hooks = entry.hooks || [];
|
|
47
|
-
const hasMindlore = hooks.some(
|
|
48
|
-
(h) => (h.command || '').includes('mindlore-')
|
|
49
|
-
);
|
|
50
|
-
// Also check flat format (legacy)
|
|
51
|
-
const flatMindlore = (entry.command || '').includes('mindlore-');
|
|
52
|
-
|
|
53
|
-
if (hasMindlore || flatMindlore) {
|
|
54
|
-
removed++;
|
|
55
|
-
return false;
|
|
56
|
-
}
|
|
57
|
-
return true;
|
|
58
|
-
});
|
|
59
|
-
|
|
60
|
-
settings.hooks[event] = filtered;
|
|
61
|
-
|
|
62
|
-
// Clean up empty arrays
|
|
63
|
-
if (settings.hooks[event].length === 0) {
|
|
64
|
-
delete settings.hooks[event];
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
if (removed > 0) {
|
|
69
|
-
fs.writeFileSync(settingsPath, JSON.stringify(settings, null, 2), 'utf8');
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
return removed;
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
// ── Remove skills from ~/.claude/skills/ ───────────────────────────────
|
|
76
|
-
|
|
77
|
-
function removeSkills() {
|
|
78
|
-
const skillsDir = path.join(homedir(), '.claude', 'skills');
|
|
79
|
-
if (!fs.existsSync(skillsDir)) return 0;
|
|
80
|
-
|
|
81
|
-
const mindloreSkills = fs
|
|
82
|
-
.readdirSync(skillsDir)
|
|
83
|
-
.filter((d) => d.startsWith('mindlore-'));
|
|
84
|
-
|
|
85
|
-
let removed = 0;
|
|
86
|
-
for (const skill of mindloreSkills) {
|
|
87
|
-
const skillPath = path.join(skillsDir, skill);
|
|
88
|
-
fs.rmSync(skillPath, { recursive: true, force: true });
|
|
89
|
-
removed++;
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
return removed;
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
// ── Remove SCHEMA.md from projectDocFiles ──────────────────────────────
|
|
96
|
-
|
|
97
|
-
function removeFromProjectDocs() {
|
|
98
|
-
const projectSettingsPath = path.join(process.cwd(), '.claude', 'settings.json');
|
|
99
|
-
if (!fs.existsSync(projectSettingsPath)) return false;
|
|
100
|
-
|
|
101
|
-
let settings;
|
|
102
|
-
try {
|
|
103
|
-
settings = JSON.parse(fs.readFileSync(projectSettingsPath, 'utf8'));
|
|
104
|
-
} catch (_err) {
|
|
105
|
-
return false;
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
if (!settings.projectDocFiles) return false;
|
|
109
|
-
|
|
110
|
-
const before = settings.projectDocFiles.length;
|
|
111
|
-
settings.projectDocFiles = settings.projectDocFiles.filter(
|
|
112
|
-
(p) => !p.includes('mindlore')
|
|
113
|
-
);
|
|
114
|
-
|
|
115
|
-
if (settings.projectDocFiles.length < before) {
|
|
116
|
-
fs.writeFileSync(
|
|
117
|
-
projectSettingsPath,
|
|
118
|
-
JSON.stringify(settings, null, 2),
|
|
119
|
-
'utf8'
|
|
120
|
-
);
|
|
121
|
-
return true;
|
|
122
|
-
}
|
|
123
|
-
return false;
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
// ── Remove .mindlore/ project data ─────────────────────────────────────
|
|
127
|
-
|
|
128
|
-
function removeProjectData() {
|
|
129
|
-
const mindloreDir = path.join(process.cwd(), '.mindlore');
|
|
130
|
-
if (!fs.existsSync(mindloreDir)) {
|
|
131
|
-
log('No .mindlore/ directory in current project');
|
|
132
|
-
return false;
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
fs.rmSync(mindloreDir, { recursive: true, force: true });
|
|
136
|
-
return true;
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
// ── Main ───────────────────────────────────────────────────────────────
|
|
140
|
-
|
|
141
|
-
function main() {
|
|
142
|
-
const args = process.argv.slice(2);
|
|
143
|
-
const removeAll = args.includes('--all');
|
|
144
|
-
|
|
145
|
-
console.log('\n Mindlore — Uninstall\n');
|
|
146
|
-
|
|
147
|
-
// Hooks
|
|
148
|
-
const hooksRemoved = removeHooks();
|
|
149
|
-
log(
|
|
150
|
-
hooksRemoved > 0
|
|
151
|
-
? `Removed ${hooksRemoved} hooks from ~/.claude/settings.json`
|
|
152
|
-
: 'No hooks found'
|
|
153
|
-
);
|
|
154
|
-
|
|
155
|
-
// Skills
|
|
156
|
-
const skillsRemoved = removeSkills();
|
|
157
|
-
log(
|
|
158
|
-
skillsRemoved > 0
|
|
159
|
-
? `Removed ${skillsRemoved} skills from ~/.claude/skills/`
|
|
160
|
-
: 'No skills found'
|
|
161
|
-
);
|
|
162
|
-
|
|
163
|
-
// Project doc files
|
|
164
|
-
const docsRemoved = removeFromProjectDocs();
|
|
165
|
-
log(
|
|
166
|
-
docsRemoved
|
|
167
|
-
? 'Removed SCHEMA.md from project settings'
|
|
168
|
-
: 'No project doc references found'
|
|
169
|
-
);
|
|
170
|
-
|
|
171
|
-
// Project data (only with --all)
|
|
172
|
-
if (removeAll) {
|
|
173
|
-
const dataRemoved = removeProjectData();
|
|
174
|
-
log(
|
|
175
|
-
dataRemoved
|
|
176
|
-
? 'Removed .mindlore/ project data'
|
|
177
|
-
: 'No .mindlore/ directory found'
|
|
178
|
-
);
|
|
179
|
-
} else {
|
|
180
|
-
log('.mindlore/ project data kept (use --all to remove)');
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
console.log('\n Done! Mindlore has been uninstalled.\n');
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
main();
|