mindlore 0.2.0 → 0.3.0

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.
Files changed (153) hide show
  1. package/README.md +15 -9
  2. package/SCHEMA.md +15 -2
  3. package/dist/scripts/init.d.ts +10 -0
  4. package/dist/scripts/init.d.ts.map +1 -0
  5. package/dist/scripts/init.js +406 -0
  6. package/dist/scripts/init.js.map +1 -0
  7. package/dist/scripts/lib/constants.d.ts +56 -0
  8. package/dist/scripts/lib/constants.d.ts.map +1 -0
  9. package/dist/scripts/lib/constants.js +131 -0
  10. package/dist/scripts/lib/constants.js.map +1 -0
  11. package/dist/scripts/lib/schemas.d.ts +93 -0
  12. package/dist/scripts/lib/schemas.d.ts.map +1 -0
  13. package/dist/scripts/lib/schemas.js +108 -0
  14. package/dist/scripts/lib/schemas.js.map +1 -0
  15. package/dist/scripts/mindlore-fts5-index.d.ts +9 -0
  16. package/dist/scripts/mindlore-fts5-index.d.ts.map +1 -0
  17. package/dist/scripts/mindlore-fts5-index.js +88 -0
  18. package/dist/scripts/mindlore-fts5-index.js.map +1 -0
  19. package/dist/scripts/mindlore-fts5-search.d.ts +10 -0
  20. package/dist/scripts/mindlore-fts5-search.d.ts.map +1 -0
  21. package/dist/scripts/mindlore-fts5-search.js +121 -0
  22. package/dist/scripts/mindlore-fts5-search.js.map +1 -0
  23. package/dist/scripts/mindlore-health-check.d.ts +10 -0
  24. package/dist/scripts/mindlore-health-check.d.ts.map +1 -0
  25. package/dist/scripts/mindlore-health-check.js +337 -0
  26. package/dist/scripts/mindlore-health-check.js.map +1 -0
  27. package/dist/scripts/uninstall.d.ts +10 -0
  28. package/dist/scripts/uninstall.d.ts.map +1 -0
  29. package/dist/scripts/uninstall.js +153 -0
  30. package/dist/scripts/uninstall.js.map +1 -0
  31. package/dist/tests/compounding.test.d.ts +8 -0
  32. package/dist/tests/compounding.test.d.ts.map +1 -0
  33. package/dist/tests/compounding.test.js +51 -0
  34. package/dist/tests/compounding.test.js.map +1 -0
  35. package/dist/tests/cwd-changed.test.d.ts +2 -0
  36. package/dist/tests/cwd-changed.test.d.ts.map +1 -0
  37. package/dist/tests/cwd-changed.test.js +62 -0
  38. package/dist/tests/cwd-changed.test.js.map +1 -0
  39. package/dist/tests/decision.test.d.ts +2 -0
  40. package/dist/tests/decision.test.d.ts.map +1 -0
  41. package/dist/tests/decision.test.js +61 -0
  42. package/dist/tests/decision.test.js.map +1 -0
  43. package/dist/tests/dedup.test.d.ts +2 -0
  44. package/dist/tests/dedup.test.d.ts.map +1 -0
  45. package/dist/tests/dedup.test.js +74 -0
  46. package/dist/tests/dedup.test.js.map +1 -0
  47. package/dist/tests/dont-repeat.test.d.ts +2 -0
  48. package/dist/tests/dont-repeat.test.d.ts.map +1 -0
  49. package/dist/tests/dont-repeat.test.js +100 -0
  50. package/dist/tests/dont-repeat.test.js.map +1 -0
  51. package/dist/tests/e2e-pipeline.test.d.ts +2 -0
  52. package/dist/tests/e2e-pipeline.test.d.ts.map +1 -0
  53. package/dist/tests/e2e-pipeline.test.js +220 -0
  54. package/dist/tests/e2e-pipeline.test.js.map +1 -0
  55. package/dist/tests/evolve.test.d.ts +2 -0
  56. package/dist/tests/evolve.test.d.ts.map +1 -0
  57. package/dist/tests/evolve.test.js +105 -0
  58. package/dist/tests/evolve.test.js.map +1 -0
  59. package/dist/tests/explore.test.d.ts +2 -0
  60. package/dist/tests/explore.test.d.ts.map +1 -0
  61. package/dist/tests/explore.test.js +146 -0
  62. package/dist/tests/explore.test.js.map +1 -0
  63. package/dist/tests/frontmatter.test.d.ts +2 -0
  64. package/dist/tests/frontmatter.test.d.ts.map +1 -0
  65. package/dist/tests/frontmatter.test.js +90 -0
  66. package/dist/tests/frontmatter.test.js.map +1 -0
  67. package/dist/tests/fts5.test.d.ts +2 -0
  68. package/dist/tests/fts5.test.d.ts.map +1 -0
  69. package/dist/tests/fts5.test.js +95 -0
  70. package/dist/tests/fts5.test.js.map +1 -0
  71. package/dist/tests/global-layer.test.d.ts +2 -0
  72. package/dist/tests/global-layer.test.d.ts.map +1 -0
  73. package/dist/tests/global-layer.test.js +152 -0
  74. package/dist/tests/global-layer.test.js.map +1 -0
  75. package/dist/tests/helpers/db.d.ts +20 -0
  76. package/dist/tests/helpers/db.d.ts.map +1 -0
  77. package/dist/tests/helpers/db.js +46 -0
  78. package/dist/tests/helpers/db.js.map +1 -0
  79. package/dist/tests/hook-smoke.test.d.ts +2 -0
  80. package/dist/tests/hook-smoke.test.d.ts.map +1 -0
  81. package/dist/tests/hook-smoke.test.js +58 -0
  82. package/dist/tests/hook-smoke.test.js.map +1 -0
  83. package/dist/tests/init.test.d.ts +2 -0
  84. package/dist/tests/init.test.d.ts.map +1 -0
  85. package/dist/tests/init.test.js +109 -0
  86. package/dist/tests/init.test.js.map +1 -0
  87. package/dist/tests/log.test.d.ts +2 -0
  88. package/dist/tests/log.test.d.ts.map +1 -0
  89. package/dist/tests/log.test.js +68 -0
  90. package/dist/tests/log.test.js.map +1 -0
  91. package/dist/tests/post-read.test.d.ts +2 -0
  92. package/dist/tests/post-read.test.d.ts.map +1 -0
  93. package/dist/tests/post-read.test.js +69 -0
  94. package/dist/tests/post-read.test.js.map +1 -0
  95. package/dist/tests/quality-populate.test.d.ts +2 -0
  96. package/dist/tests/quality-populate.test.d.ts.map +1 -0
  97. package/dist/tests/quality-populate.test.js +85 -0
  98. package/dist/tests/quality-populate.test.js.map +1 -0
  99. package/dist/tests/read-guard.test.d.ts +2 -0
  100. package/dist/tests/read-guard.test.d.ts.map +1 -0
  101. package/dist/tests/read-guard.test.js +69 -0
  102. package/dist/tests/read-guard.test.js.map +1 -0
  103. package/dist/tests/reflect.test.d.ts +2 -0
  104. package/dist/tests/reflect.test.d.ts.map +1 -0
  105. package/dist/tests/reflect.test.js +122 -0
  106. package/dist/tests/reflect.test.js.map +1 -0
  107. package/dist/tests/schemas.test.d.ts +2 -0
  108. package/dist/tests/schemas.test.d.ts.map +1 -0
  109. package/dist/tests/schemas.test.js +87 -0
  110. package/dist/tests/schemas.test.js.map +1 -0
  111. package/dist/tests/search-hook.test.d.ts +2 -0
  112. package/dist/tests/search-hook.test.d.ts.map +1 -0
  113. package/dist/tests/search-hook.test.js +108 -0
  114. package/dist/tests/search-hook.test.js.map +1 -0
  115. package/dist/tests/session-focus.test.d.ts +2 -0
  116. package/dist/tests/session-focus.test.d.ts.map +1 -0
  117. package/dist/tests/session-focus.test.js +71 -0
  118. package/dist/tests/session-focus.test.js.map +1 -0
  119. package/dist/tests/uninstall.test.d.ts +2 -0
  120. package/dist/tests/uninstall.test.d.ts.map +1 -0
  121. package/dist/tests/uninstall.test.js +98 -0
  122. package/dist/tests/uninstall.test.js.map +1 -0
  123. package/dist/tests/upgrade.test.d.ts +2 -0
  124. package/dist/tests/upgrade.test.d.ts.map +1 -0
  125. package/dist/tests/upgrade.test.js +91 -0
  126. package/dist/tests/upgrade.test.js.map +1 -0
  127. package/hooks/lib/mindlore-common.cjs +66 -5
  128. package/hooks/lib/types.d.ts +56 -0
  129. package/hooks/mindlore-cwd-changed.cjs +57 -0
  130. package/hooks/mindlore-dont-repeat.cjs +222 -0
  131. package/hooks/mindlore-fts5-sync.cjs +6 -9
  132. package/hooks/mindlore-index.cjs +3 -3
  133. package/hooks/mindlore-post-read.cjs +97 -0
  134. package/hooks/mindlore-read-guard.cjs +27 -4
  135. package/hooks/mindlore-search.cjs +73 -52
  136. package/hooks/mindlore-session-end.cjs +43 -1
  137. package/hooks/mindlore-session-focus.cjs +14 -0
  138. package/package.json +21 -8
  139. package/plugin.json +24 -1
  140. package/skills/mindlore-decide/SKILL.md +8 -0
  141. package/skills/mindlore-evolve/SKILL.md +81 -0
  142. package/skills/mindlore-explore/SKILL.md +84 -0
  143. package/skills/mindlore-health/SKILL.md +8 -0
  144. package/skills/mindlore-ingest/SKILL.md +19 -4
  145. package/skills/mindlore-log/SKILL.md +30 -13
  146. package/skills/mindlore-query/SKILL.md +8 -0
  147. package/templates/SCHEMA.md +15 -2
  148. package/scripts/init.cjs +0 -448
  149. package/scripts/lib/constants.cjs +0 -49
  150. package/scripts/mindlore-fts5-index.cjs +0 -112
  151. package/scripts/mindlore-fts5-search.cjs +0 -119
  152. package/scripts/mindlore-health-check.cjs +0 -389
  153. 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,389 +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
- const { parseFrontmatter: _parseFm, getAllMdFiles: _getAllMd } = require('../hooks/lib/mindlore-common.cjs');
19
-
20
- // ── Helpers ────────────────────────────────────────────────────────────
21
-
22
- // Wrapper: shared parseFrontmatter returns { meta, body }, health-check expects flat object or null
23
- function parseFrontmatter(content) {
24
- const { meta } = _parseFm(content);
25
- return Object.keys(meta).length > 0 ? meta : null;
26
- }
27
-
28
- // Health check needs ALL .md files (no skip), so pass empty set
29
- function getAllMdFiles(dir) {
30
- return _getAllMd(dir, new Set());
31
- }
32
-
33
- // ── Checks ─────────────────────────────────────────────────────────────
34
-
35
- class HealthChecker {
36
- constructor(baseDir) {
37
- this.baseDir = baseDir;
38
- this.results = [];
39
- this.passed = 0;
40
- this.failed = 0;
41
- this.warnings = 0;
42
- }
43
-
44
- check(name, fn) {
45
- try {
46
- const result = fn();
47
- if (result.ok) {
48
- this.passed++;
49
- this.results.push({ name, status: 'PASS', detail: result.detail });
50
- } else if (result.warn) {
51
- this.warnings++;
52
- this.results.push({ name, status: 'WARN', detail: result.detail });
53
- } else {
54
- this.failed++;
55
- this.results.push({ name, status: 'FAIL', detail: result.detail });
56
- }
57
- } catch (err) {
58
- this.failed++;
59
- this.results.push({ name, status: 'FAIL', detail: err.message });
60
- }
61
- }
62
-
63
- // Check 1-9: Directory existence
64
- checkDirectories() {
65
- for (const dir of DIRECTORIES) {
66
- this.check(`Directory: ${dir}/`, () => {
67
- const dirPath = path.join(this.baseDir, dir);
68
- if (fs.existsSync(dirPath)) {
69
- return { ok: true, detail: 'exists' };
70
- }
71
- return { ok: false, detail: 'missing' };
72
- });
73
- }
74
- }
75
-
76
- // Check 10: SCHEMA.md parseable
77
- checkSchema() {
78
- this.check('SCHEMA.md', () => {
79
- const schemaPath = path.join(this.baseDir, 'SCHEMA.md');
80
- if (!fs.existsSync(schemaPath)) {
81
- return { ok: false, detail: 'missing' };
82
- }
83
- const content = fs.readFileSync(schemaPath, 'utf8');
84
- if (content.length < 100) {
85
- return { ok: false, detail: 'too short (corrupted?)' };
86
- }
87
- if (!content.includes('## 1. Identity')) {
88
- return { warn: true, detail: 'may be outdated (missing Identity section)' };
89
- }
90
- return { ok: true, detail: `${content.split('\n').length} lines` };
91
- });
92
- }
93
-
94
- // Check 11: INDEX.md format
95
- checkIndex() {
96
- this.check('INDEX.md format', () => {
97
- const indexPath = path.join(this.baseDir, 'INDEX.md');
98
- if (!fs.existsSync(indexPath)) {
99
- return { ok: false, detail: 'missing' };
100
- }
101
- const content = fs.readFileSync(indexPath, 'utf8');
102
- const lines = content.trim().split('\n');
103
- if (lines.length > 60) {
104
- return {
105
- warn: true,
106
- detail: `${lines.length} lines (should be ~15-60, consider trimming)`,
107
- };
108
- }
109
- return { ok: true, detail: `${lines.length} lines` };
110
- });
111
- }
112
-
113
- // Check 12: Database integrity
114
- checkDatabase() {
115
- this.check('mindlore.db FTS5', () => {
116
- const dbPath = path.join(this.baseDir, 'mindlore.db');
117
- if (!fs.existsSync(dbPath)) {
118
- return { ok: false, detail: 'database missing' };
119
- }
120
-
121
- let Database;
122
- try {
123
- Database = require('better-sqlite3');
124
- } catch (_err) {
125
- return { warn: true, detail: 'better-sqlite3 not available, cannot verify' };
126
- }
127
-
128
- const db = new Database(dbPath, { readonly: true });
129
- try {
130
- const result = db.prepare('SELECT count(*) as cnt FROM mindlore_fts').get();
131
- const hashResult = db
132
- .prepare('SELECT count(*) as cnt FROM file_hashes')
133
- .get();
134
-
135
- // Verify 9-column schema (slug, description, type, category, title, content, tags, quality + path)
136
- let schemaVersion = 0;
137
- try {
138
- db.prepare('SELECT tags, quality FROM mindlore_fts LIMIT 0').run();
139
- schemaVersion = 9;
140
- } catch (_err) {
141
- try {
142
- db.prepare('SELECT slug, description, category, title FROM mindlore_fts LIMIT 0').run();
143
- schemaVersion = 7;
144
- } catch (_err2) {
145
- schemaVersion = 2;
146
- }
147
- }
148
-
149
- if (schemaVersion < 9) {
150
- return {
151
- warn: true,
152
- detail: `${result.cnt} indexed, ${hashResult.cnt} hashes — ${schemaVersion}-col schema (run: npx mindlore init to upgrade to 9-col)`,
153
- };
154
- }
155
-
156
- return {
157
- ok: true,
158
- detail: `${result.cnt} indexed, ${hashResult.cnt} hashes, 9-col schema`,
159
- };
160
- } catch (err) {
161
- return { ok: false, detail: `FTS5 error: ${err.message}` };
162
- } finally {
163
- db.close();
164
- }
165
- });
166
- }
167
-
168
- // Check 13: Orphan files (in .mindlore/ but not in FTS5)
169
- checkOrphans() {
170
- this.check('Orphan files', () => {
171
- const dbPath = path.join(this.baseDir, 'mindlore.db');
172
- if (!fs.existsSync(dbPath)) {
173
- return { warn: true, detail: 'no database, cannot check' };
174
- }
175
-
176
- let Database;
177
- try {
178
- Database = require('better-sqlite3');
179
- } catch (_err) {
180
- return { warn: true, detail: 'better-sqlite3 not available' };
181
- }
182
-
183
- const mdFiles = getAllMdFiles(this.baseDir).filter(
184
- (f) =>
185
- !f.endsWith('INDEX.md') &&
186
- !f.endsWith('SCHEMA.md') &&
187
- !f.endsWith('log.md')
188
- );
189
-
190
- const db = new Database(dbPath, { readonly: true });
191
- try {
192
- const indexed = new Set();
193
- const rows = db.prepare('SELECT path FROM file_hashes').all();
194
- for (const row of rows) {
195
- indexed.add(path.resolve(row.path));
196
- }
197
-
198
- const orphans = mdFiles.filter(
199
- (f) => !indexed.has(path.resolve(f))
200
- );
201
-
202
- if (orphans.length === 0) {
203
- return { ok: true, detail: 'no orphans' };
204
- }
205
- if (orphans.length <= 3) {
206
- return {
207
- warn: true,
208
- detail: `${orphans.length} unindexed: ${orphans.map((f) => path.basename(f)).join(', ')}`,
209
- };
210
- }
211
- return {
212
- ok: false,
213
- detail: `${orphans.length} unindexed files — run: npm run index`,
214
- };
215
- } finally {
216
- db.close();
217
- }
218
- });
219
- }
220
-
221
- // Check 14-16: Frontmatter validation
222
- checkFrontmatter() {
223
- this.check('Frontmatter: slug + type', () => {
224
- const mdFiles = getAllMdFiles(this.baseDir).filter(
225
- (f) =>
226
- !f.endsWith('INDEX.md') &&
227
- !f.endsWith('SCHEMA.md') &&
228
- !f.endsWith('log.md')
229
- );
230
-
231
- let missingSlug = 0;
232
- let missingType = 0;
233
- let wrongDir = 0;
234
-
235
- for (const file of mdFiles) {
236
- const content = fs.readFileSync(file, 'utf8').replace(/\r\n/g, '\n');
237
- const fm = parseFrontmatter(content);
238
-
239
- if (!fm) {
240
- missingSlug++;
241
- missingType++;
242
- continue;
243
- }
244
-
245
- if (!fm.slug) missingSlug++;
246
- if (!fm.type) {
247
- missingType++;
248
- continue;
249
- }
250
-
251
- // Check type-directory match
252
- const expectedDir = TYPE_TO_DIR[fm.type];
253
- if (expectedDir) {
254
- const parentDir = path.basename(path.dirname(file));
255
- if (parentDir !== expectedDir) {
256
- wrongDir++;
257
- }
258
- }
259
- }
260
-
261
- const issues = [];
262
- if (missingSlug > 0) issues.push(`${missingSlug} missing slug`);
263
- if (missingType > 0) issues.push(`${missingType} missing type`);
264
- if (wrongDir > 0) issues.push(`${wrongDir} type-dir mismatch`);
265
-
266
- if (issues.length === 0) {
267
- return {
268
- ok: true,
269
- detail: `${mdFiles.length} files validated`,
270
- };
271
- }
272
- if (wrongDir > 0) {
273
- return { ok: false, detail: issues.join(', ') };
274
- }
275
- return { warn: true, detail: issues.join(', ') };
276
- });
277
- }
278
-
279
- // Check 17: Stale deltas (30+ days without archived: true)
280
- checkStaleDeltas() {
281
- this.check('Stale deltas', () => {
282
- const diaryDir = path.join(this.baseDir, 'diary');
283
- if (!fs.existsSync(diaryDir)) return { ok: true, detail: 'no diary dir' };
284
-
285
- const now = Date.now();
286
- const thirtyDays = 30 * 24 * 60 * 60 * 1000;
287
- let stale = 0;
288
-
289
- const files = fs.readdirSync(diaryDir).filter((f) => f.startsWith('delta-') && f.endsWith('.md'));
290
- for (const file of files) {
291
- const fullPath = path.join(diaryDir, file);
292
- const content = fs.readFileSync(fullPath, 'utf8').replace(/\r\n/g, '\n');
293
- const fm = parseFrontmatter(content);
294
- if (fm && fm.archived === 'true') continue;
295
-
296
- const stat = fs.statSync(fullPath);
297
- if (now - stat.mtimeMs > thirtyDays) stale++;
298
- }
299
-
300
- if (stale === 0) return { ok: true, detail: `${files.length} deltas, none stale` };
301
- return { warn: true, detail: `${stale} deltas older than 30 days without archived flag — run /mindlore-log reflect` };
302
- });
303
- }
304
-
305
- // Check 18: Conflicting analyses (same tags, different confidence)
306
- checkConflictingAnalyses() {
307
- this.check('Conflicting analyses', () => {
308
- const analysesDir = path.join(this.baseDir, 'analyses');
309
- if (!fs.existsSync(analysesDir)) return { ok: true, detail: 'no analyses dir' };
310
-
311
- const files = fs.readdirSync(analysesDir).filter((f) => f.endsWith('.md'));
312
- if (files.length < 2) return { ok: true, detail: `${files.length} analyses, no conflict possible` };
313
-
314
- const tagMap = {};
315
- for (const file of files) {
316
- const content = fs.readFileSync(path.join(analysesDir, file), 'utf8').replace(/\r\n/g, '\n');
317
- const fm = parseFrontmatter(content);
318
- if (!fm || !fm.tags || !fm.confidence) continue;
319
-
320
- const tags = Array.isArray(fm.tags) ? fm.tags : String(fm.tags).split(',').map((t) => t.trim());
321
- for (const tag of tags) {
322
- if (!tagMap[tag]) tagMap[tag] = [];
323
- tagMap[tag].push({ file, confidence: fm.confidence });
324
- }
325
- }
326
-
327
- const conflicts = [];
328
- for (const [tag, entries] of Object.entries(tagMap)) {
329
- if (entries.length < 2) continue;
330
- const confidences = new Set(entries.map((e) => e.confidence));
331
- if (confidences.size > 1) {
332
- conflicts.push(`${tag}: ${entries.map((e) => `${e.file}(${e.confidence})`).join(' vs ')}`);
333
- }
334
- }
335
-
336
- if (conflicts.length === 0) return { ok: true, detail: `${files.length} analyses, no conflicts` };
337
- return { warn: true, detail: `${conflicts.length} tag conflicts: ${conflicts.slice(0, 2).join('; ')}` };
338
- });
339
- }
340
-
341
- // ── Run all ────────────────────────────────────────────────────────
342
-
343
- run() {
344
- this.checkDirectories();
345
- this.checkSchema();
346
- this.checkIndex();
347
- this.checkDatabase();
348
- this.checkOrphans();
349
- this.checkFrontmatter();
350
- this.checkStaleDeltas();
351
- this.checkConflictingAnalyses();
352
- return this;
353
- }
354
-
355
- report() {
356
- console.log('\n Mindlore Health Check\n');
357
-
358
- for (const r of this.results) {
359
- const icon =
360
- r.status === 'PASS' ? '+' : r.status === 'WARN' ? '~' : '-';
361
- console.log(` [${icon}] ${r.name}: ${r.detail}`);
362
- }
363
-
364
- const total = this.passed + this.failed + this.warnings;
365
- console.log(
366
- `\n Score: ${this.passed}/${total} passed, ${this.warnings} warnings, ${this.failed} failed\n`
367
- );
368
-
369
- return this.failed === 0;
370
- }
371
- }
372
-
373
- // ── Main ───────────────────────────────────────────────────────────────
374
-
375
- function main() {
376
- const baseDir = process.argv[2] || path.join(process.cwd(), '.mindlore');
377
-
378
- if (!fs.existsSync(baseDir)) {
379
- console.error(` .mindlore/ not found at: ${baseDir}`);
380
- console.error(' Run: npx mindlore init');
381
- process.exit(1);
382
- }
383
-
384
- const checker = new HealthChecker(baseDir);
385
- const healthy = checker.run().report();
386
- process.exit(healthy ? 0 : 1);
387
- }
388
-
389
- main();
@@ -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();