mindlore 0.6.8 → 0.7.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 (209) hide show
  1. package/README.md +259 -259
  2. package/SCHEMA.md +292 -292
  3. package/dist/scripts/cc-memory-bulk-sync.d.ts.map +1 -1
  4. package/dist/scripts/cc-memory-bulk-sync.js +4 -2
  5. package/dist/scripts/cc-memory-bulk-sync.js.map +1 -1
  6. package/dist/scripts/cc-session-sync.d.ts.map +1 -1
  7. package/dist/scripts/cc-session-sync.js +9 -7
  8. package/dist/scripts/cc-session-sync.js.map +1 -1
  9. package/dist/scripts/fetch-raw.js +5 -4
  10. package/dist/scripts/fetch-raw.js.map +1 -1
  11. package/dist/scripts/init.js +16 -12
  12. package/dist/scripts/init.js.map +1 -1
  13. package/dist/scripts/lib/consolidation.js +10 -10
  14. package/dist/scripts/lib/constants.d.ts +3 -0
  15. package/dist/scripts/lib/constants.d.ts.map +1 -1
  16. package/dist/scripts/lib/constants.js +11 -1
  17. package/dist/scripts/lib/constants.js.map +1 -1
  18. package/dist/scripts/lib/decay.js +9 -9
  19. package/dist/scripts/lib/episodes.js +23 -23
  20. package/dist/scripts/lib/err-msg.d.ts +2 -0
  21. package/dist/scripts/lib/err-msg.d.ts.map +1 -0
  22. package/dist/scripts/lib/err-msg.js +7 -0
  23. package/dist/scripts/lib/err-msg.js.map +1 -0
  24. package/dist/scripts/lib/mcp-namespace.d.ts +2 -0
  25. package/dist/scripts/lib/mcp-namespace.d.ts.map +1 -0
  26. package/dist/scripts/lib/mcp-namespace.js +21 -0
  27. package/dist/scripts/lib/mcp-namespace.js.map +1 -0
  28. package/dist/scripts/lib/mcp-telemetry.d.ts +11 -0
  29. package/dist/scripts/lib/mcp-telemetry.d.ts.map +1 -0
  30. package/dist/scripts/lib/mcp-telemetry.js +37 -0
  31. package/dist/scripts/lib/mcp-telemetry.js.map +1 -0
  32. package/dist/scripts/lib/mcp-tools.d.ts +10 -0
  33. package/dist/scripts/lib/mcp-tools.d.ts.map +1 -0
  34. package/dist/scripts/lib/mcp-tools.js +121 -0
  35. package/dist/scripts/lib/mcp-tools.js.map +1 -0
  36. package/dist/scripts/lib/migrations-v061.js +21 -21
  37. package/dist/scripts/lib/migrations-v062.js +11 -11
  38. package/dist/scripts/lib/migrations-v063.js +14 -14
  39. package/dist/scripts/lib/migrations-v067.js +11 -11
  40. package/dist/scripts/lib/rrf.d.ts.map +1 -1
  41. package/dist/scripts/lib/rrf.js +2 -1
  42. package/dist/scripts/lib/rrf.js.map +1 -1
  43. package/dist/scripts/lib/schema-version.js +6 -6
  44. package/dist/scripts/lib/search-cache.js +11 -11
  45. package/dist/scripts/lib/search-engine.d.ts +1 -0
  46. package/dist/scripts/lib/search-engine.d.ts.map +1 -1
  47. package/dist/scripts/lib/search-engine.js +9 -5
  48. package/dist/scripts/lib/search-engine.js.map +1 -1
  49. package/dist/scripts/lib/secure-io.d.ts +11 -0
  50. package/dist/scripts/lib/secure-io.d.ts.map +1 -0
  51. package/dist/scripts/lib/secure-io.js +26 -0
  52. package/dist/scripts/lib/secure-io.js.map +1 -0
  53. package/dist/scripts/lib/session-payload.js +7 -7
  54. package/dist/scripts/lib/slugify.d.ts +2 -0
  55. package/dist/scripts/lib/slugify.d.ts.map +1 -0
  56. package/dist/scripts/lib/slugify.js +13 -0
  57. package/dist/scripts/lib/slugify.js.map +1 -0
  58. package/dist/scripts/lib/smart-snippet.d.ts +9 -0
  59. package/dist/scripts/lib/smart-snippet.d.ts.map +1 -0
  60. package/dist/scripts/lib/smart-snippet.js +47 -0
  61. package/dist/scripts/lib/smart-snippet.js.map +1 -0
  62. package/dist/scripts/lib/tool-adapters/brief-adapter.d.ts +15 -0
  63. package/dist/scripts/lib/tool-adapters/brief-adapter.d.ts.map +1 -0
  64. package/dist/scripts/lib/tool-adapters/brief-adapter.js +66 -0
  65. package/dist/scripts/lib/tool-adapters/brief-adapter.js.map +1 -0
  66. package/dist/scripts/lib/tool-adapters/decide-adapter.d.ts +31 -0
  67. package/dist/scripts/lib/tool-adapters/decide-adapter.d.ts.map +1 -0
  68. package/dist/scripts/lib/tool-adapters/decide-adapter.js +71 -0
  69. package/dist/scripts/lib/tool-adapters/decide-adapter.js.map +1 -0
  70. package/dist/scripts/lib/tool-adapters/ingest-adapter.d.ts +16 -0
  71. package/dist/scripts/lib/tool-adapters/ingest-adapter.d.ts.map +1 -0
  72. package/dist/scripts/lib/tool-adapters/ingest-adapter.js +58 -0
  73. package/dist/scripts/lib/tool-adapters/ingest-adapter.js.map +1 -0
  74. package/dist/scripts/lib/tool-adapters/recall-adapter.d.ts +20 -0
  75. package/dist/scripts/lib/tool-adapters/recall-adapter.d.ts.map +1 -0
  76. package/dist/scripts/lib/tool-adapters/recall-adapter.js +69 -0
  77. package/dist/scripts/lib/tool-adapters/recall-adapter.js.map +1 -0
  78. package/dist/scripts/lib/tool-adapters/search-adapter.d.ts +22 -0
  79. package/dist/scripts/lib/tool-adapters/search-adapter.d.ts.map +1 -0
  80. package/dist/scripts/lib/tool-adapters/search-adapter.js +32 -0
  81. package/dist/scripts/lib/tool-adapters/search-adapter.js.map +1 -0
  82. package/dist/scripts/lib/tool-adapters/stats-adapter.d.ts +15 -0
  83. package/dist/scripts/lib/tool-adapters/stats-adapter.d.ts.map +1 -0
  84. package/dist/scripts/lib/tool-adapters/stats-adapter.js +66 -0
  85. package/dist/scripts/lib/tool-adapters/stats-adapter.js.map +1 -0
  86. package/dist/scripts/lib/triage.js +3 -3
  87. package/dist/scripts/lib/validate-manifest.d.ts +8 -0
  88. package/dist/scripts/lib/validate-manifest.d.ts.map +1 -0
  89. package/dist/scripts/lib/validate-manifest.js +80 -0
  90. package/dist/scripts/lib/validate-manifest.js.map +1 -0
  91. package/dist/scripts/maintain-cleanup.d.ts.map +1 -1
  92. package/dist/scripts/maintain-cleanup.js +3 -2
  93. package/dist/scripts/maintain-cleanup.js.map +1 -1
  94. package/dist/scripts/mcp-server.d.ts +3 -0
  95. package/dist/scripts/mcp-server.d.ts.map +1 -0
  96. package/dist/scripts/mcp-server.js +85 -0
  97. package/dist/scripts/mcp-server.js.map +1 -0
  98. package/dist/scripts/mindlore-backup.js +9 -9
  99. package/dist/scripts/mindlore-doctor.d.ts.map +1 -1
  100. package/dist/scripts/mindlore-doctor.js +4 -6
  101. package/dist/scripts/mindlore-doctor.js.map +1 -1
  102. package/dist/scripts/mindlore-fts5-index.js +12 -12
  103. package/dist/scripts/mindlore-fts5-index.js.map +1 -1
  104. package/dist/scripts/mindlore-health-check.d.ts.map +1 -1
  105. package/dist/scripts/mindlore-health-check.js +2 -2
  106. package/dist/scripts/mindlore-health-check.js.map +1 -1
  107. package/dist/scripts/validate-manifest-cli.d.ts +2 -0
  108. package/dist/scripts/validate-manifest-cli.d.ts.map +1 -0
  109. package/dist/scripts/validate-manifest-cli.js +38 -0
  110. package/dist/scripts/validate-manifest-cli.js.map +1 -0
  111. package/dist/tests/cc-memory-sync.test.js +3 -3
  112. package/dist/tests/chunks-migration.test.js +1 -1
  113. package/dist/tests/compaction-snapshot.test.js +2 -2
  114. package/dist/tests/consolidation.test.js +3 -3
  115. package/dist/tests/decay.test.js +9 -9
  116. package/dist/tests/diary.test.js +4 -4
  117. package/dist/tests/episode-file.test.js +2 -1
  118. package/dist/tests/episode-file.test.js.map +1 -1
  119. package/dist/tests/episodes-inject.test.js +9 -9
  120. package/dist/tests/err-msg.test.d.ts +2 -0
  121. package/dist/tests/err-msg.test.d.ts.map +1 -0
  122. package/dist/tests/err-msg.test.js +24 -0
  123. package/dist/tests/err-msg.test.js.map +1 -0
  124. package/dist/tests/fetch-raw.test.js +1 -2
  125. package/dist/tests/fetch-raw.test.js.map +1 -1
  126. package/dist/tests/fts5.test.js +75 -75
  127. package/dist/tests/fuzzy.test.js +1 -1
  128. package/dist/tests/git-snapshot.test.js +3 -5
  129. package/dist/tests/git-snapshot.test.js.map +1 -1
  130. package/dist/tests/helpers/db.d.ts +1 -2
  131. package/dist/tests/helpers/db.d.ts.map +1 -1
  132. package/dist/tests/helpers/db.js +18 -26
  133. package/dist/tests/helpers/db.js.map +1 -1
  134. package/dist/tests/manifest-v2.test.d.ts +2 -0
  135. package/dist/tests/manifest-v2.test.d.ts.map +1 -0
  136. package/dist/tests/manifest-v2.test.js +67 -0
  137. package/dist/tests/manifest-v2.test.js.map +1 -0
  138. package/dist/tests/mcp-server.test.d.ts +2 -0
  139. package/dist/tests/mcp-server.test.d.ts.map +1 -0
  140. package/dist/tests/mcp-server.test.js +118 -0
  141. package/dist/tests/mcp-server.test.js.map +1 -0
  142. package/dist/tests/mcp-tools.test.d.ts +2 -0
  143. package/dist/tests/mcp-tools.test.d.ts.map +1 -0
  144. package/dist/tests/mcp-tools.test.js +243 -0
  145. package/dist/tests/mcp-tools.test.js.map +1 -0
  146. package/dist/tests/migrations-v053.test.js +16 -16
  147. package/dist/tests/migrations-v061.test.js +10 -10
  148. package/dist/tests/migrations-v063.test.js +4 -4
  149. package/dist/tests/migrations-v068.test.js +6 -3
  150. package/dist/tests/migrations-v068.test.js.map +1 -1
  151. package/dist/tests/nomination-counts.test.js +6 -6
  152. package/dist/tests/nomination-counts.test.js.map +1 -1
  153. package/dist/tests/pre-compact.test.js +2 -1
  154. package/dist/tests/pre-compact.test.js.map +1 -1
  155. package/dist/tests/recall-telemetry.test.js +12 -11
  156. package/dist/tests/recall-telemetry.test.js.map +1 -1
  157. package/dist/tests/rrf.test.js +3 -3
  158. package/dist/tests/search-engine.test.js +1 -1
  159. package/dist/tests/search-hook.test.js +2 -2
  160. package/dist/tests/search-offload.test.js +1 -1
  161. package/dist/tests/search-offload.test.js.map +1 -1
  162. package/dist/tests/secure-io.test.d.ts +2 -0
  163. package/dist/tests/secure-io.test.d.ts.map +1 -0
  164. package/dist/tests/secure-io.test.js +65 -0
  165. package/dist/tests/secure-io.test.js.map +1 -0
  166. package/dist/tests/session-payload.test.js +1 -1
  167. package/dist/tests/session-summary.test.js +1 -1
  168. package/dist/tests/similarity.test.js +1 -1
  169. package/dist/tests/skill-path-resolution.test.js +22 -3
  170. package/dist/tests/skill-path-resolution.test.js.map +1 -1
  171. package/dist/tests/smart-snippet.test.d.ts +2 -0
  172. package/dist/tests/smart-snippet.test.d.ts.map +1 -0
  173. package/dist/tests/smart-snippet.test.js +67 -0
  174. package/dist/tests/smart-snippet.test.js.map +1 -0
  175. package/dist/tests/triage.test.js +1 -1
  176. package/hooks/lib/constants.cjs +15 -15
  177. package/hooks/lib/mindlore-common.cjs +975 -974
  178. package/hooks/mindlore-cwd-changed.cjs +57 -57
  179. package/hooks/mindlore-decision-detector.cjs +54 -54
  180. package/hooks/mindlore-dont-repeat.cjs +222 -222
  181. package/hooks/mindlore-fts5-sync.cjs +98 -97
  182. package/hooks/mindlore-index.cjs +230 -229
  183. package/hooks/mindlore-model-router.cjs +54 -54
  184. package/hooks/mindlore-post-compact.cjs +69 -69
  185. package/hooks/mindlore-post-read.cjs +106 -106
  186. package/hooks/mindlore-pre-compact.cjs +154 -154
  187. package/hooks/mindlore-read-guard.cjs +105 -105
  188. package/hooks/mindlore-research-guard.cjs +176 -176
  189. package/hooks/mindlore-search.cjs +200 -200
  190. package/hooks/mindlore-session-end.cjs +510 -509
  191. package/hooks/mindlore-session-focus.cjs +256 -256
  192. package/package.json +77 -75
  193. package/plugin.json +8 -1
  194. package/skills/mindlore-diary/SKILL.md +85 -85
  195. package/skills/mindlore-evolve/SKILL.md +126 -126
  196. package/skills/mindlore-explore/SKILL.md +109 -109
  197. package/skills/mindlore-ingest/SKILL.md +195 -195
  198. package/skills/mindlore-maintain/SKILL.md +125 -125
  199. package/skills/mindlore-query/SKILL.md +151 -151
  200. package/skills/mindlore-reflect/SKILL.md +141 -141
  201. package/skills/mindlore-stats/SKILL.md +106 -106
  202. package/templates/INDEX.md +14 -14
  203. package/templates/SCHEMA.md +292 -292
  204. package/templates/config.json +1 -1
  205. package/templates/extraction/article.md +15 -15
  206. package/templates/extraction/changelog.md +15 -15
  207. package/templates/extraction/default.md +15 -15
  208. package/templates/extraction/docs.md +15 -15
  209. package/templates/extraction/github-repo.md +17 -17
@@ -1,69 +1,69 @@
1
- #!/usr/bin/env node
2
- 'use strict';
3
-
4
- /**
5
- * mindlore-post-compact — PostCompact hook
6
- *
7
- * After context compaction, re-inject session context:
8
- * 1. Read INDEX.md
9
- * 2. Read latest delta
10
- * 3. Inject via stdout (same as session-focus)
11
- *
12
- * This ensures the agent has knowledge context after compaction.
13
- */
14
-
15
- const fs = require('fs');
16
- const path = require('path');
17
- const { findMindloreDir, getLatestDelta, readConfig, hookLog, withTelemetry } = require('./lib/mindlore-common.cjs');
18
-
19
- function main() {
20
- const baseDir = findMindloreDir();
21
- if (!baseDir) return;
22
-
23
- const output = [];
24
-
25
- // Re-inject INDEX.md
26
- const indexPath = path.join(baseDir, 'INDEX.md');
27
- if (fs.existsSync(indexPath)) {
28
- const content = fs.readFileSync(indexPath, 'utf8').trim();
29
- output.push(`[Mindlore INDEX (post-compact)]\n${content}`);
30
- }
31
-
32
- const diaryDir = path.join(baseDir, 'diary');
33
-
34
- const latestDelta = getLatestDelta(diaryDir);
35
- if (latestDelta) {
36
- const deltaContent = fs.readFileSync(latestDelta, 'utf8').trim();
37
- const deltaName = path.basename(latestDelta);
38
- output.push(`[Mindlore Delta (post-compact): ${deltaName}]\n${deltaContent}`);
39
- }
40
-
41
- try {
42
- const snapshots = fs.readdirSync(diaryDir)
43
- .filter(f => f.startsWith('compaction-snapshot-'))
44
- .sort();
45
- if (snapshots.length > 0) {
46
- const latestSnapshot = snapshots[snapshots.length - 1];
47
- const snapshotContent = fs.readFileSync(
48
- path.join(diaryDir, latestSnapshot), 'utf8'
49
- ).trim();
50
- output.push(`[Mindlore Compaction Resume]\n${snapshotContent}`);
51
- }
52
- } catch (_err) { /* snapshot best-effort */ }
53
-
54
- if (output.length > 0) {
55
- const config = readConfig(baseDir);
56
- const budgetConfig = config?.tokenBudget ?? {};
57
- const maxInjectChars = (budgetConfig.sessionInject || 2000) * 4;
58
- let joined = output.join('\n\n');
59
- if (joined.length > maxInjectChars) {
60
- joined = joined.slice(0, maxInjectChars) + '\n[...truncated by token budget]';
61
- }
62
- process.stdout.write(joined + '\n');
63
- }
64
- }
65
-
66
- withTelemetry('mindlore-post-compact', main).catch(err => {
67
- hookLog('mindlore-post-compact', 'error', err?.message ?? String(err));
68
- process.exit(0);
69
- });
1
+ #!/usr/bin/env node
2
+ 'use strict';
3
+
4
+ /**
5
+ * mindlore-post-compact — PostCompact hook
6
+ *
7
+ * After context compaction, re-inject session context:
8
+ * 1. Read INDEX.md
9
+ * 2. Read latest delta
10
+ * 3. Inject via stdout (same as session-focus)
11
+ *
12
+ * This ensures the agent has knowledge context after compaction.
13
+ */
14
+
15
+ const fs = require('fs');
16
+ const path = require('path');
17
+ const { findMindloreDir, getLatestDelta, readConfig, hookLog, withTelemetry } = require('./lib/mindlore-common.cjs');
18
+
19
+ function main() {
20
+ const baseDir = findMindloreDir();
21
+ if (!baseDir) return;
22
+
23
+ const output = [];
24
+
25
+ // Re-inject INDEX.md
26
+ const indexPath = path.join(baseDir, 'INDEX.md');
27
+ if (fs.existsSync(indexPath)) {
28
+ const content = fs.readFileSync(indexPath, 'utf8').trim();
29
+ output.push(`[Mindlore INDEX (post-compact)]\n${content}`);
30
+ }
31
+
32
+ const diaryDir = path.join(baseDir, 'diary');
33
+
34
+ const latestDelta = getLatestDelta(diaryDir);
35
+ if (latestDelta) {
36
+ const deltaContent = fs.readFileSync(latestDelta, 'utf8').trim();
37
+ const deltaName = path.basename(latestDelta);
38
+ output.push(`[Mindlore Delta (post-compact): ${deltaName}]\n${deltaContent}`);
39
+ }
40
+
41
+ try {
42
+ const snapshots = fs.readdirSync(diaryDir)
43
+ .filter(f => f.startsWith('compaction-snapshot-'))
44
+ .sort();
45
+ if (snapshots.length > 0) {
46
+ const latestSnapshot = snapshots[snapshots.length - 1];
47
+ const snapshotContent = fs.readFileSync(
48
+ path.join(diaryDir, latestSnapshot), 'utf8'
49
+ ).trim();
50
+ output.push(`[Mindlore Compaction Resume]\n${snapshotContent}`);
51
+ }
52
+ } catch (_err) { /* snapshot best-effort */ }
53
+
54
+ if (output.length > 0) {
55
+ const config = readConfig(baseDir);
56
+ const budgetConfig = config?.tokenBudget ?? {};
57
+ const maxInjectChars = (budgetConfig.sessionInject || 2000) * 4;
58
+ let joined = output.join('\n\n');
59
+ if (joined.length > maxInjectChars) {
60
+ joined = joined.slice(0, maxInjectChars) + '\n[...truncated by token budget]';
61
+ }
62
+ process.stdout.write(joined + '\n');
63
+ }
64
+ }
65
+
66
+ withTelemetry('mindlore-post-compact', main).catch(err => {
67
+ hookLog('mindlore-post-compact', 'error', err?.message ?? String(err));
68
+ process.exit(0);
69
+ });
@@ -1,106 +1,106 @@
1
- #!/usr/bin/env node
2
- 'use strict';
3
-
4
- /**
5
- * mindlore-post-read — PostToolUse hook (matcher: "Read")
6
- *
7
- * After a file is read, estimate its token count
8
- * and store in _session-reads.json for the read-guard to reference.
9
- *
10
- * Outputs token estimate via additionalContext JSON.
11
- * Also stores token info in _session-reads.json for read-guard.
12
- */
13
-
14
- const fs = require('fs');
15
- const path = require('path');
16
- const { findMindloreDir, getProjectName, hookLog, withTelemetry } = require('./lib/mindlore-common.cjs');
17
-
18
- const CODE_EXTS = new Set(['.ts', '.tsx', '.js', '.jsx', '.py', '.rs', '.go', '.java', '.c', '.cpp', '.h', '.css', '.scss', '.sql', '.sh', '.yaml', '.yml', '.json', '.toml', '.xml', '.cjs', '.mjs']);
19
- const PROSE_EXTS = new Set(['.md', '.txt', '.rst', '.adoc']);
20
-
21
- function estimateTokens(charCount, ext) {
22
- const ratio = CODE_EXTS.has(ext) ? 3.5 : PROSE_EXTS.has(ext) ? 4.0 : 3.75;
23
- return Math.ceil(charCount / ratio);
24
- }
25
-
26
- function main() {
27
- const baseDir = findMindloreDir();
28
- if (!baseDir) return;
29
-
30
- let input = '';
31
- const stdinTimeout = setTimeout(() => process.exit(0), 3000);
32
- process.stdin.setEncoding('utf8');
33
- process.stdin.on('error', () => process.exit(0));
34
- process.stdin.on('data', chunk => input += chunk);
35
- process.stdin.on('end', () => {
36
- clearTimeout(stdinTimeout);
37
- try {
38
- const data = JSON.parse(input || '{}');
39
- const toolInput = data.tool_input || {};
40
- const toolOutput = data.tool_output || {};
41
-
42
- const filePath = toolInput.file_path || toolInput.path || '';
43
- if (!filePath) return process.exit(0);
44
-
45
- // Skip .mindlore/ internals
46
- const resolved = path.resolve(filePath);
47
- if (resolved.startsWith(path.resolve(baseDir))) return process.exit(0);
48
-
49
- // Get content length from tool output or read file
50
- let charCount = 0;
51
- if (toolOutput.content) {
52
- charCount = typeof toolOutput.content === 'string'
53
- ? toolOutput.content.length
54
- : JSON.stringify(toolOutput.content).length;
55
- } else {
56
- // Fallback: read file size
57
- try {
58
- const stat = fs.statSync(resolved);
59
- charCount = stat.size;
60
- } catch { return process.exit(0); }
61
- }
62
-
63
- if (charCount === 0) return process.exit(0);
64
-
65
- const ext = path.extname(filePath).toLowerCase();
66
- const tokens = estimateTokens(charCount, ext);
67
-
68
- // Update _session-reads.json with token info
69
- const diaryDir = path.join(baseDir, 'diary');
70
- const readsPath = path.join(diaryDir, `_session-reads-${getProjectName()}.json`);
71
- let reads = {};
72
- if (fs.existsSync(readsPath)) {
73
- try { reads = JSON.parse(fs.readFileSync(readsPath, 'utf8')); } catch { reads = {}; }
74
- }
75
-
76
- const normalizedPath = path.resolve(filePath);
77
- const key = normalizedPath;
78
-
79
- if (typeof reads[key] === 'number') {
80
- // Upgrade from old format (just count) to new format (object)
81
- reads[key] = { count: reads[key], tokens, chars: charCount };
82
- } else if (reads[key] && typeof reads[key] === 'object') {
83
- reads[key].tokens = tokens;
84
- reads[key].chars = charCount;
85
- } else {
86
- reads[key] = { count: 1, tokens, chars: charCount };
87
- }
88
-
89
- fs.writeFileSync(readsPath, JSON.stringify(reads, null, 2), 'utf8');
90
-
91
- // Output token estimate to Claude via additionalContext
92
- const basename = path.basename(filePath);
93
- process.stdout.write(JSON.stringify({
94
- hookSpecificOutput: {
95
- hookEventName: 'PostToolUse',
96
- additionalContext: `[Mindlore: ${basename} — ~${tokens} token (${charCount} char). Edit etmeyeceksen ctx_execute_file kullan.]`
97
- }
98
- }));
99
- } catch {
100
- // Silent fail
101
- }
102
- process.exit(0);
103
- });
104
- }
105
-
106
- withTelemetry('mindlore-post-read', main).catch(err => { hookLog('post-read', 'error', err?.message ?? String(err)); });
1
+ #!/usr/bin/env node
2
+ 'use strict';
3
+
4
+ /**
5
+ * mindlore-post-read — PostToolUse hook (matcher: "Read")
6
+ *
7
+ * After a file is read, estimate its token count
8
+ * and store in _session-reads.json for the read-guard to reference.
9
+ *
10
+ * Outputs token estimate via additionalContext JSON.
11
+ * Also stores token info in _session-reads.json for read-guard.
12
+ */
13
+
14
+ const fs = require('fs');
15
+ const path = require('path');
16
+ const { findMindloreDir, getProjectName, hookLog, withTelemetry } = require('./lib/mindlore-common.cjs');
17
+
18
+ const CODE_EXTS = new Set(['.ts', '.tsx', '.js', '.jsx', '.py', '.rs', '.go', '.java', '.c', '.cpp', '.h', '.css', '.scss', '.sql', '.sh', '.yaml', '.yml', '.json', '.toml', '.xml', '.cjs', '.mjs']);
19
+ const PROSE_EXTS = new Set(['.md', '.txt', '.rst', '.adoc']);
20
+
21
+ function estimateTokens(charCount, ext) {
22
+ const ratio = CODE_EXTS.has(ext) ? 3.5 : PROSE_EXTS.has(ext) ? 4.0 : 3.75;
23
+ return Math.ceil(charCount / ratio);
24
+ }
25
+
26
+ function main() {
27
+ const baseDir = findMindloreDir();
28
+ if (!baseDir) return;
29
+
30
+ let input = '';
31
+ const stdinTimeout = setTimeout(() => process.exit(0), 3000);
32
+ process.stdin.setEncoding('utf8');
33
+ process.stdin.on('error', () => process.exit(0));
34
+ process.stdin.on('data', chunk => input += chunk);
35
+ process.stdin.on('end', () => {
36
+ clearTimeout(stdinTimeout);
37
+ try {
38
+ const data = JSON.parse(input || '{}');
39
+ const toolInput = data.tool_input || {};
40
+ const toolOutput = data.tool_output || {};
41
+
42
+ const filePath = toolInput.file_path || toolInput.path || '';
43
+ if (!filePath) return process.exit(0);
44
+
45
+ // Skip .mindlore/ internals
46
+ const resolved = path.resolve(filePath);
47
+ if (resolved.startsWith(path.resolve(baseDir))) return process.exit(0);
48
+
49
+ // Get content length from tool output or read file
50
+ let charCount = 0;
51
+ if (toolOutput.content) {
52
+ charCount = typeof toolOutput.content === 'string'
53
+ ? toolOutput.content.length
54
+ : JSON.stringify(toolOutput.content).length;
55
+ } else {
56
+ // Fallback: read file size
57
+ try {
58
+ const stat = fs.statSync(resolved);
59
+ charCount = stat.size;
60
+ } catch { return process.exit(0); }
61
+ }
62
+
63
+ if (charCount === 0) return process.exit(0);
64
+
65
+ const ext = path.extname(filePath).toLowerCase();
66
+ const tokens = estimateTokens(charCount, ext);
67
+
68
+ // Update _session-reads.json with token info
69
+ const diaryDir = path.join(baseDir, 'diary');
70
+ const readsPath = path.join(diaryDir, `_session-reads-${getProjectName()}.json`);
71
+ let reads = {};
72
+ if (fs.existsSync(readsPath)) {
73
+ try { reads = JSON.parse(fs.readFileSync(readsPath, 'utf8')); } catch { reads = {}; }
74
+ }
75
+
76
+ const normalizedPath = path.resolve(filePath);
77
+ const key = normalizedPath;
78
+
79
+ if (typeof reads[key] === 'number') {
80
+ // Upgrade from old format (just count) to new format (object)
81
+ reads[key] = { count: reads[key], tokens, chars: charCount };
82
+ } else if (reads[key] && typeof reads[key] === 'object') {
83
+ reads[key].tokens = tokens;
84
+ reads[key].chars = charCount;
85
+ } else {
86
+ reads[key] = { count: 1, tokens, chars: charCount };
87
+ }
88
+
89
+ fs.writeFileSync(readsPath, JSON.stringify(reads, null, 2), 'utf8');
90
+
91
+ // Output token estimate to Claude via additionalContext
92
+ const basename = path.basename(filePath);
93
+ process.stdout.write(JSON.stringify({
94
+ hookSpecificOutput: {
95
+ hookEventName: 'PostToolUse',
96
+ additionalContext: `[Mindlore: ${basename} — ~${tokens} token (${charCount} char). Edit etmeyeceksen ctx_execute_file kullan.]`
97
+ }
98
+ }));
99
+ } catch {
100
+ // Silent fail
101
+ }
102
+ process.exit(0);
103
+ });
104
+ }
105
+
106
+ withTelemetry('mindlore-post-read', main).catch(err => { hookLog('post-read', 'error', err?.message ?? String(err)); });
@@ -1,154 +1,154 @@
1
- #!/usr/bin/env node
2
- 'use strict';
3
-
4
- /**
5
- * mindlore-pre-compact — PreCompact hook
6
- *
7
- * Before context compaction:
8
- * 1. Ensure FTS5 index is up to date
9
- * 2. Write pre-compact episode to episodes/ and append log entry
10
- */
11
-
12
- const fs = require('fs');
13
- const path = require('path');
14
- const { findMindloreDir, openDatabase, hookLog, withTelemetry, listSnapshots } = require('./lib/mindlore-common.cjs');
15
-
16
- function collectRecentEpisodes(baseDir) {
17
- try {
18
- const dbPath = path.join(baseDir, 'mindlore.db');
19
- const db = openDatabase(dbPath, { readonly: true });
20
- if (!db) return [];
21
- try {
22
- const episodes = db.prepare(
23
- "SELECT kind, summary FROM episodes WHERE created_at > datetime('now', '-4 hours') ORDER BY created_at DESC LIMIT 20"
24
- ).all();
25
- if (episodes.length === 0) return [];
26
- const grouped = {};
27
- for (const ep of episodes) {
28
- const kind = ep.kind || 'other';
29
- if (!grouped[kind]) grouped[kind] = [];
30
- grouped[kind].push(ep.summary);
31
- }
32
- const lines = ['## Session Episodes'];
33
- for (const [kind, items] of Object.entries(grouped)) {
34
- lines.push(`### ${kind}`);
35
- for (const item of items) lines.push(`- ${item}`);
36
- }
37
- return lines;
38
- } finally {
39
- db.close();
40
- }
41
- } catch (_err) {
42
- return [];
43
- }
44
- }
45
-
46
- function collectGitDiff() {
47
- try {
48
- const { execFileSync } = require('child_process');
49
- let diffStat = '';
50
- try {
51
- diffStat = execFileSync('git', ['diff', '--stat', 'HEAD'], { encoding: 'utf8', timeout: 5000, stdio: ['pipe', 'pipe', 'pipe'] }).trim();
52
- } catch { diffStat = ''; }
53
- if (diffStat) return ['## Changed Files (uncommitted)', '```', diffStat, '```'];
54
- return [];
55
- } catch (_err) {
56
- return [];
57
- }
58
- }
59
-
60
- function getActivePlan() {
61
- try {
62
- const plansDir = path.join(process.cwd(), '.claude', 'plans');
63
- if (!fs.existsSync(plansDir)) return [];
64
- const plans = fs.readdirSync(plansDir).filter(f => f.endsWith('.md'));
65
- if (plans.length === 0) return [];
66
- const latestPlan = plans.sort().pop();
67
- return [`## Active Plan: ${latestPlan}`];
68
- } catch (_err) {
69
- return [];
70
- }
71
- }
72
-
73
- function main() {
74
- const baseDir = findMindloreDir();
75
- if (!baseDir) return;
76
-
77
- // Trigger FTS5 sync via the index script
78
- const indexScript = path.join(__dirname, '..', 'scripts', 'mindlore-fts5-index.cjs');
79
- if (fs.existsSync(indexScript)) {
80
- try {
81
- const { spawnSync } = require('child_process');
82
- spawnSync('node', [indexScript, baseDir], {
83
- timeout: 10000,
84
- stdio: 'pipe',
85
- windowsHide: true,
86
- });
87
- } catch (_err) {
88
- // Non-fatal — index might fail if better-sqlite3 not available
89
- }
90
- }
91
-
92
- const now = new Date();
93
- const iso = now.toISOString();
94
- const ts = iso.replace(/[:.]/g, '-');
95
-
96
- const episodesDir = path.join(baseDir, 'episodes');
97
- try {
98
- const episodePath = path.join(episodesDir, `pre-compact-${ts}.md`);
99
- const content = [
100
- '---',
101
- 'type: episode',
102
- 'subtype: pre-compact',
103
- `date: ${iso.slice(0, 10)}`,
104
- `project: ${path.basename(process.cwd())}`,
105
- '---',
106
- '',
107
- `Pre-compact snapshot at ${iso}.`,
108
- `Working directory: ${process.cwd()}`,
109
- ].join('\n');
110
- fs.writeFileSync(episodePath, content, 'utf8');
111
- } catch (_err) { /* episodes dir may not exist */ }
112
-
113
- // Append log entry
114
- const logPath = path.join(baseDir, 'log.md');
115
- try {
116
- const entry = `| ${iso.slice(0, 10)} | pre-compact | FTS5 flush before compaction |\n`;
117
- fs.appendFileSync(logPath, entry, 'utf8');
118
- } catch (_err) { /* log file may not exist */ }
119
-
120
- // Build compaction snapshot (#17)
121
- const diaryDir = path.join(baseDir, 'diary');
122
- try {
123
- const sections = [];
124
- sections.push(...collectRecentEpisodes(baseDir));
125
- sections.push(...collectGitDiff());
126
- sections.push(...getActivePlan());
127
-
128
- if (sections.length > 0) {
129
- const snapshotContent = [
130
- '---',
131
- 'type: compaction-snapshot',
132
- `date: ${iso.slice(0, 10)}`,
133
- `project: ${path.basename(process.cwd())}`,
134
- '---',
135
- '',
136
- ...sections,
137
- ].join('\n');
138
- fs.writeFileSync(path.join(diaryDir, `compaction-snapshot-${ts}.md`), snapshotContent);
139
- }
140
-
141
- const snapshots = listSnapshots(diaryDir).filter(f => f.startsWith('compaction-'));
142
- while (snapshots.length > 5) {
143
- const oldest = snapshots.shift();
144
- if (oldest) fs.unlinkSync(path.join(diaryDir, oldest));
145
- }
146
- } catch (_err) { /* snapshot is best-effort */ }
147
-
148
- process.stdout.write('[Mindlore: pre-compact FTS5 flush complete]\n');
149
- }
150
-
151
- withTelemetry('mindlore-pre-compact', main).catch(err => {
152
- hookLog('mindlore-pre-compact', 'error', err?.message ?? String(err));
153
- process.exit(0);
154
- });
1
+ #!/usr/bin/env node
2
+ 'use strict';
3
+
4
+ /**
5
+ * mindlore-pre-compact — PreCompact hook
6
+ *
7
+ * Before context compaction:
8
+ * 1. Ensure FTS5 index is up to date
9
+ * 2. Write pre-compact episode to episodes/ and append log entry
10
+ */
11
+
12
+ const fs = require('fs');
13
+ const path = require('path');
14
+ const { findMindloreDir, openDatabase, hookLog, withTelemetry, listSnapshots } = require('./lib/mindlore-common.cjs');
15
+
16
+ function collectRecentEpisodes(baseDir) {
17
+ try {
18
+ const dbPath = path.join(baseDir, 'mindlore.db');
19
+ const db = openDatabase(dbPath, { readonly: true });
20
+ if (!db) return [];
21
+ try {
22
+ const episodes = db.prepare(
23
+ "SELECT kind, summary FROM episodes WHERE created_at > datetime('now', '-4 hours') ORDER BY created_at DESC LIMIT 20"
24
+ ).all();
25
+ if (episodes.length === 0) return [];
26
+ const grouped = {};
27
+ for (const ep of episodes) {
28
+ const kind = ep.kind || 'other';
29
+ if (!grouped[kind]) grouped[kind] = [];
30
+ grouped[kind].push(ep.summary);
31
+ }
32
+ const lines = ['## Session Episodes'];
33
+ for (const [kind, items] of Object.entries(grouped)) {
34
+ lines.push(`### ${kind}`);
35
+ for (const item of items) lines.push(`- ${item}`);
36
+ }
37
+ return lines;
38
+ } finally {
39
+ db.close();
40
+ }
41
+ } catch (_err) {
42
+ return [];
43
+ }
44
+ }
45
+
46
+ function collectGitDiff() {
47
+ try {
48
+ const { execFileSync } = require('child_process');
49
+ let diffStat = '';
50
+ try {
51
+ diffStat = execFileSync('git', ['diff', '--stat', 'HEAD'], { encoding: 'utf8', timeout: 5000, stdio: ['pipe', 'pipe', 'pipe'] }).trim();
52
+ } catch { diffStat = ''; }
53
+ if (diffStat) return ['## Changed Files (uncommitted)', '```', diffStat, '```'];
54
+ return [];
55
+ } catch (_err) {
56
+ return [];
57
+ }
58
+ }
59
+
60
+ function getActivePlan() {
61
+ try {
62
+ const plansDir = path.join(process.cwd(), '.claude', 'plans');
63
+ if (!fs.existsSync(plansDir)) return [];
64
+ const plans = fs.readdirSync(plansDir).filter(f => f.endsWith('.md'));
65
+ if (plans.length === 0) return [];
66
+ const latestPlan = plans.sort().pop();
67
+ return [`## Active Plan: ${latestPlan}`];
68
+ } catch (_err) {
69
+ return [];
70
+ }
71
+ }
72
+
73
+ function main() {
74
+ const baseDir = findMindloreDir();
75
+ if (!baseDir) return;
76
+
77
+ // Trigger FTS5 sync via the index script
78
+ const indexScript = path.join(__dirname, '..', 'scripts', 'mindlore-fts5-index.cjs');
79
+ if (fs.existsSync(indexScript)) {
80
+ try {
81
+ const { spawnSync } = require('child_process');
82
+ spawnSync('node', [indexScript, baseDir], {
83
+ timeout: 10000,
84
+ stdio: 'pipe',
85
+ windowsHide: true,
86
+ });
87
+ } catch (_err) {
88
+ // Non-fatal — index might fail if better-sqlite3 not available
89
+ }
90
+ }
91
+
92
+ const now = new Date();
93
+ const iso = now.toISOString();
94
+ const ts = iso.replace(/[:.]/g, '-');
95
+
96
+ const episodesDir = path.join(baseDir, 'episodes');
97
+ try {
98
+ const episodePath = path.join(episodesDir, `pre-compact-${ts}.md`);
99
+ const content = [
100
+ '---',
101
+ 'type: episode',
102
+ 'subtype: pre-compact',
103
+ `date: ${iso.slice(0, 10)}`,
104
+ `project: ${path.basename(process.cwd())}`,
105
+ '---',
106
+ '',
107
+ `Pre-compact snapshot at ${iso}.`,
108
+ `Working directory: ${process.cwd()}`,
109
+ ].join('\n');
110
+ fs.writeFileSync(episodePath, content, 'utf8');
111
+ } catch (_err) { /* episodes dir may not exist */ }
112
+
113
+ // Append log entry
114
+ const logPath = path.join(baseDir, 'log.md');
115
+ try {
116
+ const entry = `| ${iso.slice(0, 10)} | pre-compact | FTS5 flush before compaction |\n`;
117
+ fs.appendFileSync(logPath, entry, 'utf8');
118
+ } catch (_err) { /* log file may not exist */ }
119
+
120
+ // Build compaction snapshot (#17)
121
+ const diaryDir = path.join(baseDir, 'diary');
122
+ try {
123
+ const sections = [];
124
+ sections.push(...collectRecentEpisodes(baseDir));
125
+ sections.push(...collectGitDiff());
126
+ sections.push(...getActivePlan());
127
+
128
+ if (sections.length > 0) {
129
+ const snapshotContent = [
130
+ '---',
131
+ 'type: compaction-snapshot',
132
+ `date: ${iso.slice(0, 10)}`,
133
+ `project: ${path.basename(process.cwd())}`,
134
+ '---',
135
+ '',
136
+ ...sections,
137
+ ].join('\n');
138
+ fs.writeFileSync(path.join(diaryDir, `compaction-snapshot-${ts}.md`), snapshotContent);
139
+ }
140
+
141
+ const snapshots = listSnapshots(diaryDir).filter(f => f.startsWith('compaction-'));
142
+ while (snapshots.length > 5) {
143
+ const oldest = snapshots.shift();
144
+ if (oldest) fs.unlinkSync(path.join(diaryDir, oldest));
145
+ }
146
+ } catch (_err) { /* snapshot is best-effort */ }
147
+
148
+ process.stdout.write('[Mindlore: pre-compact FTS5 flush complete]\n');
149
+ }
150
+
151
+ withTelemetry('mindlore-pre-compact', main).catch(err => {
152
+ hookLog('mindlore-pre-compact', 'error', err?.message ?? String(err));
153
+ process.exit(0);
154
+ });