mindlore 0.6.7 → 0.6.8

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 (169) 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 +47 -42
  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 +58 -48
  8. package/dist/scripts/cc-session-sync.js.map +1 -1
  9. package/dist/scripts/init.js +8 -8
  10. package/dist/scripts/init.js.map +1 -1
  11. package/dist/scripts/lib/all-migrations.d.ts.map +1 -1
  12. package/dist/scripts/lib/all-migrations.js +4 -1
  13. package/dist/scripts/lib/all-migrations.js.map +1 -1
  14. package/dist/scripts/lib/consolidation.d.ts +4 -3
  15. package/dist/scripts/lib/consolidation.d.ts.map +1 -1
  16. package/dist/scripts/lib/consolidation.js +10 -10
  17. package/dist/scripts/lib/consolidation.js.map +1 -1
  18. package/dist/scripts/lib/constants.d.ts +1 -7
  19. package/dist/scripts/lib/constants.d.ts.map +1 -1
  20. package/dist/scripts/lib/constants.js +2 -9
  21. package/dist/scripts/lib/constants.js.map +1 -1
  22. package/dist/scripts/lib/db-helpers.d.ts +0 -15
  23. package/dist/scripts/lib/db-helpers.d.ts.map +1 -1
  24. package/dist/scripts/lib/db-helpers.js +1 -51
  25. package/dist/scripts/lib/db-helpers.js.map +1 -1
  26. package/dist/scripts/lib/decay.d.ts.map +1 -1
  27. package/dist/scripts/lib/decay.js +9 -9
  28. package/dist/scripts/lib/decay.js.map +1 -1
  29. package/dist/scripts/lib/episodes.js +23 -23
  30. package/dist/scripts/lib/migrations-v061.js +21 -21
  31. package/dist/scripts/lib/migrations-v062.js +11 -11
  32. package/dist/scripts/lib/migrations-v063.js +14 -14
  33. package/dist/scripts/lib/migrations-v067.js +11 -11
  34. package/dist/scripts/lib/migrations-v068.d.ts +3 -0
  35. package/dist/scripts/lib/migrations-v068.d.ts.map +1 -0
  36. package/dist/scripts/lib/migrations-v068.js +37 -0
  37. package/dist/scripts/lib/migrations-v068.js.map +1 -0
  38. package/dist/scripts/lib/migrations.d.ts.map +1 -1
  39. package/dist/scripts/lib/migrations.js +0 -15
  40. package/dist/scripts/lib/migrations.js.map +1 -1
  41. package/dist/scripts/lib/schema-version.js +6 -6
  42. package/dist/scripts/lib/search-cache.js +11 -11
  43. package/dist/scripts/lib/session-payload.d.ts.map +1 -1
  44. package/dist/scripts/lib/session-payload.js +7 -7
  45. package/dist/scripts/lib/session-payload.js.map +1 -1
  46. package/dist/scripts/lib/triage.js +3 -3
  47. package/dist/scripts/mindlore-backup.js +9 -9
  48. package/dist/scripts/mindlore-fts5-index.d.ts +1 -2
  49. package/dist/scripts/mindlore-fts5-index.d.ts.map +1 -1
  50. package/dist/scripts/mindlore-fts5-index.js +12 -64
  51. package/dist/scripts/mindlore-fts5-index.js.map +1 -1
  52. package/dist/scripts/mindlore-health-check.d.ts.map +1 -1
  53. package/dist/scripts/mindlore-health-check.js +0 -11
  54. package/dist/scripts/mindlore-health-check.js.map +1 -1
  55. package/dist/tests/cc-memory-bulk-sync.test.js +23 -0
  56. package/dist/tests/cc-memory-bulk-sync.test.js.map +1 -1
  57. package/dist/tests/cc-session-sync.test.js +25 -0
  58. package/dist/tests/cc-session-sync.test.js.map +1 -1
  59. package/dist/tests/compaction-snapshot.test.js +2 -2
  60. package/dist/tests/consolidation.test.js +5 -5
  61. package/dist/tests/consolidation.test.js.map +1 -1
  62. package/dist/tests/decay.test.js +9 -9
  63. package/dist/tests/diary.test.js +4 -4
  64. package/dist/tests/episode-kind-constant.test.d.ts +2 -0
  65. package/dist/tests/episode-kind-constant.test.d.ts.map +1 -0
  66. package/dist/tests/episode-kind-constant.test.js +28 -0
  67. package/dist/tests/episode-kind-constant.test.js.map +1 -0
  68. package/dist/tests/episodes-inject.test.js +9 -9
  69. package/dist/tests/fts5.test.js +66 -125
  70. package/dist/tests/fts5.test.js.map +1 -1
  71. package/dist/tests/globalSetup.d.ts +2 -0
  72. package/dist/tests/globalSetup.d.ts.map +1 -0
  73. package/dist/tests/globalSetup.js +36 -0
  74. package/dist/tests/globalSetup.js.map +1 -0
  75. package/dist/tests/helpers/db.d.ts +13 -5
  76. package/dist/tests/helpers/db.d.ts.map +1 -1
  77. package/dist/tests/helpers/db.js +60 -33
  78. package/dist/tests/helpers/db.js.map +1 -1
  79. package/dist/tests/lesson-graduation.test.js +11 -11
  80. package/dist/tests/lesson-graduation.test.js.map +1 -1
  81. package/dist/tests/migrations-v053.test.js +16 -16
  82. package/dist/tests/migrations-v061.test.js +10 -10
  83. package/dist/tests/migrations-v063.test.js +2 -2
  84. package/dist/tests/migrations-v068.test.d.ts +2 -0
  85. package/dist/tests/migrations-v068.test.d.ts.map +1 -0
  86. package/dist/tests/migrations-v068.test.js +53 -0
  87. package/dist/tests/migrations-v068.test.js.map +1 -0
  88. package/dist/tests/nomination-counts.test.d.ts +2 -0
  89. package/dist/tests/nomination-counts.test.d.ts.map +1 -0
  90. package/dist/tests/nomination-counts.test.js +51 -0
  91. package/dist/tests/nomination-counts.test.js.map +1 -0
  92. package/dist/tests/recall-telemetry.test.js +8 -8
  93. package/dist/tests/schema-version.test.js +3 -7
  94. package/dist/tests/schema-version.test.js.map +1 -1
  95. package/dist/tests/search-hook.test.js +2 -2
  96. package/dist/tests/sec-regression.test.js +0 -50
  97. package/dist/tests/sec-regression.test.js.map +1 -1
  98. package/dist/tests/session-end-cleanup.test.js +3 -20
  99. package/dist/tests/session-end-cleanup.test.js.map +1 -1
  100. package/dist/tests/session-focus.test.js +7 -30
  101. package/dist/tests/session-focus.test.js.map +1 -1
  102. package/dist/tests/session-payload.test.js +1 -1
  103. package/dist/tests/session-summary.test.js +1 -1
  104. package/hooks/lib/constants.cjs +15 -0
  105. package/hooks/lib/mindlore-common.cjs +974 -1042
  106. package/hooks/mindlore-cwd-changed.cjs +57 -57
  107. package/hooks/mindlore-decision-detector.cjs +54 -54
  108. package/hooks/mindlore-dont-repeat.cjs +222 -222
  109. package/hooks/mindlore-fts5-sync.cjs +97 -88
  110. package/hooks/mindlore-index.cjs +229 -229
  111. package/hooks/mindlore-model-router.cjs +54 -54
  112. package/hooks/mindlore-post-compact.cjs +69 -69
  113. package/hooks/mindlore-post-read.cjs +106 -106
  114. package/hooks/mindlore-pre-compact.cjs +154 -154
  115. package/hooks/mindlore-read-guard.cjs +105 -105
  116. package/hooks/mindlore-research-guard.cjs +176 -176
  117. package/hooks/mindlore-search.cjs +200 -200
  118. package/hooks/mindlore-session-end.cjs +509 -526
  119. package/hooks/mindlore-session-focus.cjs +256 -259
  120. package/package.json +75 -78
  121. package/plugin.json +1 -1
  122. package/skills/mindlore-diary/SKILL.md +85 -85
  123. package/skills/mindlore-evolve/SKILL.md +126 -126
  124. package/skills/mindlore-explore/SKILL.md +109 -109
  125. package/skills/mindlore-ingest/SKILL.md +195 -195
  126. package/skills/mindlore-maintain/SKILL.md +125 -125
  127. package/skills/mindlore-query/SKILL.md +151 -151
  128. package/skills/mindlore-reflect/SKILL.md +141 -141
  129. package/skills/mindlore-stats/SKILL.md +106 -106
  130. package/templates/INDEX.md +14 -14
  131. package/templates/SCHEMA.md +292 -292
  132. package/templates/config.json +1 -1
  133. package/templates/extraction/article.md +15 -15
  134. package/templates/extraction/changelog.md +15 -15
  135. package/templates/extraction/default.md +15 -15
  136. package/templates/extraction/docs.md +15 -15
  137. package/templates/extraction/github-repo.md +17 -17
  138. package/dist/scripts/lib/daemon.d.ts +0 -16
  139. package/dist/scripts/lib/daemon.d.ts.map +0 -1
  140. package/dist/scripts/lib/daemon.js +0 -133
  141. package/dist/scripts/lib/daemon.js.map +0 -1
  142. package/dist/scripts/lib/embedding.d.ts +0 -5
  143. package/dist/scripts/lib/embedding.d.ts.map +0 -1
  144. package/dist/scripts/lib/embedding.js +0 -44
  145. package/dist/scripts/lib/embedding.js.map +0 -1
  146. package/dist/scripts/mindlore-daemon.d.ts +0 -2
  147. package/dist/scripts/mindlore-daemon.d.ts.map +0 -1
  148. package/dist/scripts/mindlore-daemon.js +0 -117
  149. package/dist/scripts/mindlore-daemon.js.map +0 -1
  150. package/dist/tests/daemon-integration.test.d.ts +0 -2
  151. package/dist/tests/daemon-integration.test.d.ts.map +0 -1
  152. package/dist/tests/daemon-integration.test.js +0 -37
  153. package/dist/tests/daemon-integration.test.js.map +0 -1
  154. package/dist/tests/daemon.test.d.ts +0 -2
  155. package/dist/tests/daemon.test.d.ts.map +0 -1
  156. package/dist/tests/daemon.test.js +0 -187
  157. package/dist/tests/daemon.test.js.map +0 -1
  158. package/dist/tests/embedding-hf-integration.test.d.ts +0 -2
  159. package/dist/tests/embedding-hf-integration.test.d.ts.map +0 -1
  160. package/dist/tests/embedding-hf-integration.test.js +0 -52
  161. package/dist/tests/embedding-hf-integration.test.js.map +0 -1
  162. package/dist/tests/embedding.test.d.ts +0 -6
  163. package/dist/tests/embedding.test.d.ts.map +0 -1
  164. package/dist/tests/embedding.test.js +0 -71
  165. package/dist/tests/embedding.test.js.map +0 -1
  166. package/dist/tests/sqlite-vec-v12.test.d.ts +0 -2
  167. package/dist/tests/sqlite-vec-v12.test.d.ts.map +0 -1
  168. package/dist/tests/sqlite-vec-v12.test.js +0 -72
  169. package/dist/tests/sqlite-vec-v12.test.js.map +0 -1
@@ -1,54 +1,54 @@
1
- 'use strict';
2
-
3
- /**
4
- * mindlore-model-router — PreToolUse (Agent) hook
5
- * Overrides model for [mindlore:SKILL] marked Agent spawns.
6
- */
7
-
8
- const fs = require('fs');
9
- const { findMindloreDir, readConfig, DEFAULT_MODELS, hookLog, withTelemetrySync } = require('./lib/mindlore-common.cjs');
10
-
11
- const SKILL_KEYS = Object.keys(DEFAULT_MODELS).filter((k) => k !== 'default');
12
- const MARKER_REGEX = new RegExp(`\\[mindlore:(${SKILL_KEYS.join('|')})\\]`);
13
-
14
- function main() {
15
- const mindloreDir = findMindloreDir();
16
- if (!mindloreDir) return;
17
-
18
- let input;
19
- try {
20
- const raw = fs.readFileSync(0, 'utf8').trim();
21
- if (!raw) return;
22
- input = JSON.parse(raw);
23
- } catch (_err) {
24
- return;
25
- }
26
-
27
- const toolName = input.tool_name || '';
28
- if (toolName !== 'Agent') return;
29
-
30
- const toolInput = input.tool_input || {};
31
- const prompt = toolInput.prompt || '';
32
- const match = prompt.match(MARKER_REGEX);
33
- if (!match) return;
34
-
35
- const skill = match[1];
36
-
37
- // Resolve model: config.json → config default → hardcoded
38
- const config = readConfig(mindloreDir);
39
- const models = (config && config.models) || {};
40
- const model = models[skill] || models.default || DEFAULT_MODELS[skill] || DEFAULT_MODELS.default;
41
-
42
- const updatedInput = { ...toolInput, model: model };
43
-
44
- const output = {
45
- hookSpecificOutput: {
46
- hookEventName: 'PreToolUse',
47
- updatedInput: updatedInput,
48
- },
49
- };
50
-
51
- process.stdout.write(JSON.stringify(output));
52
- }
53
-
54
- try { withTelemetrySync('mindlore-model-router', main); } catch (err) { hookLog('model-router', 'error', err?.message ?? String(err)); }
1
+ 'use strict';
2
+
3
+ /**
4
+ * mindlore-model-router — PreToolUse (Agent) hook
5
+ * Overrides model for [mindlore:SKILL] marked Agent spawns.
6
+ */
7
+
8
+ const fs = require('fs');
9
+ const { findMindloreDir, readConfig, DEFAULT_MODELS, hookLog, withTelemetrySync } = require('./lib/mindlore-common.cjs');
10
+
11
+ const SKILL_KEYS = Object.keys(DEFAULT_MODELS).filter((k) => k !== 'default');
12
+ const MARKER_REGEX = new RegExp(`\\[mindlore:(${SKILL_KEYS.join('|')})\\]`);
13
+
14
+ function main() {
15
+ const mindloreDir = findMindloreDir();
16
+ if (!mindloreDir) return;
17
+
18
+ let input;
19
+ try {
20
+ const raw = fs.readFileSync(0, 'utf8').trim();
21
+ if (!raw) return;
22
+ input = JSON.parse(raw);
23
+ } catch (_err) {
24
+ return;
25
+ }
26
+
27
+ const toolName = input.tool_name || '';
28
+ if (toolName !== 'Agent') return;
29
+
30
+ const toolInput = input.tool_input || {};
31
+ const prompt = toolInput.prompt || '';
32
+ const match = prompt.match(MARKER_REGEX);
33
+ if (!match) return;
34
+
35
+ const skill = match[1];
36
+
37
+ // Resolve model: config.json → config default → hardcoded
38
+ const config = readConfig(mindloreDir);
39
+ const models = (config && config.models) || {};
40
+ const model = models[skill] || models.default || DEFAULT_MODELS[skill] || DEFAULT_MODELS.default;
41
+
42
+ const updatedInput = { ...toolInput, model: model };
43
+
44
+ const output = {
45
+ hookSpecificOutput: {
46
+ hookEventName: 'PreToolUse',
47
+ updatedInput: updatedInput,
48
+ },
49
+ };
50
+
51
+ process.stdout.write(JSON.stringify(output));
52
+ }
53
+
54
+ try { withTelemetrySync('mindlore-model-router', main); } catch (err) { hookLog('model-router', 'error', err?.message ?? String(err)); }
@@ -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)); });