mindlore 0.7.0 → 0.7.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 (56) hide show
  1. package/README.md +30 -3
  2. package/dist/scripts/bundle-hooks.d.ts +2 -0
  3. package/dist/scripts/bundle-hooks.d.ts.map +1 -0
  4. package/dist/scripts/bundle-hooks.js +68 -0
  5. package/dist/scripts/bundle-hooks.js.map +1 -0
  6. package/dist/scripts/init.js +0 -3
  7. package/dist/scripts/init.js.map +1 -1
  8. package/dist/scripts/lib/constants.d.ts +0 -2
  9. package/dist/scripts/lib/constants.d.ts.map +1 -1
  10. package/dist/scripts/lib/constants.js +0 -21
  11. package/dist/scripts/lib/constants.js.map +1 -1
  12. package/dist/tests/hook-smoke.test.js +1 -1
  13. package/dist/tests/hook-smoke.test.js.map +1 -1
  14. package/dist/tests/search-hook.test.js +1 -1
  15. package/dist/tests/search-hook.test.js.map +1 -1
  16. package/hooks/cc-memory-bulk-sync.cjs +592 -0
  17. package/hooks/cc-session-sync.cjs +842 -0
  18. package/hooks/hooks.json +149 -0
  19. package/hooks/lib/mindlore-common.cjs +2 -2
  20. package/hooks/lib/secure-io.cjs +17 -0
  21. package/hooks/mindlore-cwd-changed.cjs +19 -34
  22. package/hooks/mindlore-decision-detector.cjs +40 -31
  23. package/hooks/mindlore-dont-repeat.cjs +57 -115
  24. package/hooks/mindlore-fts5-sync.cjs +15 -44
  25. package/hooks/mindlore-index.cjs +100 -101
  26. package/hooks/mindlore-model-router.cjs +20 -32
  27. package/hooks/mindlore-post-compact.cjs +26 -42
  28. package/hooks/mindlore-post-read.cjs +35 -60
  29. package/hooks/mindlore-pre-compact.cjs +55 -73
  30. package/hooks/mindlore-read-guard.cjs +28 -51
  31. package/hooks/mindlore-research-guard.cjs +63 -101
  32. package/hooks/mindlore-search.cjs +1142 -93
  33. package/hooks/mindlore-session-end.cjs +155 -276
  34. package/hooks/mindlore-session-focus.cjs +639 -110
  35. package/hooks/src/lib/constants.cjs +15 -0
  36. package/hooks/src/lib/mindlore-common.cjs +975 -0
  37. package/hooks/src/lib/mindlore-common.d.cts +72 -0
  38. package/hooks/src/lib/secure-io.cjs +17 -0
  39. package/hooks/src/lib/types.d.ts +58 -0
  40. package/hooks/src/mindlore-cwd-changed.cjs +57 -0
  41. package/hooks/src/mindlore-decision-detector.cjs +54 -0
  42. package/hooks/src/mindlore-dont-repeat.cjs +222 -0
  43. package/hooks/src/mindlore-fts5-sync.cjs +98 -0
  44. package/hooks/src/mindlore-index.cjs +230 -0
  45. package/hooks/src/mindlore-model-router.cjs +54 -0
  46. package/hooks/src/mindlore-post-compact.cjs +69 -0
  47. package/hooks/src/mindlore-post-read.cjs +106 -0
  48. package/hooks/src/mindlore-pre-compact.cjs +154 -0
  49. package/hooks/src/mindlore-read-guard.cjs +105 -0
  50. package/hooks/src/mindlore-research-guard.cjs +176 -0
  51. package/hooks/src/mindlore-search.cjs +200 -0
  52. package/hooks/src/mindlore-session-end.cjs +511 -0
  53. package/hooks/src/mindlore-session-focus.cjs +256 -0
  54. package/package.json +7 -3
  55. package/plugin.json +3 -3
  56. package/templates/config.json +1 -1
@@ -1,49 +1,29 @@
1
1
  #!/usr/bin/env node
2
- 'use strict';
3
-
4
- /**
5
- * mindlore-fts5-sync — FileChanged hook (incremental re-index)
6
- *
7
- * Handles bulk file changes by checking all .mindlore/ .md files
8
- * against their content hashes and re-indexing only changed ones.
9
- *
10
- * Lightweight complement to mindlore-index.cjs which handles single files.
11
- * This hook catches cases where multiple files change at once (e.g., git pull).
12
- */
13
-
14
- const fs = require('fs');
15
- const path = require('path');
16
- const { DB_NAME, sha256, openDatabase, getAllMdFiles, parseFrontmatter, extractFtsMetadata, insertFtsRow, readHookStdin, getActiveMindloreDir, getProjectName, resolveProject, hookLog, withTelemetry, SQL_FTS_SESSIONS_INSERT, isSessionCategory, isInsideMindloreDir } = require('./lib/mindlore-common.cjs');
2
+ "use strict";
17
3
 
4
+ // hooks/src/mindlore-fts5-sync.cjs
5
+ var fs = require("fs");
6
+ var path = require("path");
7
+ var { DB_NAME, sha256, openDatabase, getAllMdFiles, parseFrontmatter, extractFtsMetadata, insertFtsRow, readHookStdin, getActiveMindloreDir, getProjectName, resolveProject, hookLog, withTelemetry, SQL_FTS_SESSIONS_INSERT, isSessionCategory, isInsideMindloreDir } = require("./lib/mindlore-common.cjs");
18
8
  function main() {
19
- const filePath = readHookStdin(['path', 'file_path']);
20
-
9
+ const filePath = readHookStdin(["path", "file_path"]);
21
10
  if (!filePath) return;
22
11
  const resolved = path.resolve(filePath);
23
12
  if (!isInsideMindloreDir(resolved)) return;
24
-
25
- // Skip if this is a single .md file change — mindlore-index.cjs handles those.
26
- // This hook is for bulk changes (git pull, manual batch edits).
27
- if (filePath.endsWith('.md')) return;
28
-
13
+ if (filePath.endsWith(".md")) return;
29
14
  const baseDir = getActiveMindloreDir();
30
15
  if (!fs.existsSync(baseDir)) return;
31
-
32
16
  const dbPath = path.join(baseDir, DB_NAME);
33
17
  if (!fs.existsSync(dbPath)) return;
34
-
35
18
  const db = openDatabase(dbPath);
36
19
  if (!db) return;
37
-
38
20
  const mdFiles = getAllMdFiles(baseDir);
39
-
40
- const allHashes = new Map();
41
- for (const row of db.prepare('SELECT path, content_hash FROM file_hashes').all()) {
21
+ const allHashes = /* @__PURE__ */ new Map();
22
+ for (const row of db.prepare("SELECT path, content_hash FROM file_hashes").all()) {
42
23
  allHashes.set(row.path, row.content_hash);
43
24
  }
44
-
45
- const deleteFts = db.prepare('DELETE FROM mindlore_fts WHERE path = ?');
46
- const deleteFtsSessions = db.prepare('DELETE FROM mindlore_fts_sessions WHERE path = ?');
25
+ const deleteFts = db.prepare("DELETE FROM mindlore_fts WHERE path = ?");
26
+ const deleteFtsSessions = db.prepare("DELETE FROM mindlore_fts_sessions WHERE path = ?");
47
27
  const insertFtsSessions = db.prepare(SQL_FTS_SESSIONS_INSERT);
48
28
  const upsertHash = db.prepare(`
49
29
  INSERT INTO file_hashes (path, content_hash, last_indexed)
@@ -52,27 +32,20 @@ function main() {
52
32
  content_hash = excluded.content_hash,
53
33
  last_indexed = excluded.last_indexed
54
34
  `);
55
-
56
- const now = new Date().toISOString();
57
-
35
+ const now = (/* @__PURE__ */ new Date()).toISOString();
58
36
  try {
59
37
  const project = getProjectName();
60
-
61
38
  const changedFiles = [];
62
39
  for (const file of mdFiles) {
63
- const content = fs.readFileSync(file, 'utf8').replace(/\r\n/g, '\n');
40
+ const content = fs.readFileSync(file, "utf8").replace(/\r\n/g, "\n");
64
41
  const hash = sha256(content);
65
-
66
42
  const existingHash = allHashes.get(file);
67
43
  if (existingHash === hash) continue;
68
-
69
44
  const { meta, body } = parseFrontmatter(content);
70
45
  const { slug, description, type, category, title, tags, quality, dateCaptured, project: ftsProject } = extractFtsMetadata(meta, body, file, baseDir);
71
46
  const resolvedProject = resolveProject(ftsProject, file, project);
72
47
  changedFiles.push({ file, hash, slug, description, type, category, title, tags, quality, dateCaptured, resolvedProject, body });
73
48
  }
74
-
75
- // No file I/O inside — minimize lock hold time
76
49
  const transaction = db.transaction(() => {
77
50
  for (const item of changedFiles) {
78
51
  deleteFts.run(item.file);
@@ -89,10 +62,8 @@ function main() {
89
62
  } finally {
90
63
  db.close();
91
64
  }
92
-
93
65
  }
94
-
95
- withTelemetry('mindlore-fts5-sync', main).catch(err => {
96
- hookLog('mindlore-fts5-sync', 'error', err?.message ?? String(err));
66
+ withTelemetry("mindlore-fts5-sync", main).catch((err) => {
67
+ hookLog("mindlore-fts5-sync", "error", err?.message ?? String(err));
97
68
  process.exit(0);
98
69
  });
@@ -1,92 +1,117 @@
1
1
  #!/usr/bin/env node
2
- 'use strict';
3
-
4
- /**
5
- * mindlore-index FileChanged hook
6
- *
7
- * When a .md file in .mindlore/ changes, update its FTS5 entry.
8
- * Reads changed file path from stdin (CC FileChanged event).
9
- */
10
-
11
- const fs = require('fs');
12
- const path = require('path');
13
- const { safeMkdir, safeWriteFile } = require('../dist/scripts/lib/secure-io.js');
14
- const { DB_NAME, SKIP_FILES, sha256, openDatabase, parseFrontmatter, extractFtsMetadata, insertFtsRow, readHookStdin, getProjectName, resolveProject, globalDir, hookLog, withTelemetry, isInsideMindloreDir, extractMindloreBaseDir } = require('./lib/mindlore-common.cjs');
2
+ "use strict";
3
+ var __getOwnPropNames = Object.getOwnPropertyNames;
4
+ var __commonJS = (cb, mod) => function __require() {
5
+ return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
6
+ };
7
+
8
+ // dist/scripts/lib/privacy-filter.js
9
+ var require_privacy_filter = __commonJS({
10
+ "dist/scripts/lib/privacy-filter.js"(exports2) {
11
+ "use strict";
12
+ Object.defineProperty(exports2, "__esModule", { value: true });
13
+ exports2.DEFAULT_PATTERNS = void 0;
14
+ exports2.redactSecrets = redactSecrets;
15
+ var REPLACEMENT = "[REDACTED]";
16
+ var PATTERN_PREFIXES = [
17
+ { prefix: "sk-", pattern: /sk-(?:proj-|ant-)?[A-Za-z0-9_-]{20,}/g },
18
+ { prefix: "AKIA", pattern: /AKIA[0-9A-Z]{16}/g },
19
+ { prefix: "ghp_", pattern: /ghp_[A-Za-z0-9]{36,}/g },
20
+ { prefix: "gho_", pattern: /gho_[A-Za-z0-9]{36,}/g },
21
+ { prefix: "github_pat_", pattern: /github_pat_[A-Za-z0-9_]{22,}/g },
22
+ { prefix: "npm_", pattern: /npm_[A-Za-z0-9]{36,}/g },
23
+ { prefix: "xox", pattern: /xox[bporas]-[A-Za-z0-9-]{10,}/g },
24
+ { prefix: "eyJ", pattern: /eyJ[a-zA-Z0-9_\-]{20,}\.[a-zA-Z0-9_\-]{20,}\.[a-zA-Z0-9_\-]{20,}/g },
25
+ { prefix: "AIza", pattern: /AIza[0-9A-Za-z_\-]{30,}/g },
26
+ { prefix: "sk_live_", pattern: /sk_live_[a-zA-Z0-9]{20,}/g },
27
+ { prefix: "pk_live_", pattern: /pk_live_[a-zA-Z0-9]{20,}/g },
28
+ { prefix: "Bearer", pattern: /Bearer\s+[a-zA-Z0-9\-._~+/]+=*/g },
29
+ { prefix: "-----BEGIN", pattern: /-----BEGIN\s(?:RSA\s|EC\s|DSA\s|OPENSSH\s)?PRIVATE\sKEY-----/g },
30
+ { prefix: "Basic", pattern: /Basic\s+[a-zA-Z0-9+\/]{16,}={0,2}/g },
31
+ { prefix: "-----BEGIN CERTIFICATE", pattern: /-----BEGIN\sCERTIFICATE-----/g }
32
+ ];
33
+ var NO_PREFIX_PATTERNS = [
34
+ /(?:postgres|mysql|mongodb|redis|amqp)(?:\+srv)?:\/\/[^\s"']+/g,
35
+ /(?:PASSWORD|SECRET|TOKEN|API_KEY|PRIVATE_KEY|DATABASE_URL|DB_PASSWORD|AUTH_TOKEN|ACCESS_KEY|SECRET_KEY)=\S+/gi,
36
+ /(?:api_key|auth_token|access_token|refresh_token|client_secret|private_key|secret_key)\s*[:=]\s*["']?[^\s"',}{]{8,}["']?/gi
37
+ ];
38
+ exports2.DEFAULT_PATTERNS = [
39
+ ...PATTERN_PREFIXES.map((p) => p.pattern),
40
+ ...NO_PREFIX_PATTERNS
41
+ ];
42
+ function redactSecrets(text, extraPatterns) {
43
+ let result = text;
44
+ for (const { prefix, pattern } of PATTERN_PREFIXES) {
45
+ if (result.includes(prefix)) {
46
+ pattern.lastIndex = 0;
47
+ result = result.replace(pattern, REPLACEMENT);
48
+ }
49
+ }
50
+ const remaining = extraPatterns ? [...NO_PREFIX_PATTERNS, ...extraPatterns] : NO_PREFIX_PATTERNS;
51
+ for (const pattern of remaining) {
52
+ pattern.lastIndex = 0;
53
+ result = result.replace(pattern, REPLACEMENT);
54
+ }
55
+ return result;
56
+ }
57
+ }
58
+ });
15
59
 
60
+ // hooks/src/mindlore-index.cjs
61
+ var fs = require("fs");
62
+ var path = require("path");
63
+ var { safeMkdir, safeWriteFile } = require("./lib/secure-io.cjs");
64
+ var { DB_NAME, SKIP_FILES, sha256, openDatabase, parseFrontmatter, extractFtsMetadata, insertFtsRow, readHookStdin, getProjectName, resolveProject, globalDir, hookLog, withTelemetry, isInsideMindloreDir, extractMindloreBaseDir } = require("./lib/mindlore-common.cjs");
16
65
  function invalidateSearchCache(db) {
17
- try { db.exec('DELETE FROM search_cache'); } catch (_) { /* table may not exist */ }
66
+ try {
67
+ db.exec("DELETE FROM search_cache");
68
+ } catch (_) {
69
+ }
18
70
  }
19
-
20
71
  function main() {
21
- const filePath = readHookStdin(['path', 'file_path']);
72
+ const filePath = readHookStdin(["path", "file_path"]);
22
73
  if (!filePath) return;
23
-
24
- // Only process .md files inside .mindlore/ (resolved path check prevents traversal)
25
- if (!filePath.endsWith('.md')) return;
74
+ if (!filePath.endsWith(".md")) return;
26
75
  const resolvedFile = path.resolve(filePath);
27
76
  if (!isInsideMindloreDir(resolvedFile)) {
28
- // CC memory path (~/.claude/projects/*/memory/*.md) index to global mindlore DB
29
- const isCcMemory = resolvedFile.includes(path.sep + '.claude' + path.sep + 'projects' + path.sep)
30
- && resolvedFile.includes(path.sep + 'memory' + path.sep)
31
- && resolvedFile.endsWith('.md');
77
+ const isCcMemory = resolvedFile.includes(path.sep + ".claude" + path.sep + "projects" + path.sep) && resolvedFile.includes(path.sep + "memory" + path.sep) && resolvedFile.endsWith(".md");
32
78
  if (!isCcMemory) return;
33
-
34
- // CC memory path — index to global mindlore DB
35
79
  indexCcMemory(resolvedFile);
36
80
  return;
37
81
  }
38
-
39
82
  const fileName = path.basename(filePath);
40
-
41
83
  const baseDir = extractMindloreBaseDir(resolvedFile);
42
84
  if (!baseDir) return;
43
85
  const dbPath = path.join(baseDir, DB_NAME);
44
-
45
86
  if (!fs.existsSync(dbPath)) return;
46
-
47
- // Catch-up scan: when INDEX.md or log.md triggers, index recently-modified files
48
- if (['INDEX.md', 'log.md'].includes(fileName)) {
87
+ if (["INDEX.md", "log.md"].includes(fileName)) {
49
88
  catchUpScan(baseDir, dbPath);
50
89
  return;
51
90
  }
52
-
53
91
  if (SKIP_FILES.has(fileName)) return;
54
-
55
92
  if (!fs.existsSync(filePath)) {
56
- // File was deleted — remove from index
57
- const db = openDatabase(dbPath);
58
- if (!db) return;
93
+ const db2 = openDatabase(dbPath);
94
+ if (!db2) return;
59
95
  try {
60
- db.prepare('DELETE FROM mindlore_fts WHERE path = ?').run(filePath);
61
- db.prepare('DELETE FROM file_hashes WHERE path = ?').run(filePath);
62
- invalidateSearchCache(db);
96
+ db2.prepare("DELETE FROM mindlore_fts WHERE path = ?").run(filePath);
97
+ db2.prepare("DELETE FROM file_hashes WHERE path = ?").run(filePath);
98
+ invalidateSearchCache(db2);
63
99
  } finally {
64
- db.close();
100
+ db2.close();
65
101
  }
66
102
  return;
67
103
  }
68
-
69
- const content = fs.readFileSync(filePath, 'utf8').replace(/\r\n/g, '\n');
104
+ const content = fs.readFileSync(filePath, "utf8").replace(/\r\n/g, "\n");
70
105
  const hash = sha256(content);
71
-
72
106
  const db = openDatabase(dbPath);
73
107
  if (!db) return;
74
-
75
108
  try {
76
- // Check if content changed
77
- const existing = db
78
- .prepare('SELECT content_hash FROM file_hashes WHERE path = ?')
79
- .get(filePath);
80
-
81
- if (existing && existing.content_hash === hash) return; // Unchanged
82
-
83
- // Parse frontmatter for rich FTS5 columns
109
+ const existing = db.prepare("SELECT content_hash FROM file_hashes WHERE path = ?").get(filePath);
110
+ if (existing && existing.content_hash === hash) return;
84
111
  const { meta, body } = parseFrontmatter(content);
85
112
  const { slug, description, type, category, title, tags, quality, dateCaptured, project: ftsProject } = extractFtsMetadata(meta, body, filePath, baseDir);
86
-
87
- // Update FTS5 + hash atomically
88
113
  const updateIndex = db.transaction(() => {
89
- db.prepare('DELETE FROM mindlore_fts WHERE path = ?').run(filePath);
114
+ db.prepare("DELETE FROM mindlore_fts WHERE path = ?").run(filePath);
90
115
  insertFtsRow(db, { path: filePath, slug, description, type, category, title, content: body, tags, quality, dateCaptured, project: resolveProject(ftsProject, filePath, getProjectName()) });
91
116
  db.prepare(
92
117
  `INSERT INTO file_hashes (path, content_hash, last_indexed)
@@ -94,7 +119,7 @@ function main() {
94
119
  ON CONFLICT(path) DO UPDATE SET
95
120
  content_hash = excluded.content_hash,
96
121
  last_indexed = excluded.last_indexed`
97
- ).run(filePath, hash, new Date().toISOString());
122
+ ).run(filePath, hash, (/* @__PURE__ */ new Date()).toISOString());
98
123
  });
99
124
  updateIndex();
100
125
  invalidateSearchCache(db);
@@ -102,54 +127,39 @@ function main() {
102
127
  db.close();
103
128
  }
104
129
  }
105
-
106
130
  function indexCcMemory(filePath) {
107
- const CC_MEMORY_CATEGORY = 'cc-memory';
108
- // CC memory constants live in TS (scripts/lib/constants.ts) — CJS hooks can't require TS directly
131
+ const CC_MEMORY_CATEGORY = "cc-memory";
109
132
  const globalBase = globalDir();
110
133
  const dbPath = path.join(globalBase, DB_NAME);
111
-
112
- const content = fs.readFileSync(filePath, 'utf8').replace(/\r\n/g, '\n');
134
+ const content = fs.readFileSync(filePath, "utf8").replace(/\r\n/g, "\n");
113
135
  if (!content.trim()) return;
114
-
115
- // Privacy filter — redact secrets before DB write
116
136
  let cleaned = content;
117
137
  try {
118
- const { redactSecrets } = require('../dist/scripts/lib/privacy-filter.js');
138
+ const { redactSecrets } = require_privacy_filter();
119
139
  cleaned = redactSecrets(content);
120
140
  } catch (_err) {
121
- // privacy-filter not built — use raw content
122
141
  }
123
-
124
- // SHA256 dedup
125
142
  const hash = sha256(cleaned);
126
143
  const db = openDatabase(dbPath);
127
144
  if (!db) return;
128
-
129
145
  try {
130
- const existing = db.prepare('SELECT content_hash FROM file_hashes WHERE path = ?').get(filePath);
146
+ const existing = db.prepare("SELECT content_hash FROM file_hashes WHERE path = ?").get(filePath);
131
147
  if (existing && existing.content_hash === hash) {
132
- return; // unchanged — finally handles db.close()
148
+ return;
133
149
  }
134
-
135
150
  const { meta, body } = parseFrontmatter(cleaned);
136
- const memType = String(meta.type || 'unknown');
137
-
138
- // Extract project scope from path: ~/.claude/projects/C--Users-X-proj/memory/
151
+ const memType = String(meta.type || "unknown");
139
152
  const projMatch = filePath.match(/projects[/\\]([^/\\]+)[/\\]memory/);
140
153
  const projectScope = projMatch ? projMatch[1] : null;
141
-
142
154
  const ftsData = extractFtsMetadata(meta, body, filePath, globalBase);
143
-
144
- // Update FTS5 + hash atomically
145
155
  const updateIndex = db.transaction(() => {
146
- db.prepare('DELETE FROM mindlore_fts WHERE path = ?').run(filePath);
156
+ db.prepare("DELETE FROM mindlore_fts WHERE path = ?").run(filePath);
147
157
  insertFtsRow(db, {
148
158
  path: filePath,
149
159
  ...ftsData,
150
160
  category: CC_MEMORY_CATEGORY,
151
161
  type: memType,
152
- project: projectScope,
162
+ project: projectScope
153
163
  });
154
164
  db.prepare(
155
165
  `INSERT INTO file_hashes (path, content_hash, last_indexed, source_type, project_scope)
@@ -159,12 +169,10 @@ function indexCcMemory(filePath) {
159
169
  last_indexed = excluded.last_indexed,
160
170
  source_type = excluded.source_type,
161
171
  project_scope = excluded.project_scope`
162
- ).run(filePath, hash, new Date().toISOString(), CC_MEMORY_CATEGORY, projectScope);
172
+ ).run(filePath, hash, (/* @__PURE__ */ new Date()).toISOString(), CC_MEMORY_CATEGORY, projectScope);
163
173
  });
164
174
  updateIndex();
165
-
166
- // Copy to ~/.mindlore/memory/{project}/ for git-sync + obsidian
167
- const memoryDir = path.join(globalBase, 'memory', projectScope || '_global');
175
+ const memoryDir = path.join(globalBase, "memory", projectScope || "_global");
168
176
  safeMkdir(memoryDir);
169
177
  const destPath = path.join(memoryDir, path.basename(filePath));
170
178
  safeWriteFile(destPath, cleaned);
@@ -172,37 +180,29 @@ function indexCcMemory(filePath) {
172
180
  db.close();
173
181
  }
174
182
  }
175
-
176
183
  function catchUpScan(baseDir, dbPath) {
177
- const CATCH_UP_DIRS = ['raw', 'sources', 'analyses', 'diary'];
178
- const fiveMinAgo = Date.now() - 5 * 60 * 1000;
179
-
184
+ const CATCH_UP_DIRS = ["raw", "sources", "analyses", "diary"];
185
+ const fiveMinAgo = Date.now() - 5 * 60 * 1e3;
180
186
  const db = openDatabase(dbPath);
181
187
  if (!db) return;
182
-
183
188
  try {
184
189
  let indexed = 0;
185
190
  for (const dir of CATCH_UP_DIRS) {
186
191
  const dirPath = path.join(baseDir, dir);
187
192
  if (!fs.existsSync(dirPath)) continue;
188
-
189
- const files = fs.readdirSync(dirPath).filter(f => f.endsWith('.md'));
193
+ const files = fs.readdirSync(dirPath).filter((f) => f.endsWith(".md"));
190
194
  for (const file of files) {
191
195
  const filePath = path.join(dirPath, file);
192
196
  const stat = fs.statSync(filePath);
193
197
  if (stat.mtimeMs < fiveMinAgo) continue;
194
-
195
- const content = fs.readFileSync(filePath, 'utf8').replace(/\r\n/g, '\n');
198
+ const content = fs.readFileSync(filePath, "utf8").replace(/\r\n/g, "\n");
196
199
  const hash = sha256(content);
197
-
198
- const existing = db.prepare('SELECT content_hash FROM file_hashes WHERE path = ?').get(filePath);
200
+ const existing = db.prepare("SELECT content_hash FROM file_hashes WHERE path = ?").get(filePath);
199
201
  if (existing && existing.content_hash === hash) continue;
200
-
201
202
  const { meta, body } = parseFrontmatter(content);
202
203
  const ftsData = extractFtsMetadata(meta, body, filePath, baseDir);
203
-
204
204
  const update = db.transaction(() => {
205
- db.prepare('DELETE FROM mindlore_fts WHERE path = ?').run(filePath);
205
+ db.prepare("DELETE FROM mindlore_fts WHERE path = ?").run(filePath);
206
206
  insertFtsRow(db, { path: filePath, ...ftsData, project: resolveProject(ftsData.project, filePath, getProjectName()) });
207
207
  db.prepare(
208
208
  `INSERT INTO file_hashes (path, content_hash, last_indexed)
@@ -210,7 +210,7 @@ function catchUpScan(baseDir, dbPath) {
210
210
  ON CONFLICT(path) DO UPDATE SET
211
211
  content_hash = excluded.content_hash,
212
212
  last_indexed = excluded.last_indexed`
213
- ).run(filePath, hash, new Date().toISOString());
213
+ ).run(filePath, hash, (/* @__PURE__ */ new Date()).toISOString());
214
214
  });
215
215
  update();
216
216
  indexed++;
@@ -223,8 +223,7 @@ function catchUpScan(baseDir, dbPath) {
223
223
  db.close();
224
224
  }
225
225
  }
226
-
227
- withTelemetry('mindlore-index', main).catch(err => {
228
- hookLog('mindlore-index', 'error', err?.message ?? String(err));
226
+ withTelemetry("mindlore-index", main).catch((err) => {
227
+ hookLog("mindlore-index", "error", err?.message ?? String(err));
229
228
  process.exit(0);
230
229
  });
@@ -1,54 +1,42 @@
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('|')})\\]`);
1
+ "use strict";
13
2
 
3
+ // hooks/src/mindlore-model-router.cjs
4
+ var fs = require("fs");
5
+ var { findMindloreDir, readConfig, DEFAULT_MODELS, hookLog, withTelemetrySync } = require("./lib/mindlore-common.cjs");
6
+ var SKILL_KEYS = Object.keys(DEFAULT_MODELS).filter((k) => k !== "default");
7
+ var MARKER_REGEX = new RegExp(`\\[mindlore:(${SKILL_KEYS.join("|")})\\]`);
14
8
  function main() {
15
9
  const mindloreDir = findMindloreDir();
16
10
  if (!mindloreDir) return;
17
-
18
11
  let input;
19
12
  try {
20
- const raw = fs.readFileSync(0, 'utf8').trim();
13
+ const raw = fs.readFileSync(0, "utf8").trim();
21
14
  if (!raw) return;
22
15
  input = JSON.parse(raw);
23
16
  } catch (_err) {
24
17
  return;
25
18
  }
26
-
27
- const toolName = input.tool_name || '';
28
- if (toolName !== 'Agent') return;
29
-
19
+ const toolName = input.tool_name || "";
20
+ if (toolName !== "Agent") return;
30
21
  const toolInput = input.tool_input || {};
31
- const prompt = toolInput.prompt || '';
22
+ const prompt = toolInput.prompt || "";
32
23
  const match = prompt.match(MARKER_REGEX);
33
24
  if (!match) return;
34
-
35
25
  const skill = match[1];
36
-
37
- // Resolve model: config.json → config default → hardcoded
38
26
  const config = readConfig(mindloreDir);
39
- const models = (config && config.models) || {};
27
+ const models = config && config.models || {};
40
28
  const model = models[skill] || models.default || DEFAULT_MODELS[skill] || DEFAULT_MODELS.default;
41
-
42
- const updatedInput = { ...toolInput, model: model };
43
-
29
+ const updatedInput = { ...toolInput, model };
44
30
  const output = {
45
31
  hookSpecificOutput: {
46
- hookEventName: 'PreToolUse',
47
- updatedInput: updatedInput,
48
- },
32
+ hookEventName: "PreToolUse",
33
+ updatedInput
34
+ }
49
35
  };
50
-
51
36
  process.stdout.write(JSON.stringify(output));
52
37
  }
53
-
54
- try { withTelemetrySync('mindlore-model-router', main); } catch (err) { hookLog('model-router', 'error', err?.message ?? String(err)); }
38
+ try {
39
+ withTelemetrySync("mindlore-model-router", main);
40
+ } catch (err) {
41
+ hookLog("model-router", "error", err?.message ?? String(err));
42
+ }
@@ -1,69 +1,53 @@
1
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');
2
+ "use strict";
18
3
 
4
+ // hooks/src/mindlore-post-compact.cjs
5
+ var fs = require("fs");
6
+ var path = require("path");
7
+ var { findMindloreDir, getLatestDelta, readConfig, hookLog, withTelemetry } = require("./lib/mindlore-common.cjs");
19
8
  function main() {
20
9
  const baseDir = findMindloreDir();
21
10
  if (!baseDir) return;
22
-
23
11
  const output = [];
24
-
25
- // Re-inject INDEX.md
26
- const indexPath = path.join(baseDir, 'INDEX.md');
12
+ const indexPath = path.join(baseDir, "INDEX.md");
27
13
  if (fs.existsSync(indexPath)) {
28
- const content = fs.readFileSync(indexPath, 'utf8').trim();
29
- output.push(`[Mindlore INDEX (post-compact)]\n${content}`);
14
+ const content = fs.readFileSync(indexPath, "utf8").trim();
15
+ output.push(`[Mindlore INDEX (post-compact)]
16
+ ${content}`);
30
17
  }
31
-
32
- const diaryDir = path.join(baseDir, 'diary');
33
-
18
+ const diaryDir = path.join(baseDir, "diary");
34
19
  const latestDelta = getLatestDelta(diaryDir);
35
20
  if (latestDelta) {
36
- const deltaContent = fs.readFileSync(latestDelta, 'utf8').trim();
21
+ const deltaContent = fs.readFileSync(latestDelta, "utf8").trim();
37
22
  const deltaName = path.basename(latestDelta);
38
- output.push(`[Mindlore Delta (post-compact): ${deltaName}]\n${deltaContent}`);
23
+ output.push(`[Mindlore Delta (post-compact): ${deltaName}]
24
+ ${deltaContent}`);
39
25
  }
40
-
41
26
  try {
42
- const snapshots = fs.readdirSync(diaryDir)
43
- .filter(f => f.startsWith('compaction-snapshot-'))
44
- .sort();
27
+ const snapshots = fs.readdirSync(diaryDir).filter((f) => f.startsWith("compaction-snapshot-")).sort();
45
28
  if (snapshots.length > 0) {
46
29
  const latestSnapshot = snapshots[snapshots.length - 1];
47
30
  const snapshotContent = fs.readFileSync(
48
- path.join(diaryDir, latestSnapshot), 'utf8'
31
+ path.join(diaryDir, latestSnapshot),
32
+ "utf8"
49
33
  ).trim();
50
- output.push(`[Mindlore Compaction Resume]\n${snapshotContent}`);
34
+ output.push(`[Mindlore Compaction Resume]
35
+ ${snapshotContent}`);
51
36
  }
52
- } catch (_err) { /* snapshot best-effort */ }
53
-
37
+ } catch (_err) {
38
+ }
54
39
  if (output.length > 0) {
55
40
  const config = readConfig(baseDir);
56
41
  const budgetConfig = config?.tokenBudget ?? {};
57
- const maxInjectChars = (budgetConfig.sessionInject || 2000) * 4;
58
- let joined = output.join('\n\n');
42
+ const maxInjectChars = (budgetConfig.sessionInject || 2e3) * 4;
43
+ let joined = output.join("\n\n");
59
44
  if (joined.length > maxInjectChars) {
60
- joined = joined.slice(0, maxInjectChars) + '\n[...truncated by token budget]';
45
+ joined = joined.slice(0, maxInjectChars) + "\n[...truncated by token budget]";
61
46
  }
62
- process.stdout.write(joined + '\n');
47
+ process.stdout.write(joined + "\n");
63
48
  }
64
49
  }
65
-
66
- withTelemetry('mindlore-post-compact', main).catch(err => {
67
- hookLog('mindlore-post-compact', 'error', err?.message ?? String(err));
50
+ withTelemetry("mindlore-post-compact", main).catch((err) => {
51
+ hookLog("mindlore-post-compact", "error", err?.message ?? String(err));
68
52
  process.exit(0);
69
53
  });