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.
Files changed (98) hide show
  1. package/README.md +13 -5
  2. package/SCHEMA.md +60 -4
  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 +375 -0
  6. package/dist/scripts/init.js.map +1 -0
  7. package/dist/scripts/lib/constants.d.ts +29 -0
  8. package/dist/scripts/lib/constants.d.ts.map +1 -0
  9. package/dist/scripts/lib/constants.js +58 -0
  10. package/dist/scripts/lib/constants.js.map +1 -0
  11. package/dist/scripts/mindlore-fts5-index.d.ts +9 -0
  12. package/dist/scripts/mindlore-fts5-index.d.ts.map +1 -0
  13. package/dist/scripts/mindlore-fts5-index.js +89 -0
  14. package/dist/scripts/mindlore-fts5-index.js.map +1 -0
  15. package/dist/scripts/mindlore-fts5-search.d.ts +10 -0
  16. package/dist/scripts/mindlore-fts5-search.d.ts.map +1 -0
  17. package/dist/scripts/mindlore-fts5-search.js +108 -0
  18. package/dist/scripts/mindlore-fts5-search.js.map +1 -0
  19. package/dist/scripts/mindlore-health-check.d.ts +10 -0
  20. package/dist/scripts/mindlore-health-check.d.ts.map +1 -0
  21. package/dist/scripts/mindlore-health-check.js +337 -0
  22. package/dist/scripts/mindlore-health-check.js.map +1 -0
  23. package/dist/scripts/uninstall.d.ts +10 -0
  24. package/dist/scripts/uninstall.d.ts.map +1 -0
  25. package/dist/scripts/uninstall.js +143 -0
  26. package/dist/scripts/uninstall.js.map +1 -0
  27. package/dist/tests/compounding.test.d.ts +8 -0
  28. package/dist/tests/compounding.test.d.ts.map +1 -0
  29. package/dist/tests/compounding.test.js +51 -0
  30. package/dist/tests/compounding.test.js.map +1 -0
  31. package/dist/tests/decision.test.d.ts +2 -0
  32. package/dist/tests/decision.test.d.ts.map +1 -0
  33. package/dist/tests/decision.test.js +61 -0
  34. package/dist/tests/decision.test.js.map +1 -0
  35. package/dist/tests/dedup.test.d.ts +2 -0
  36. package/dist/tests/dedup.test.d.ts.map +1 -0
  37. package/dist/tests/dedup.test.js +74 -0
  38. package/dist/tests/dedup.test.js.map +1 -0
  39. package/dist/tests/frontmatter.test.d.ts +2 -0
  40. package/dist/tests/frontmatter.test.d.ts.map +1 -0
  41. package/dist/tests/frontmatter.test.js +90 -0
  42. package/dist/tests/frontmatter.test.js.map +1 -0
  43. package/dist/tests/fts5.test.d.ts +2 -0
  44. package/dist/tests/fts5.test.d.ts.map +1 -0
  45. package/dist/tests/fts5.test.js +95 -0
  46. package/dist/tests/fts5.test.js.map +1 -0
  47. package/dist/tests/helpers/db.d.ts +7 -0
  48. package/dist/tests/helpers/db.d.ts.map +1 -0
  49. package/dist/tests/helpers/db.js +46 -0
  50. package/dist/tests/helpers/db.js.map +1 -0
  51. package/dist/tests/hook-smoke.test.d.ts +2 -0
  52. package/dist/tests/hook-smoke.test.d.ts.map +1 -0
  53. package/dist/tests/hook-smoke.test.js +58 -0
  54. package/dist/tests/hook-smoke.test.js.map +1 -0
  55. package/dist/tests/init.test.d.ts +2 -0
  56. package/dist/tests/init.test.d.ts.map +1 -0
  57. package/dist/tests/init.test.js +85 -0
  58. package/dist/tests/init.test.js.map +1 -0
  59. package/dist/tests/log.test.d.ts +2 -0
  60. package/dist/tests/log.test.d.ts.map +1 -0
  61. package/dist/tests/log.test.js +68 -0
  62. package/dist/tests/log.test.js.map +1 -0
  63. package/dist/tests/read-guard.test.d.ts +2 -0
  64. package/dist/tests/read-guard.test.d.ts.map +1 -0
  65. package/dist/tests/read-guard.test.js +69 -0
  66. package/dist/tests/read-guard.test.js.map +1 -0
  67. package/dist/tests/search-hook.test.d.ts +2 -0
  68. package/dist/tests/search-hook.test.d.ts.map +1 -0
  69. package/dist/tests/search-hook.test.js +108 -0
  70. package/dist/tests/search-hook.test.js.map +1 -0
  71. package/dist/tests/session-focus.test.d.ts +2 -0
  72. package/dist/tests/session-focus.test.d.ts.map +1 -0
  73. package/dist/tests/session-focus.test.js +71 -0
  74. package/dist/tests/session-focus.test.js.map +1 -0
  75. package/dist/tests/uninstall.test.d.ts +2 -0
  76. package/dist/tests/uninstall.test.d.ts.map +1 -0
  77. package/dist/tests/uninstall.test.js +98 -0
  78. package/dist/tests/uninstall.test.js.map +1 -0
  79. package/hooks/lib/mindlore-common.cjs +36 -2
  80. package/hooks/mindlore-decision-detector.cjs +51 -0
  81. package/hooks/mindlore-fts5-sync.cjs +4 -18
  82. package/hooks/mindlore-index.cjs +4 -18
  83. package/hooks/mindlore-read-guard.cjs +62 -0
  84. package/hooks/mindlore-search.cjs +5 -18
  85. package/hooks/mindlore-session-end.cjs +74 -8
  86. package/package.json +19 -7
  87. package/plugin.json +26 -2
  88. package/skills/mindlore-decide/SKILL.md +71 -0
  89. package/skills/mindlore-ingest/SKILL.md +13 -2
  90. package/skills/mindlore-log/SKILL.md +79 -0
  91. package/skills/mindlore-query/SKILL.md +125 -0
  92. package/templates/SCHEMA.md +60 -4
  93. package/scripts/init.cjs +0 -450
  94. package/scripts/lib/constants.cjs +0 -49
  95. package/scripts/mindlore-fts5-index.cjs +0 -112
  96. package/scripts/mindlore-fts5-search.cjs +0 -119
  97. package/scripts/mindlore-health-check.cjs +0 -336
  98. package/scripts/uninstall.cjs +0 -186
@@ -0,0 +1,98 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ const fs_1 = __importDefault(require("fs"));
7
+ const path_1 = __importDefault(require("path"));
8
+ const TEST_DIR = path_1.default.join(__dirname, '..', '.test-mindlore-uninstall');
9
+ const MOCK_HOME = path_1.default.join(TEST_DIR, 'home');
10
+ const MOCK_PROJECT = path_1.default.join(TEST_DIR, 'project');
11
+ function setupMockEnvironment() {
12
+ const claudeDir = path_1.default.join(MOCK_HOME, '.claude');
13
+ const skillsDir = path_1.default.join(claudeDir, 'skills');
14
+ fs_1.default.mkdirSync(path_1.default.join(skillsDir, 'mindlore-ingest'), { recursive: true });
15
+ fs_1.default.mkdirSync(path_1.default.join(skillsDir, 'mindlore-health'), { recursive: true });
16
+ fs_1.default.mkdirSync(path_1.default.join(skillsDir, 'other-skill'), { recursive: true });
17
+ fs_1.default.writeFileSync(path_1.default.join(skillsDir, 'mindlore-ingest', 'SKILL.md'), '# Ingest');
18
+ fs_1.default.writeFileSync(path_1.default.join(skillsDir, 'mindlore-health', 'SKILL.md'), '# Health');
19
+ fs_1.default.writeFileSync(path_1.default.join(skillsDir, 'other-skill', 'SKILL.md'), '# Other');
20
+ const settings = {
21
+ hooks: {
22
+ SessionStart: [
23
+ { hooks: [{ type: 'command', command: 'node some-other-hook.cjs' }] },
24
+ { hooks: [{ type: 'command', command: 'node mindlore-session-focus.cjs' }] },
25
+ ],
26
+ UserPromptSubmit: [
27
+ { hooks: [{ type: 'command', command: 'node mindlore-search.cjs' }] },
28
+ ],
29
+ SessionEnd: [
30
+ { hooks: [{ type: 'command', command: 'node mindlore-session-end.cjs' }] },
31
+ ],
32
+ },
33
+ };
34
+ fs_1.default.writeFileSync(path_1.default.join(claudeDir, 'settings.json'), JSON.stringify(settings, null, 2));
35
+ const projectClaude = path_1.default.join(MOCK_PROJECT, '.claude');
36
+ const projectMindlore = path_1.default.join(MOCK_PROJECT, '.mindlore');
37
+ fs_1.default.mkdirSync(path_1.default.join(projectMindlore, 'sources'), { recursive: true });
38
+ fs_1.default.mkdirSync(projectClaude, { recursive: true });
39
+ fs_1.default.writeFileSync(path_1.default.join(projectMindlore, 'INDEX.md'), '# Mindlore Index');
40
+ fs_1.default.writeFileSync(path_1.default.join(projectClaude, 'settings.json'), JSON.stringify({ projectDocFiles: ['.mindlore/SCHEMA.md', 'other.md'] }));
41
+ }
42
+ beforeEach(() => {
43
+ fs_1.default.rmSync(TEST_DIR, { recursive: true, force: true });
44
+ setupMockEnvironment();
45
+ });
46
+ afterEach(() => {
47
+ fs_1.default.rmSync(TEST_DIR, { recursive: true, force: true });
48
+ });
49
+ describe('Uninstall Logic', () => {
50
+ test('should remove mindlore hooks but keep other hooks', () => {
51
+ const settingsPath = path_1.default.join(MOCK_HOME, '.claude', 'settings.json');
52
+ const settings = JSON.parse(fs_1.default.readFileSync(settingsPath, 'utf8'));
53
+ for (const event of Object.keys(settings.hooks)) {
54
+ settings.hooks[event] = (settings.hooks[event] ?? []).filter((entry) => {
55
+ const hooks = entry.hooks ?? [];
56
+ return !hooks.some((h) => (h.command ?? '').includes('mindlore-'));
57
+ });
58
+ if (settings.hooks[event].length === 0) {
59
+ delete settings.hooks[event];
60
+ }
61
+ }
62
+ expect(settings.hooks['SessionStart']).toHaveLength(1);
63
+ expect(settings.hooks['SessionStart'][0].hooks[0].command).toContain('some-other-hook');
64
+ expect(settings.hooks['UserPromptSubmit']).toBeUndefined();
65
+ expect(settings.hooks['SessionEnd']).toBeUndefined();
66
+ });
67
+ test('should remove mindlore skills but keep other skills', () => {
68
+ const skillsDir = path_1.default.join(MOCK_HOME, '.claude', 'skills');
69
+ const before = fs_1.default.readdirSync(skillsDir);
70
+ expect(before).toContain('mindlore-ingest');
71
+ expect(before).toContain('mindlore-health');
72
+ expect(before).toContain('other-skill');
73
+ const mindloreSkills = before.filter((d) => d.startsWith('mindlore-'));
74
+ for (const skill of mindloreSkills) {
75
+ fs_1.default.rmSync(path_1.default.join(skillsDir, skill), { recursive: true });
76
+ }
77
+ const after = fs_1.default.readdirSync(skillsDir);
78
+ expect(after).not.toContain('mindlore-ingest');
79
+ expect(after).not.toContain('mindlore-health');
80
+ expect(after).toContain('other-skill');
81
+ });
82
+ test('should remove SCHEMA.md from projectDocFiles but keep others', () => {
83
+ const settingsPath = path_1.default.join(MOCK_PROJECT, '.claude', 'settings.json');
84
+ const settings = JSON.parse(fs_1.default.readFileSync(settingsPath, 'utf8'));
85
+ expect(settings.projectDocFiles).toContain('.mindlore/SCHEMA.md');
86
+ expect(settings.projectDocFiles).toContain('other.md');
87
+ settings.projectDocFiles = settings.projectDocFiles.filter((p) => !p.includes('mindlore'));
88
+ expect(settings.projectDocFiles).not.toContain('.mindlore/SCHEMA.md');
89
+ expect(settings.projectDocFiles).toContain('other.md');
90
+ });
91
+ test('should remove .mindlore/ with --all flag', () => {
92
+ const mindloreDir = path_1.default.join(MOCK_PROJECT, '.mindlore');
93
+ expect(fs_1.default.existsSync(mindloreDir)).toBe(true);
94
+ fs_1.default.rmSync(mindloreDir, { recursive: true, force: true });
95
+ expect(fs_1.default.existsSync(mindloreDir)).toBe(false);
96
+ });
97
+ });
98
+ //# sourceMappingURL=uninstall.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"uninstall.test.js","sourceRoot":"","sources":["../../tests/uninstall.test.ts"],"names":[],"mappings":"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"}
@@ -86,14 +86,22 @@ function extractFtsMetadata(meta, body, filePath, baseDir) {
86
86
  const headingMatch = body.match(/^#\s+(.+)/m);
87
87
  title = headingMatch ? headingMatch[1].trim() : path.basename(filePath, '.md');
88
88
  }
89
- return { slug, description, type, category, title };
89
+ let tags = '';
90
+ if (meta.tags) {
91
+ tags = Array.isArray(meta.tags) ? meta.tags.join(', ') : String(meta.tags);
92
+ }
93
+ const quality = meta.quality !== undefined && meta.quality !== null ? meta.quality : null;
94
+ return { slug, description, type, category, title, tags, quality };
90
95
  }
91
96
 
92
97
  /**
93
98
  * Shared SQL constants to prevent drift across indexing paths.
94
99
  */
100
+ const SQL_FTS_CREATE =
101
+ "CREATE VIRTUAL TABLE IF NOT EXISTS mindlore_fts USING fts5(path UNINDEXED, slug, description, type UNINDEXED, category, title, content, tags, quality UNINDEXED, tokenize='porter unicode61')";
102
+
95
103
  const SQL_FTS_INSERT =
96
- 'INSERT INTO mindlore_fts (path, slug, description, type, category, title, content) VALUES (?, ?, ?, ?, ?, ?, ?)';
104
+ 'INSERT INTO mindlore_fts (path, slug, description, type, category, title, content, tags, quality) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)';
97
105
 
98
106
  /**
99
107
  * Extract headings (h1-h3) from markdown content.
@@ -145,6 +153,30 @@ function getAllMdFiles(dir, skip) {
145
153
  return results;
146
154
  }
147
155
 
156
+ /**
157
+ * Read CC hook stdin and parse JSON envelope.
158
+ * Returns the value of the first matching field, or raw text as fallback.
159
+ * @param {string[]} fields - Priority-ordered field names to extract
160
+ */
161
+ function readHookStdin(fields) {
162
+ let input = '';
163
+ try {
164
+ input = fs.readFileSync(0, 'utf8').trim();
165
+ } catch (_err) {
166
+ return '';
167
+ }
168
+ if (!input) return '';
169
+ try {
170
+ const parsed = JSON.parse(input);
171
+ for (const f of fields) {
172
+ if (parsed[f]) return parsed[f];
173
+ }
174
+ } catch (_err) {
175
+ // plain text
176
+ }
177
+ return input;
178
+ }
179
+
148
180
  module.exports = {
149
181
  MINDLORE_DIR,
150
182
  DB_NAME,
@@ -154,6 +186,8 @@ module.exports = {
154
186
  sha256,
155
187
  parseFrontmatter,
156
188
  extractFtsMetadata,
189
+ readHookStdin,
190
+ SQL_FTS_CREATE,
157
191
  SQL_FTS_INSERT,
158
192
  extractHeadings,
159
193
  requireDatabase,
@@ -0,0 +1,51 @@
1
+ #!/usr/bin/env node
2
+ 'use strict';
3
+
4
+ /**
5
+ * mindlore-decision-detector — UserPromptSubmit hook
6
+ *
7
+ * Detects decision signals in user messages (TR + EN).
8
+ * Outputs a suggestion to record the decision via /mindlore-decide.
9
+ * Does NOT block (exit 0) — advisory only.
10
+ */
11
+
12
+ const { findMindloreDir, readHookStdin } = require('./lib/mindlore-common.cjs');
13
+
14
+ const SIGNALS_TR = [
15
+ 'karar verdik', 'karar verildi', 'kararlastirdik', 'kararlaştırdık',
16
+ 'şunu seçtik', 'sunu sectik', 'bunu yapmayalım', 'bunu yapmayalim',
17
+ 'yerine', 'tercih ettik', 'onaylandi', 'onaylandı', 'kesinleşti', 'kesinlesti',
18
+ 'vazgeçtik', 'vazgectik', 'iptal ettik',
19
+ ];
20
+
21
+ const SIGNALS_EN = [
22
+ 'decided', 'decision made', "let's go with", 'lets go with',
23
+ "we'll use", 'well use', 'approved', 'settled on',
24
+ 'going with', 'chosen', 'finalized', 'rejected',
25
+ ];
26
+
27
+ function detectDecision(text) {
28
+ const lower = text.toLowerCase();
29
+ for (const signal of SIGNALS_TR) {
30
+ if (lower.includes(signal)) return signal;
31
+ }
32
+ for (const signal of SIGNALS_EN) {
33
+ if (lower.includes(signal)) return signal;
34
+ }
35
+ return null;
36
+ }
37
+
38
+ function main() {
39
+ const baseDir = findMindloreDir();
40
+ if (!baseDir) return;
41
+
42
+ const userText = readHookStdin(['prompt', 'content', 'message']);
43
+ if (!userText || userText.length < 10) return;
44
+
45
+ const signal = detectDecision(userText);
46
+ if (signal) {
47
+ process.stdout.write(`[Mindlore: Karar sinyali tespit edildi ("${signal}") — /mindlore-decide record ile kaydetmek ister misin?]\n`);
48
+ }
49
+ }
50
+
51
+ main();
@@ -13,24 +13,10 @@
13
13
 
14
14
  const fs = require('fs');
15
15
  const path = require('path');
16
- const { MINDLORE_DIR, DB_NAME, sha256, openDatabase, getAllMdFiles, parseFrontmatter, extractFtsMetadata, SQL_FTS_INSERT } = require('./lib/mindlore-common.cjs');
16
+ const { MINDLORE_DIR, DB_NAME, sha256, openDatabase, getAllMdFiles, parseFrontmatter, extractFtsMetadata, SQL_FTS_INSERT, readHookStdin } = require('./lib/mindlore-common.cjs');
17
17
 
18
18
  function main() {
19
- // Read stdin to check if this is a .mindlore/ file change
20
- let input = '';
21
- try {
22
- input = fs.readFileSync(0, 'utf8').trim();
23
- } catch (_err) {
24
- // No stdin — skip
25
- }
26
-
27
- let filePath = '';
28
- try {
29
- const parsed = JSON.parse(input);
30
- filePath = parsed.path || parsed.file_path || '';
31
- } catch (_err) {
32
- filePath = input;
33
- }
19
+ const filePath = readHookStdin(['path', 'file_path']);
34
20
 
35
21
  // Only trigger on .mindlore/ changes (empty filePath = skip)
36
22
  if (!filePath || !filePath.includes(MINDLORE_DIR)) return;
@@ -74,9 +60,9 @@ function main() {
74
60
  if (existing && existing.content_hash === hash) continue;
75
61
 
76
62
  const { meta, body } = parseFrontmatter(content);
77
- const { slug, description, type, category, title } = extractFtsMetadata(meta, body, file, baseDir);
63
+ const { slug, description, type, category, title, tags, quality } = extractFtsMetadata(meta, body, file, baseDir);
78
64
  deleteFts.run(file);
79
- insertFts.run(file, slug, description, type, category, title, body);
65
+ insertFts.run(file, slug, description, type, category, title, body, tags, quality);
80
66
  upsertHash.run(file, hash, now);
81
67
  synced++;
82
68
  }
@@ -10,24 +10,10 @@
10
10
 
11
11
  const fs = require('fs');
12
12
  const path = require('path');
13
- const { MINDLORE_DIR, DB_NAME, SKIP_FILES, sha256, openDatabase, parseFrontmatter, extractFtsMetadata, SQL_FTS_INSERT } = require('./lib/mindlore-common.cjs');
13
+ const { MINDLORE_DIR, DB_NAME, SKIP_FILES, sha256, openDatabase, parseFrontmatter, extractFtsMetadata, SQL_FTS_INSERT, readHookStdin } = require('./lib/mindlore-common.cjs');
14
14
 
15
15
  function main() {
16
- let input = '';
17
- try {
18
- input = fs.readFileSync(0, 'utf8').trim();
19
- } catch (_err) {
20
- return;
21
- }
22
-
23
- let filePath = '';
24
- try {
25
- const parsed = JSON.parse(input);
26
- filePath = parsed.path || parsed.file_path || '';
27
- } catch (_err) {
28
- filePath = input;
29
- }
30
-
16
+ const filePath = readHookStdin(['path', 'file_path']);
31
17
  if (!filePath) return;
32
18
 
33
19
  // Only process .md files inside .mindlore/
@@ -72,11 +58,11 @@ function main() {
72
58
 
73
59
  // Parse frontmatter for rich FTS5 columns
74
60
  const { meta, body } = parseFrontmatter(content);
75
- const { slug, description, type, category, title } = extractFtsMetadata(meta, body, filePath, baseDir);
61
+ const { slug, description, type, category, title, tags, quality } = extractFtsMetadata(meta, body, filePath, baseDir);
76
62
 
77
63
  // Update FTS5
78
64
  db.prepare('DELETE FROM mindlore_fts WHERE path = ?').run(filePath);
79
- db.prepare(SQL_FTS_INSERT).run(filePath, slug, description, type, category, title, body);
65
+ db.prepare(SQL_FTS_INSERT).run(filePath, slug, description, type, category, title, body, tags, quality);
80
66
 
81
67
  // Update hash
82
68
  db.prepare(
@@ -0,0 +1,62 @@
1
+ #!/usr/bin/env node
2
+ 'use strict';
3
+
4
+ /**
5
+ * mindlore-read-guard — PreToolUse hook (if: "Read")
6
+ *
7
+ * OpenWolf repeated-read pattern: detects files read multiple times
8
+ * in the same session and emits a soft warning.
9
+ * Does NOT block (exit 0) — advisory only.
10
+ *
11
+ * Storage: .mindlore/diary/_session-reads.json
12
+ * Cleanup: session-end hook writes stats to delta then deletes the file.
13
+ */
14
+
15
+ const fs = require('fs');
16
+ const path = require('path');
17
+ const { findMindloreDir, readHookStdin } = require('./lib/mindlore-common.cjs');
18
+
19
+ function main() {
20
+ const baseDir = findMindloreDir();
21
+ if (!baseDir) return;
22
+
23
+ const filePath = readHookStdin(['file_path', 'path']);
24
+ if (!filePath) return;
25
+
26
+ // Only track CWD-relative files, skip .mindlore/ internals
27
+ const cwd = process.cwd();
28
+ const resolved = path.resolve(filePath);
29
+ if (!resolved.startsWith(cwd)) return;
30
+ if (resolved.startsWith(path.resolve(baseDir))) return;
31
+
32
+ // Load or create session reads tracker
33
+ const diaryDir = path.join(baseDir, 'diary');
34
+ if (!fs.existsSync(diaryDir)) {
35
+ fs.mkdirSync(diaryDir, { recursive: true });
36
+ }
37
+
38
+ const readsPath = path.join(diaryDir, '_session-reads.json');
39
+ let reads = {};
40
+ if (fs.existsSync(readsPath)) {
41
+ try {
42
+ reads = JSON.parse(fs.readFileSync(readsPath, 'utf8'));
43
+ } catch (_err) {
44
+ reads = {};
45
+ }
46
+ }
47
+
48
+ const normalizedPath = path.resolve(filePath);
49
+ const count = (reads[normalizedPath] || 0) + 1;
50
+ reads[normalizedPath] = count;
51
+
52
+ // Write updated reads
53
+ fs.writeFileSync(readsPath, JSON.stringify(reads, null, 2), 'utf8');
54
+
55
+ // Warn on repeated reads (2nd+ time)
56
+ if (count > 1) {
57
+ const basename = path.basename(filePath);
58
+ process.stdout.write(`[Mindlore: ${basename} bu session'da ${count}. kez okunuyor. Değişiklik yoksa tekrar okumayı atlayabilirsin.]\n`);
59
+ }
60
+ }
61
+
62
+ main();
@@ -10,7 +10,7 @@
10
10
 
11
11
  const fs = require('fs');
12
12
  const path = require('path');
13
- const { findMindloreDir, DB_NAME, requireDatabase, extractHeadings } = require('./lib/mindlore-common.cjs');
13
+ const { findMindloreDir, DB_NAME, requireDatabase, extractHeadings, readHookStdin } = require('./lib/mindlore-common.cjs');
14
14
 
15
15
  const MAX_RESULTS = 3;
16
16
  const MIN_QUERY_WORDS = 3;
@@ -60,21 +60,7 @@ function extractKeywords(text) {
60
60
  }
61
61
 
62
62
  function main() {
63
- let input = '';
64
- try {
65
- input = fs.readFileSync(0, 'utf8');
66
- } catch (_err) {
67
- return;
68
- }
69
-
70
- let userMessage = '';
71
- try {
72
- const parsed = JSON.parse(input);
73
- userMessage = parsed.prompt || parsed.content || parsed.message || parsed.query || input;
74
- } catch (_err) {
75
- userMessage = input;
76
- }
77
-
63
+ const userMessage = readHookStdin(['prompt', 'content', 'message', 'query']);
78
64
  if (!userMessage || userMessage.length < MIN_QUERY_WORDS) return;
79
65
 
80
66
  const baseDir = findMindloreDir();
@@ -127,7 +113,7 @@ function main() {
127
113
 
128
114
  // Build rich inject output
129
115
  const metaStmt = db.prepare(
130
- 'SELECT slug, description, category, title FROM mindlore_fts WHERE path = ?'
116
+ 'SELECT slug, description, category, title, tags FROM mindlore_fts WHERE path = ?'
131
117
  );
132
118
 
133
119
  const output = [];
@@ -146,8 +132,9 @@ function main() {
146
132
  const description = meta.description || '';
147
133
 
148
134
  const headingStr = headings.length > 0 ? `\nBasliklar: ${headings.join(', ')}` : '';
135
+ const tagsStr = meta.tags ? `\nTags: ${meta.tags}` : '';
149
136
  output.push(
150
- `[Mindlore: ${category}/${title}] ${description}\nDosya: ${relativePath}${headingStr}`
137
+ `[Mindlore: ${category}/${title}] ${description}\nDosya: ${relativePath}${tagsStr}${headingStr}`
151
138
  );
152
139
  }
153
140
 
@@ -11,6 +11,7 @@
11
11
 
12
12
  const fs = require('fs');
13
13
  const path = require('path');
14
+ const { execSync } = require('child_process');
14
15
  const { findMindloreDir } = require('./lib/mindlore-common.cjs');
15
16
 
16
17
  function formatDate(date) {
@@ -22,6 +23,47 @@ function formatDate(date) {
22
23
  return `${y}-${m}-${d}-${h}${min}`;
23
24
  }
24
25
 
26
+ function getRecentGitChanges() {
27
+ try {
28
+ const raw = execSync('git diff --name-only HEAD~5..HEAD 2>/dev/null', {
29
+ encoding: 'utf8',
30
+ timeout: 5000,
31
+ }).trim();
32
+ if (!raw) return [];
33
+ return raw.split('\n').filter(Boolean).slice(0, 20);
34
+ } catch (_err) {
35
+ return [];
36
+ }
37
+ }
38
+
39
+ function getRecentCommits() {
40
+ try {
41
+ const raw = execSync('git log --oneline -5 2>/dev/null', {
42
+ encoding: 'utf8',
43
+ timeout: 5000,
44
+ }).trim();
45
+ if (!raw) return [];
46
+ return raw.split('\n').filter(Boolean);
47
+ } catch (_err) {
48
+ return [];
49
+ }
50
+ }
51
+
52
+ function getSessionReads(baseDir) {
53
+ const readsPath = path.join(baseDir, 'diary', '_session-reads.json');
54
+ if (!fs.existsSync(readsPath)) return null;
55
+ try {
56
+ const data = JSON.parse(fs.readFileSync(readsPath, 'utf8'));
57
+ const count = Object.keys(data).length;
58
+ const repeats = Object.values(data).filter((v) => v > 1).length;
59
+ // Clean up session file
60
+ fs.unlinkSync(readsPath);
61
+ return { count, repeats };
62
+ } catch (_err) {
63
+ return null;
64
+ }
65
+ }
66
+
25
67
  function main() {
26
68
  const baseDir = findMindloreDir();
27
69
  if (!baseDir) return;
@@ -38,7 +80,12 @@ function main() {
38
80
  // Don't overwrite existing delta (idempotent)
39
81
  if (fs.existsSync(deltaPath)) return;
40
82
 
41
- const content = [
83
+ // Gather structured data
84
+ const commits = getRecentCommits();
85
+ const changedFiles = getRecentGitChanges();
86
+ const reads = getSessionReads(baseDir);
87
+
88
+ const sections = [
42
89
  '---',
43
90
  `slug: delta-${dateStr}`,
44
91
  'type: diary',
@@ -48,14 +95,33 @@ function main() {
48
95
  `# Session Delta — ${dateStr}`,
49
96
  '',
50
97
  `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');
98
+ ];
99
+
100
+ // Commits section
101
+ sections.push('', '## Commits');
102
+ if (commits.length > 0) {
103
+ for (const c of commits) sections.push(`- ${c}`);
104
+ } else {
105
+ sections.push('- _(no commits)_');
106
+ }
107
+
108
+ // Changed files section
109
+ sections.push('', '## Changed Files');
110
+ if (changedFiles.length > 0) {
111
+ for (const f of changedFiles) sections.push(`- ${f}`);
112
+ } else {
113
+ sections.push('- _(no file changes)_');
114
+ }
115
+
116
+ // Read stats (from read-guard, if active)
117
+ if (reads) {
118
+ sections.push('', '## Read Stats');
119
+ sections.push(`- ${reads.count} files read, ${reads.repeats} repeated reads`);
120
+ }
121
+
122
+ sections.push('');
57
123
 
58
- fs.writeFileSync(deltaPath, content, 'utf8');
124
+ fs.writeFileSync(deltaPath, sections.join('\n'), 'utf8');
59
125
 
60
126
  // Append to log.md
61
127
  const logPath = path.join(baseDir, 'log.md');
package/package.json CHANGED
@@ -1,17 +1,22 @@
1
1
  {
2
2
  "name": "mindlore",
3
- "version": "0.1.0",
3
+ "version": "0.2.1",
4
4
  "description": "AI-native knowledge system for Claude Code",
5
5
  "type": "commonjs",
6
6
  "bin": {
7
- "mindlore": "scripts/init.cjs"
7
+ "mindlore": "dist/scripts/init.js"
8
8
  },
9
9
  "scripts": {
10
+ "build": "tsc",
11
+ "prebuild": "node -e \"require('fs').rmSync('dist',{recursive:true,force:true})\"",
12
+ "pretest": "npm run build",
10
13
  "test": "jest --config jest.config.cjs",
11
14
  "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
+ "typecheck": "tsc --noEmit",
16
+ "prepublishOnly": "npm run build",
17
+ "health": "node dist/scripts/mindlore-health-check.js",
18
+ "index": "node dist/scripts/mindlore-fts5-index.js",
19
+ "search": "node dist/scripts/mindlore-fts5-search.js"
15
20
  },
16
21
  "keywords": [
17
22
  "claude-code",
@@ -40,12 +45,19 @@
40
45
  "better-sqlite3": "^11.0.0"
41
46
  },
42
47
  "devDependencies": {
48
+ "@types/better-sqlite3": "^7.6.13",
49
+ "@types/jest": "^30.0.0",
50
+ "@types/node": "^25.6.0",
51
+ "@typescript-eslint/eslint-plugin": "^8.58.1",
52
+ "@typescript-eslint/parser": "^8.58.1",
43
53
  "eslint": "^9.0.0",
54
+ "globals": "^15.0.0",
44
55
  "jest": "^29.7.0",
45
- "globals": "^15.0.0"
56
+ "ts-jest": "^29.4.9",
57
+ "typescript": "^6.0.2"
46
58
  },
47
59
  "files": [
48
- "scripts/",
60
+ "dist/",
49
61
  "hooks/",
50
62
  "skills/",
51
63
  "templates/",
package/plugin.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "mindlore",
3
3
  "description": "AI-native knowledge system for Claude Code. Persistent, searchable, evolving knowledge base with FTS5.",
4
- "version": "0.0.1",
4
+ "version": "0.2.0",
5
5
  "skills": [
6
6
  {
7
7
  "name": "mindlore-ingest",
@@ -11,7 +11,22 @@
11
11
  {
12
12
  "name": "mindlore-health",
13
13
  "path": "skills/mindlore-health/SKILL.md",
14
- "description": "Run 16-point structural health check on .mindlore/ knowledge base"
14
+ "description": "Run 18-point structural health check on .mindlore/ knowledge base"
15
+ },
16
+ {
17
+ "name": "mindlore-query",
18
+ "path": "skills/mindlore-query/SKILL.md",
19
+ "description": "Search, ask, stats, brief — compounding knowledge pipeline"
20
+ },
21
+ {
22
+ "name": "mindlore-log",
23
+ "path": "skills/mindlore-log/SKILL.md",
24
+ "description": "Session logging, pattern extraction, wiki updates"
25
+ },
26
+ {
27
+ "name": "mindlore-decide",
28
+ "path": "skills/mindlore-decide/SKILL.md",
29
+ "description": "Record and list decisions with context, alternatives, rationale"
15
30
  }
16
31
  ],
17
32
  "hooks": [
@@ -23,6 +38,10 @@
23
38
  "event": "UserPromptSubmit",
24
39
  "script": "hooks/mindlore-search.cjs"
25
40
  },
41
+ {
42
+ "event": "UserPromptSubmit",
43
+ "script": "hooks/mindlore-decision-detector.cjs"
44
+ },
26
45
  {
27
46
  "event": "FileChanged",
28
47
  "script": "hooks/mindlore-index.cjs"
@@ -42,6 +61,11 @@
42
61
  {
43
62
  "event": "PostCompact",
44
63
  "script": "hooks/mindlore-post-compact.cjs"
64
+ },
65
+ {
66
+ "event": "PreToolUse",
67
+ "script": "hooks/mindlore-read-guard.cjs",
68
+ "if": "Read"
45
69
  }
46
70
  ]
47
71
  }