mindlore 0.5.0 → 0.5.2

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 (149) hide show
  1. package/README.md +15 -2
  2. package/dist/scripts/fetch-raw.d.ts +2 -0
  3. package/dist/scripts/fetch-raw.d.ts.map +1 -0
  4. package/dist/scripts/fetch-raw.js +124 -0
  5. package/dist/scripts/fetch-raw.js.map +1 -0
  6. package/dist/scripts/init.js +45 -3
  7. package/dist/scripts/init.js.map +1 -1
  8. package/dist/scripts/lib/constants.d.ts +13 -0
  9. package/dist/scripts/lib/constants.d.ts.map +1 -1
  10. package/dist/scripts/lib/constants.js +24 -4
  11. package/dist/scripts/lib/constants.js.map +1 -1
  12. package/dist/scripts/lib/db-helpers.d.ts +12 -2
  13. package/dist/scripts/lib/db-helpers.d.ts.map +1 -1
  14. package/dist/scripts/lib/db-helpers.js +36 -0
  15. package/dist/scripts/lib/db-helpers.js.map +1 -1
  16. package/dist/scripts/lib/hybrid-search.d.ts +0 -2
  17. package/dist/scripts/lib/hybrid-search.d.ts.map +1 -1
  18. package/dist/scripts/lib/hybrid-search.js +7 -1
  19. package/dist/scripts/lib/hybrid-search.js.map +1 -1
  20. package/dist/scripts/lib/migrations-v051.d.ts +3 -0
  21. package/dist/scripts/lib/migrations-v051.d.ts.map +1 -0
  22. package/dist/scripts/lib/migrations-v051.js +24 -0
  23. package/dist/scripts/lib/migrations-v051.js.map +1 -0
  24. package/dist/scripts/lib/migrations-v052.d.ts +3 -0
  25. package/dist/scripts/lib/migrations-v052.d.ts.map +1 -0
  26. package/dist/scripts/lib/migrations-v052.js +23 -0
  27. package/dist/scripts/lib/migrations-v052.js.map +1 -0
  28. package/dist/scripts/lib/migrations.d.ts +1 -0
  29. package/dist/scripts/lib/migrations.d.ts.map +1 -1
  30. package/dist/scripts/lib/migrations.js +3 -1
  31. package/dist/scripts/lib/migrations.js.map +1 -1
  32. package/dist/scripts/lib/privacy-filter.d.ts +3 -0
  33. package/dist/scripts/lib/privacy-filter.d.ts.map +1 -0
  34. package/dist/scripts/lib/privacy-filter.js +28 -0
  35. package/dist/scripts/lib/privacy-filter.js.map +1 -0
  36. package/dist/scripts/lib/similarity.d.ts +12 -0
  37. package/dist/scripts/lib/similarity.d.ts.map +1 -0
  38. package/dist/scripts/lib/similarity.js +64 -0
  39. package/dist/scripts/lib/similarity.js.map +1 -0
  40. package/dist/scripts/lib/skeleton.d.ts +2 -0
  41. package/dist/scripts/lib/skeleton.d.ts.map +1 -0
  42. package/dist/scripts/lib/skeleton.js +99 -0
  43. package/dist/scripts/lib/skeleton.js.map +1 -0
  44. package/dist/scripts/lib/skill-memory.d.ts +13 -0
  45. package/dist/scripts/lib/skill-memory.d.ts.map +1 -0
  46. package/dist/scripts/lib/skill-memory.js +94 -0
  47. package/dist/scripts/lib/skill-memory.js.map +1 -0
  48. package/dist/scripts/mindlore-fts5-index.js +18 -3
  49. package/dist/scripts/mindlore-fts5-index.js.map +1 -1
  50. package/dist/scripts/mindlore-health-check.d.ts +1 -1
  51. package/dist/scripts/mindlore-health-check.d.ts.map +1 -1
  52. package/dist/scripts/mindlore-health-check.js +117 -96
  53. package/dist/scripts/mindlore-health-check.js.map +1 -1
  54. package/dist/scripts/quality-populate.js +8 -4
  55. package/dist/scripts/quality-populate.js.map +1 -1
  56. package/dist/tests/catch-up.test.d.ts +2 -0
  57. package/dist/tests/catch-up.test.d.ts.map +1 -0
  58. package/dist/tests/catch-up.test.js +88 -0
  59. package/dist/tests/catch-up.test.js.map +1 -0
  60. package/dist/tests/cc-memory-sync.test.d.ts +2 -0
  61. package/dist/tests/cc-memory-sync.test.d.ts.map +1 -0
  62. package/dist/tests/cc-memory-sync.test.js +121 -0
  63. package/dist/tests/cc-memory-sync.test.js.map +1 -0
  64. package/dist/tests/episode-file.test.d.ts +2 -0
  65. package/dist/tests/episode-file.test.d.ts.map +1 -0
  66. package/dist/tests/episode-file.test.js +82 -0
  67. package/dist/tests/episode-file.test.js.map +1 -0
  68. package/dist/tests/fetch-raw.test.d.ts +2 -0
  69. package/dist/tests/fetch-raw.test.d.ts.map +1 -0
  70. package/dist/tests/fetch-raw.test.js +43 -0
  71. package/dist/tests/fetch-raw.test.js.map +1 -0
  72. package/dist/tests/helpers/db.d.ts +1 -0
  73. package/dist/tests/helpers/db.d.ts.map +1 -1
  74. package/dist/tests/helpers/db.js +10 -0
  75. package/dist/tests/helpers/db.js.map +1 -1
  76. package/dist/tests/hook-logging.test.d.ts +2 -0
  77. package/dist/tests/hook-logging.test.d.ts.map +1 -0
  78. package/dist/tests/hook-logging.test.js +108 -0
  79. package/dist/tests/hook-logging.test.js.map +1 -0
  80. package/dist/tests/index-cli-embed.test.d.ts +7 -0
  81. package/dist/tests/index-cli-embed.test.d.ts.map +1 -0
  82. package/dist/tests/index-cli-embed.test.js +128 -0
  83. package/dist/tests/index-cli-embed.test.js.map +1 -0
  84. package/dist/tests/privacy-filter.test.d.ts +2 -0
  85. package/dist/tests/privacy-filter.test.d.ts.map +1 -0
  86. package/dist/tests/privacy-filter.test.js +56 -0
  87. package/dist/tests/privacy-filter.test.js.map +1 -0
  88. package/dist/tests/resolve-hook-common.test.d.ts +2 -0
  89. package/dist/tests/resolve-hook-common.test.d.ts.map +1 -0
  90. package/dist/tests/resolve-hook-common.test.js +30 -0
  91. package/dist/tests/resolve-hook-common.test.js.map +1 -0
  92. package/dist/tests/schema-version.test.js +28 -0
  93. package/dist/tests/schema-version.test.js.map +1 -1
  94. package/dist/tests/search-cli-hybrid.test.d.ts +6 -0
  95. package/dist/tests/search-cli-hybrid.test.d.ts.map +1 -0
  96. package/dist/tests/search-cli-hybrid.test.js +103 -0
  97. package/dist/tests/search-cli-hybrid.test.js.map +1 -0
  98. package/dist/tests/search-hook.test.js +33 -0
  99. package/dist/tests/search-hook.test.js.map +1 -1
  100. package/dist/tests/search-offload.test.d.ts +2 -0
  101. package/dist/tests/search-offload.test.d.ts.map +1 -0
  102. package/dist/tests/search-offload.test.js +34 -0
  103. package/dist/tests/search-offload.test.js.map +1 -0
  104. package/dist/tests/similarity.test.d.ts +2 -0
  105. package/dist/tests/similarity.test.d.ts.map +1 -0
  106. package/dist/tests/similarity.test.js +61 -0
  107. package/dist/tests/similarity.test.js.map +1 -0
  108. package/dist/tests/skeleton.test.d.ts +2 -0
  109. package/dist/tests/skeleton.test.d.ts.map +1 -0
  110. package/dist/tests/skeleton.test.js +116 -0
  111. package/dist/tests/skeleton.test.js.map +1 -0
  112. package/dist/tests/skill-memory-api.test.d.ts +2 -0
  113. package/dist/tests/skill-memory-api.test.d.ts.map +1 -0
  114. package/dist/tests/skill-memory-api.test.js +62 -0
  115. package/dist/tests/skill-memory-api.test.js.map +1 -0
  116. package/dist/tests/skill-memory.test.d.ts +2 -0
  117. package/dist/tests/skill-memory.test.d.ts.map +1 -0
  118. package/dist/tests/skill-memory.test.js +67 -0
  119. package/dist/tests/skill-memory.test.js.map +1 -0
  120. package/dist/tests/token-budget.test.d.ts +2 -0
  121. package/dist/tests/token-budget.test.d.ts.map +1 -0
  122. package/dist/tests/token-budget.test.js +32 -0
  123. package/dist/tests/token-budget.test.js.map +1 -0
  124. package/dist/tests/wiki-lint.test.d.ts +2 -0
  125. package/dist/tests/wiki-lint.test.d.ts.map +1 -0
  126. package/dist/tests/wiki-lint.test.js +47 -0
  127. package/dist/tests/wiki-lint.test.js.map +1 -0
  128. package/hooks/lib/mindlore-common.cjs +109 -2
  129. package/hooks/mindlore-cwd-changed.cjs +2 -2
  130. package/hooks/mindlore-decision-detector.cjs +2 -2
  131. package/hooks/mindlore-dont-repeat.cjs +2 -2
  132. package/hooks/mindlore-fts5-sync.cjs +2 -2
  133. package/hooks/mindlore-index.cjs +141 -3
  134. package/hooks/mindlore-model-router.cjs +2 -2
  135. package/hooks/mindlore-post-compact.cjs +2 -2
  136. package/hooks/mindlore-post-read.cjs +2 -2
  137. package/hooks/mindlore-pre-compact.cjs +2 -2
  138. package/hooks/mindlore-read-guard.cjs +15 -3
  139. package/hooks/mindlore-research-guard.cjs +2 -2
  140. package/hooks/mindlore-search.cjs +69 -41
  141. package/hooks/mindlore-session-end.cjs +129 -39
  142. package/hooks/mindlore-session-focus.cjs +24 -3
  143. package/package.json +6 -5
  144. package/plugin.json +29 -2
  145. package/skills/mindlore-diary/SKILL.md +76 -0
  146. package/skills/mindlore-ingest/SKILL.md +48 -50
  147. package/skills/mindlore-log/SKILL.md +3 -143
  148. package/skills/mindlore-reflect/SKILL.md +104 -0
  149. package/templates/config.json +6 -1
@@ -0,0 +1 @@
1
+ {"version":3,"file":"skill-memory-api.test.js","sourceRoot":"","sources":["../../tests/skill-memory-api.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=skill-memory.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"skill-memory.test.d.ts","sourceRoot":"","sources":["../../tests/skill-memory.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,67 @@
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 better_sqlite3_1 = __importDefault(require("better-sqlite3"));
7
+ const fs_1 = __importDefault(require("fs"));
8
+ const path_1 = __importDefault(require("path"));
9
+ const os_1 = __importDefault(require("os"));
10
+ const schema_version_1 = require("../scripts/lib/schema-version");
11
+ const migrations_v052_1 = require("../scripts/lib/migrations-v052");
12
+ describe('skill_memory table', () => {
13
+ let db;
14
+ let tmpDir;
15
+ beforeEach(() => {
16
+ tmpDir = fs_1.default.mkdtempSync(path_1.default.join(os_1.default.tmpdir(), 'mindlore-skillmem-'));
17
+ const dbPath = path_1.default.join(tmpDir, 'test.db');
18
+ db = new better_sqlite3_1.default(dbPath);
19
+ db.pragma('journal_mode = WAL');
20
+ (0, schema_version_1.ensureSchemaTable)(db);
21
+ (0, schema_version_1.runMigrations)(db, migrations_v052_1.V052_MIGRATIONS);
22
+ });
23
+ afterEach(() => {
24
+ db.close();
25
+ fs_1.default.rmSync(tmpDir, { recursive: true, force: true });
26
+ });
27
+ it('creates skill_memory table', () => {
28
+ const tables = db.prepare("SELECT name FROM sqlite_master WHERE type='table' AND name='skill_memory'").all();
29
+ expect(tables).toHaveLength(1);
30
+ });
31
+ it('inserts and retrieves skill memory', () => {
32
+ db.prepare(`INSERT INTO skill_memory (skill_name, key, value, updated_at)
33
+ VALUES (?, ?, ?, ?)`).run('mindlore-ingest', 'last_ingest_urls', '["https://example.com"]', new Date().toISOString());
34
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion -- better-sqlite3 .get() returns unknown
35
+ const row = db.prepare('SELECT * FROM skill_memory WHERE skill_name = ? AND key = ?').get('mindlore-ingest', 'last_ingest_urls');
36
+ expect(row).toBeDefined();
37
+ expect(row.value).toBe('["https://example.com"]');
38
+ expect(row.access_count).toBe(0);
39
+ });
40
+ it('enforces UNIQUE(skill_name, key) constraint', () => {
41
+ const insert = db.prepare(`INSERT INTO skill_memory (skill_name, key, value, updated_at)
42
+ VALUES (?, ?, ?, ?)`);
43
+ insert.run('mindlore-query', 'log', '[]', new Date().toISOString());
44
+ expect(() => {
45
+ insert.run('mindlore-query', 'log', '["x"]', new Date().toISOString());
46
+ }).toThrow();
47
+ });
48
+ it('upserts with ON CONFLICT', () => {
49
+ const upsert = db.prepare(`INSERT INTO skill_memory (skill_name, key, value, updated_at)
50
+ VALUES (?, ?, ?, ?)
51
+ ON CONFLICT(skill_name, key) DO UPDATE SET
52
+ value = excluded.value,
53
+ updated_at = excluded.updated_at,
54
+ access_count = access_count + 1`);
55
+ const now = new Date().toISOString();
56
+ upsert.run('mindlore-diary', 'last_date', '"2026-04-18"', now);
57
+ upsert.run('mindlore-diary', 'last_date', '"2026-04-19"', now);
58
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion -- better-sqlite3 .get() returns unknown
59
+ const row = db.prepare('SELECT * FROM skill_memory WHERE skill_name = ? AND key = ?').get('mindlore-diary', 'last_date');
60
+ expect(row.value).toBe('"2026-04-19"');
61
+ expect(row.access_count).toBe(1);
62
+ });
63
+ it('migration is idempotent', () => {
64
+ expect(() => (0, schema_version_1.runMigrations)(db, migrations_v052_1.V052_MIGRATIONS)).not.toThrow();
65
+ });
66
+ });
67
+ //# sourceMappingURL=skill-memory.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"skill-memory.test.js","sourceRoot":"","sources":["../../tests/skill-memory.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=token-budget.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"token-budget.test.d.ts","sourceRoot":"","sources":["../../tests/token-budget.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,32 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const constants_js_1 = require("../scripts/lib/constants.js");
4
+ describe('Token Budget', () => {
5
+ test('should have default budget values', () => {
6
+ expect(constants_js_1.DEFAULT_TOKEN_BUDGET.sessionInject).toBe(2000);
7
+ expect(constants_js_1.DEFAULT_TOKEN_BUDGET.searchResults).toBe(1500);
8
+ expect(constants_js_1.DEFAULT_TOKEN_BUDGET.perResult).toBe(500);
9
+ });
10
+ test('should truncate text to approximate token limit', () => {
11
+ // ~4 chars per token heuristic
12
+ const longText = 'a'.repeat(3000);
13
+ const maxChars = 500 * 4; // perResult=500 tokens ≈ 2000 chars
14
+ const truncated = longText.slice(0, maxChars);
15
+ expect(truncated.length).toBeLessThanOrEqual(maxChars);
16
+ });
17
+ });
18
+ describe('Search Hook — FTS5 Fallback OR Optimization', () => {
19
+ test('should build OR-joined FTS5 MATCH query from keywords', () => {
20
+ const keywords = ['react', 'hooks', 'useEffect'];
21
+ const sanitized = keywords.map(kw => kw.replace(/["*(){}[\]^~:]/g, ''));
22
+ const ftsQuery = sanitized.filter(Boolean).map(kw => `"${kw}"`).join(' OR ');
23
+ expect(ftsQuery).toBe('"react" OR "hooks" OR "useEffect"');
24
+ });
25
+ test('should handle keywords with special characters', () => {
26
+ const keywords = ['react*', '"hooks"', 'use:Effect'];
27
+ const sanitized = keywords.map(kw => kw.replace(/["*(){}[\]^~:]/g, ''));
28
+ const ftsQuery = sanitized.filter(Boolean).map(kw => `"${kw}"`).join(' OR ');
29
+ expect(ftsQuery).toBe('"react" OR "hooks" OR "useEffect"');
30
+ });
31
+ });
32
+ //# sourceMappingURL=token-budget.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"token-budget.test.js","sourceRoot":"","sources":["../../tests/token-budget.test.ts"],"names":[],"mappings":";;AAAA,8DAAmE;AAEnE,QAAQ,CAAC,cAAc,EAAE,GAAG,EAAE;IAC5B,IAAI,CAAC,mCAAmC,EAAE,GAAG,EAAE;QAC7C,MAAM,CAAC,mCAAoB,CAAC,aAAa,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACtD,MAAM,CAAC,mCAAoB,CAAC,aAAa,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACtD,MAAM,CAAC,mCAAoB,CAAC,SAAS,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IACnD,CAAC,CAAC,CAAC;IAEH,IAAI,CAAC,iDAAiD,EAAE,GAAG,EAAE;QAC3D,+BAA+B;QAC/B,MAAM,QAAQ,GAAG,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;QAClC,MAAM,QAAQ,GAAG,GAAG,GAAG,CAAC,CAAC,CAAC,oCAAoC;QAC9D,MAAM,SAAS,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC,EAAE,QAAQ,CAAC,CAAC;QAC9C,MAAM,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC,mBAAmB,CAAC,QAAQ,CAAC,CAAC;IACzD,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAEH,QAAQ,CAAC,6CAA6C,EAAE,GAAG,EAAE;IAC3D,IAAI,CAAC,uDAAuD,EAAE,GAAG,EAAE;QACjE,MAAM,QAAQ,GAAG,CAAC,OAAO,EAAE,OAAO,EAAE,WAAW,CAAC,CAAC;QACjD,MAAM,SAAS,GAAG,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,OAAO,CAAC,iBAAiB,EAAE,EAAE,CAAC,CAAC,CAAC;QACxE,MAAM,QAAQ,GAAG,SAAS,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QAC7E,MAAM,CAAC,QAAQ,CAAC,CAAC,IAAI,CAAC,mCAAmC,CAAC,CAAC;IAC7D,CAAC,CAAC,CAAC;IAEH,IAAI,CAAC,gDAAgD,EAAE,GAAG,EAAE;QAC1D,MAAM,QAAQ,GAAG,CAAC,QAAQ,EAAE,SAAS,EAAE,YAAY,CAAC,CAAC;QACrD,MAAM,SAAS,GAAG,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,OAAO,CAAC,iBAAiB,EAAE,EAAE,CAAC,CAAC,CAAC;QACxE,MAAM,QAAQ,GAAG,SAAS,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QAC7E,MAAM,CAAC,QAAQ,CAAC,CAAC,IAAI,CAAC,mCAAmC,CAAC,CAAC;IAC7D,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=wiki-lint.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"wiki-lint.test.d.ts","sourceRoot":"","sources":["../../tests/wiki-lint.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,47 @@
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 os_1 = __importDefault(require("os"));
9
+ const mindlore_health_check_js_1 = require("../scripts/mindlore-health-check.js");
10
+ describe('wiki lint (contradiction detection)', () => {
11
+ let tmpDir;
12
+ let sourcesDir;
13
+ beforeEach(() => {
14
+ tmpDir = fs_1.default.mkdtempSync(path_1.default.join(os_1.default.tmpdir(), 'mindlore-wikilint-'));
15
+ sourcesDir = path_1.default.join(tmpDir, 'sources');
16
+ fs_1.default.mkdirSync(sourcesDir, { recursive: true });
17
+ });
18
+ afterEach(() => {
19
+ fs_1.default.rmSync(tmpDir, { recursive: true, force: true });
20
+ });
21
+ it('detects conflicting numeric claims with same tag', () => {
22
+ fs_1.default.writeFileSync(path_1.default.join(sourcesDir, 'a.md'), '---\nslug: a\ntype: source\ntags: [fts5]\n---\nFTS5 has 10 columns.', 'utf8');
23
+ fs_1.default.writeFileSync(path_1.default.join(sourcesDir, 'b.md'), '---\nslug: b\ntype: source\ntags: [fts5]\n---\nFTS5 uses 9 columns.', 'utf8');
24
+ const warnings = (0, mindlore_health_check_js_1.detectWikiContradictions)(tmpDir);
25
+ expect(warnings.length).toBeGreaterThan(0);
26
+ expect(warnings[0]).toMatch(/conflicting values/);
27
+ expect(warnings[0]).toMatch(/10.*9|9.*10/);
28
+ });
29
+ it('no contradiction when values match', () => {
30
+ fs_1.default.writeFileSync(path_1.default.join(sourcesDir, 'a.md'), '---\nslug: a\ntype: source\ntags: [fts5]\n---\nFTS5 has 11 columns.', 'utf8');
31
+ fs_1.default.writeFileSync(path_1.default.join(sourcesDir, 'b.md'), '---\nslug: b\ntype: source\ntags: [fts5]\n---\nFTS5 uses 11 columns.', 'utf8');
32
+ const warnings = (0, mindlore_health_check_js_1.detectWikiContradictions)(tmpDir);
33
+ expect(warnings.length).toBe(0);
34
+ });
35
+ it('ignores files with no tags', () => {
36
+ fs_1.default.writeFileSync(path_1.default.join(sourcesDir, 'a.md'), '---\nslug: a\ntype: source\n---\nFTS5 has 10 columns.', 'utf8');
37
+ fs_1.default.writeFileSync(path_1.default.join(sourcesDir, 'b.md'), '---\nslug: b\ntype: source\n---\nFTS5 uses 9 columns.', 'utf8');
38
+ const warnings = (0, mindlore_health_check_js_1.detectWikiContradictions)(tmpDir);
39
+ expect(warnings.length).toBe(0);
40
+ });
41
+ it('no false positive when only one file has a tag', () => {
42
+ fs_1.default.writeFileSync(path_1.default.join(sourcesDir, 'a.md'), '---\nslug: a\ntype: source\ntags: [fts5]\n---\nFTS5 has 10 columns.', 'utf8');
43
+ const warnings = (0, mindlore_health_check_js_1.detectWikiContradictions)(tmpDir);
44
+ expect(warnings.length).toBe(0);
45
+ });
46
+ });
47
+ //# sourceMappingURL=wiki-lint.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"wiki-lint.test.js","sourceRoot":"","sources":["../../tests/wiki-lint.test.ts"],"names":[],"mappings":"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"}
@@ -310,7 +310,7 @@ CREATE TABLE IF NOT EXISTS episodes (
310
310
  /**
311
311
  * Valid episode kinds. CO-EVOLUTION: mirrors EPISODE_KINDS in scripts/lib/episodes.ts
312
312
  */
313
- // ~500 tokens context budget for multi-session inject
313
+ // ~625 tokens context budget for multi-session inject (~4 chars/token)
314
314
  const MULTI_SESSION_TOKEN_CAP_CHARS = 2500;
315
315
 
316
316
  const EPISODE_KINDS_CJS = ['session', 'decision', 'event', 'preference', 'learning', 'friction', 'discovery', 'nomination'];
@@ -554,7 +554,7 @@ const STOP_WORDS = new Set([
554
554
  'evet', 'hayir', 'tamam', 'ok', 'oldu', 'olur', 'dur',
555
555
  'simdi', 'sonra', 'once', 'hemen', 'biraz',
556
556
  'lan', 'ya', 'ki', 'abi', 'hadi', 'hey', 'selam',
557
- 'olarak', 'olan', 'gibi', 'kadar', 'daha', 'cok', 'hem',
557
+ 'olarak', 'olan', 'gibi', 'kadar', 'daha', 'cok',
558
558
  'bunu', 'buna', 'icinde', 'uzerinde', 'arasinda',
559
559
  'sonucu', 'tarafindan', 'zaten', 'gayet',
560
560
  'acaba', 'nedir', 'midir', 'mudur',
@@ -590,6 +590,37 @@ function sanitizeKeyword(kw) {
590
590
  return clean.length >= 2 ? `"${clean}"` : null;
591
591
  }
592
592
 
593
+ // Derive from compiled constants — single source of truth
594
+ const SHARED_EXPORT_DIRS = (() => {
595
+ try {
596
+ const { DIRECTORIES } = require('../../dist/scripts/lib/constants.js');
597
+ return [...DIRECTORIES, 'memory'];
598
+ } catch {
599
+ return ['raw', 'sources', 'domains', 'analyses', 'insights', 'connections', 'learnings', 'diary', 'decisions', 'memory'];
600
+ }
601
+ })();
602
+
603
+ function resolveWin32Bin(name) {
604
+ if (process.platform === 'win32') {
605
+ try {
606
+ return require('child_process')
607
+ .execSync(`where ${name}`, { encoding: 'utf8', timeout: 3000 })
608
+ .trim().split('\n')[0].trim();
609
+ } catch (_e) { /* fall through */ }
610
+ }
611
+ return name;
612
+ }
613
+
614
+ // Import from compiled TS — single source of truth
615
+ const extractSkeleton = (() => {
616
+ try {
617
+ return require('../../dist/scripts/lib/skeleton.js').extractSkeleton;
618
+ } catch {
619
+ // Fallback: identity function if dist not built
620
+ return (content) => content;
621
+ }
622
+ })();
623
+
593
624
  module.exports = {
594
625
  MINDLORE_DIR,
595
626
  GLOBAL_MINDLORE_DIR,
@@ -636,6 +667,14 @@ module.exports = {
636
667
  // Hybrid search helpers (v0.5.0)
637
668
  loadSqliteVecCjs,
638
669
  hasVecTableCjs,
670
+ // Hook logging (v0.5.1)
671
+ hookLog,
672
+ getRecentHookErrors,
673
+ // Shared helpers (v0.5.1)
674
+ SHARED_EXPORT_DIRS,
675
+ resolveWin32Bin,
676
+ // Skeleton compression (v0.5.2)
677
+ extractSkeleton,
639
678
  };
640
679
 
641
680
  /**
@@ -666,3 +705,71 @@ function hasVecTableCjs(db) {
666
705
  return false;
667
706
  }
668
707
  }
708
+
709
+ // --- Hook Logging (v0.5.1) ---
710
+
711
+ function hookLogPath() { return path.join(globalDir(), 'diary', '_hook-log.jsonl'); }
712
+
713
+ /**
714
+ * Append a structured log entry for any mindlore hook.
715
+ * JSONL format — one JSON object per line.
716
+ * Levels: 'info' | 'warn' | 'error'
717
+ * @param {string} hook - Hook name (e.g. 'session-end', 'search', 'read-guard')
718
+ * @param {'info'|'warn'|'error'} level
719
+ * @param {string} message
720
+ */
721
+ const HOOK_LOG_MAX_BYTES = 512 * 1024; // 500KB
722
+ const HOOK_LOG_KEEP_LINES = 500;
723
+
724
+ function hookLog(hook, level, message) {
725
+ try {
726
+ const logFile = hookLogPath();
727
+ const entry = JSON.stringify({
728
+ ts: new Date().toISOString(),
729
+ hook,
730
+ level,
731
+ msg: message,
732
+ pid: process.pid,
733
+ });
734
+ // Rotate if file exceeds threshold (at most once per hook — each runs as separate process)
735
+ try {
736
+ const stat = fs.statSync(logFile);
737
+ if (stat.size > HOOK_LOG_MAX_BYTES) {
738
+ const lines = fs.readFileSync(logFile, 'utf8').trim().split('\n');
739
+ fs.writeFileSync(logFile, lines.slice(-HOOK_LOG_KEEP_LINES).join('\n') + '\n');
740
+ }
741
+ } catch (_rotateErr) { /* file may not exist yet */ }
742
+ fs.appendFileSync(logFile, entry + '\n');
743
+ } catch (_err) {
744
+ // Best effort — never crash a hook for logging
745
+ }
746
+ }
747
+
748
+ /**
749
+ * Read recent hook errors/warnings since a given ISO date.
750
+ * Returns array of { ts, hook, level, msg } for level 'error' or 'warn'.
751
+ * Used by SessionStart to inject warnings into CC context.
752
+ * @param {string} [since] - ISO date string, defaults to 24h ago
753
+ * @param {number} [limit=10]
754
+ * @returns {Array<{ts: string, hook: string, level: string, msg: string}>}
755
+ */
756
+ function getRecentHookErrors(since, limit) {
757
+ const maxEntries = limit ?? 10;
758
+ const cutoff = since ?? new Date(Date.now() - 24 * 60 * 60 * 1000).toISOString();
759
+ const results = [];
760
+ try {
761
+ if (!fs.existsSync(hookLogPath())) return results;
762
+ const lines = fs.readFileSync(hookLogPath(), 'utf8').trim().split('\n');
763
+ for (let i = lines.length - 1; i >= 0 && results.length < maxEntries; i--) {
764
+ if (!lines[i]) continue;
765
+ try {
766
+ const entry = JSON.parse(lines[i]);
767
+ if (entry.ts < cutoff) break; // JSONL is chronological, stop early
768
+ if (entry.level === 'error' || entry.level === 'warn') {
769
+ results.push(entry);
770
+ }
771
+ } catch (_parseErr) { /* skip malformed lines */ }
772
+ }
773
+ } catch (_err) { /* silent */ }
774
+ return results.reverse(); // chronological order
775
+ }
@@ -15,7 +15,7 @@
15
15
 
16
16
  const fs = require('fs');
17
17
  const path = require('path');
18
- const { findMindloreDir, globalDir } = require('./lib/mindlore-common.cjs');
18
+ const { findMindloreDir, globalDir, hookLog } = require('./lib/mindlore-common.cjs');
19
19
 
20
20
  function main() {
21
21
  const cwd = process.cwd();
@@ -54,4 +54,4 @@ function main() {
54
54
  }
55
55
  }
56
56
 
57
- main();
57
+ try { main(); } catch (err) { hookLog('cwd-changed', 'error', err?.message ?? String(err)); }
@@ -9,7 +9,7 @@
9
9
  * Does NOT block (exit 0) — advisory only.
10
10
  */
11
11
 
12
- const { findMindloreDir, readHookStdin } = require('./lib/mindlore-common.cjs');
12
+ const { findMindloreDir, readHookStdin, hookLog } = require('./lib/mindlore-common.cjs');
13
13
 
14
14
  const SIGNALS_TR = [
15
15
  'karar verdik', 'karar verildi', 'kararlastirdik', 'kararlaştırdık',
@@ -48,4 +48,4 @@ function main() {
48
48
  }
49
49
  }
50
50
 
51
- main();
51
+ try { main(); } catch (err) { hookLog('decision-detector', 'error', err?.message ?? String(err)); }
@@ -18,7 +18,7 @@
18
18
  const fs = require('fs');
19
19
  const path = require('path');
20
20
  const os = require('os');
21
- const { findMindloreDir, getProjectName } = require('./lib/mindlore-common.cjs');
21
+ const { findMindloreDir, getProjectName, hookLog } = require('./lib/mindlore-common.cjs');
22
22
 
23
23
  /**
24
24
  * File-persisted pattern cache — survives across process invocations.
@@ -219,4 +219,4 @@ function main() {
219
219
  });
220
220
  }
221
221
 
222
- main();
222
+ try { main(); } catch (err) { hookLog('dont-repeat', 'error', err?.message ?? String(err)); }
@@ -13,7 +13,7 @@
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, insertFtsRow, readHookStdin, getActiveMindloreDir, getProjectName } = require('./lib/mindlore-common.cjs');
16
+ const { MINDLORE_DIR, DB_NAME, sha256, openDatabase, getAllMdFiles, parseFrontmatter, extractFtsMetadata, insertFtsRow, readHookStdin, getActiveMindloreDir, getProjectName, hookLog } = require('./lib/mindlore-common.cjs');
17
17
 
18
18
  function main() {
19
19
  const filePath = readHookStdin(['path', 'file_path']);
@@ -75,4 +75,4 @@ function main() {
75
75
  // process.stdout.write kaldırıldı (kimse görmüyor)
76
76
  }
77
77
 
78
- main();
78
+ try { main(); } catch (err) { hookLog('fts5-sync', 'error', err?.message ?? String(err)); }
@@ -10,7 +10,7 @@
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, insertFtsRow, readHookStdin, getProjectName } = require('./lib/mindlore-common.cjs');
13
+ const { MINDLORE_DIR, DB_NAME, SKIP_FILES, sha256, openDatabase, parseFrontmatter, extractFtsMetadata, insertFtsRow, readHookStdin, getProjectName, globalDir, hookLog } = require('./lib/mindlore-common.cjs');
14
14
 
15
15
  function main() {
16
16
  const filePath = readHookStdin(['path', 'file_path']);
@@ -19,10 +19,19 @@ function main() {
19
19
  // Only process .md files inside .mindlore/ (resolved path check prevents traversal)
20
20
  if (!filePath.endsWith('.md')) return;
21
21
  const resolvedFile = path.resolve(filePath);
22
- if (!resolvedFile.includes(path.sep + MINDLORE_DIR + path.sep) && !resolvedFile.includes(path.sep + MINDLORE_DIR)) return;
22
+ if (!resolvedFile.includes(path.sep + MINDLORE_DIR + path.sep) && !resolvedFile.includes(path.sep + MINDLORE_DIR)) {
23
+ // CC memory path (~/.claude/projects/*/memory/*.md) — index to global mindlore DB
24
+ const isCcMemory = resolvedFile.includes(path.sep + '.claude' + path.sep + 'projects' + path.sep)
25
+ && resolvedFile.includes(path.sep + 'memory' + path.sep)
26
+ && resolvedFile.endsWith('.md');
27
+ if (!isCcMemory) return;
28
+
29
+ // CC memory path — index to global mindlore DB
30
+ indexCcMemory(resolvedFile);
31
+ return;
32
+ }
23
33
 
24
34
  const fileName = path.basename(filePath);
25
- if (SKIP_FILES.has(fileName)) return;
26
35
 
27
36
  // Find the .mindlore dir from the file path
28
37
  const mindloreIdx = filePath.indexOf(MINDLORE_DIR);
@@ -31,6 +40,14 @@ function main() {
31
40
 
32
41
  if (!fs.existsSync(dbPath)) return;
33
42
 
43
+ // Catch-up scan: when INDEX.md or log.md triggers, index recently-modified files
44
+ if (['INDEX.md', 'log.md'].includes(fileName)) {
45
+ catchUpScan(baseDir, dbPath);
46
+ return;
47
+ }
48
+
49
+ if (SKIP_FILES.has(fileName)) return;
50
+
34
51
  if (!fs.existsSync(filePath)) {
35
52
  // File was deleted — remove from index
36
53
  const db = openDatabase(dbPath);
@@ -80,4 +97,125 @@ function main() {
80
97
  }
81
98
  }
82
99
 
100
+ function indexCcMemory(filePath) {
101
+ const CC_MEMORY_CATEGORY = 'cc-memory';
102
+ // CC memory constants live in TS (scripts/lib/constants.ts) — CJS hooks can't require TS directly
103
+ const globalBase = globalDir();
104
+ const dbPath = path.join(globalBase, DB_NAME);
105
+
106
+ const content = fs.readFileSync(filePath, 'utf8').replace(/\r\n/g, '\n');
107
+ if (!content.trim()) return;
108
+
109
+ // Privacy filter — redact secrets before DB write
110
+ let cleaned = content;
111
+ try {
112
+ const { redactSecrets } = require('../dist/scripts/lib/privacy-filter.js');
113
+ cleaned = redactSecrets(content);
114
+ } catch (_err) {
115
+ // privacy-filter not built — use raw content
116
+ }
117
+
118
+ // SHA256 dedup
119
+ const hash = sha256(cleaned);
120
+ const db = openDatabase(dbPath);
121
+ if (!db) return;
122
+
123
+ try {
124
+ const existing = db.prepare('SELECT content_hash FROM file_hashes WHERE path = ?').get(filePath);
125
+ if (existing && existing.content_hash === hash) {
126
+ return; // unchanged — finally handles db.close()
127
+ }
128
+
129
+ const { meta, body } = parseFrontmatter(cleaned);
130
+ const memType = String(meta.type || 'unknown');
131
+
132
+ // Extract project scope from path: ~/.claude/projects/C--Users-X-proj/memory/
133
+ const projMatch = filePath.match(/projects[/\\]([^/\\]+)[/\\]memory/);
134
+ const projectScope = projMatch ? projMatch[1] : null;
135
+
136
+ const ftsData = extractFtsMetadata(meta, body, filePath, globalBase);
137
+
138
+ // Update FTS5 + hash atomically
139
+ const updateIndex = db.transaction(() => {
140
+ db.prepare('DELETE FROM mindlore_fts WHERE path = ?').run(filePath);
141
+ insertFtsRow(db, {
142
+ path: filePath,
143
+ ...ftsData,
144
+ category: CC_MEMORY_CATEGORY,
145
+ type: memType,
146
+ project: projectScope,
147
+ });
148
+ db.prepare(
149
+ `INSERT INTO file_hashes (path, content_hash, last_indexed, source_type, project_scope)
150
+ VALUES (?, ?, ?, ?, ?)
151
+ ON CONFLICT(path) DO UPDATE SET
152
+ content_hash = excluded.content_hash,
153
+ last_indexed = excluded.last_indexed,
154
+ source_type = excluded.source_type,
155
+ project_scope = excluded.project_scope`
156
+ ).run(filePath, hash, new Date().toISOString(), CC_MEMORY_CATEGORY, projectScope);
157
+ });
158
+ updateIndex();
159
+
160
+ // Copy to ~/.mindlore/memory/{project}/ for git-sync + obsidian
161
+ const memoryDir = path.join(globalBase, 'memory', projectScope || '_global');
162
+ fs.mkdirSync(memoryDir, { recursive: true });
163
+ const destPath = path.join(memoryDir, path.basename(filePath));
164
+ fs.writeFileSync(destPath, cleaned, 'utf8');
165
+ } finally {
166
+ db.close();
167
+ }
168
+ }
169
+
170
+ function catchUpScan(baseDir, dbPath) {
171
+ const CATCH_UP_DIRS = ['raw', 'sources', 'analyses', 'diary'];
172
+ const fiveMinAgo = Date.now() - 5 * 60 * 1000;
173
+
174
+ const db = openDatabase(dbPath);
175
+ if (!db) return;
176
+
177
+ try {
178
+ let indexed = 0;
179
+ for (const dir of CATCH_UP_DIRS) {
180
+ const dirPath = path.join(baseDir, dir);
181
+ if (!fs.existsSync(dirPath)) continue;
182
+
183
+ const files = fs.readdirSync(dirPath).filter(f => f.endsWith('.md'));
184
+ for (const file of files) {
185
+ const filePath = path.join(dirPath, file);
186
+ const stat = fs.statSync(filePath);
187
+ if (stat.mtimeMs < fiveMinAgo) continue;
188
+
189
+ const content = fs.readFileSync(filePath, 'utf8').replace(/\r\n/g, '\n');
190
+ const hash = sha256(content);
191
+
192
+ const existing = db.prepare('SELECT content_hash FROM file_hashes WHERE path = ?').get(filePath);
193
+ if (existing && existing.content_hash === hash) continue;
194
+
195
+ const { meta, body } = parseFrontmatter(content);
196
+ const ftsData = extractFtsMetadata(meta, body, filePath, baseDir);
197
+
198
+ const update = db.transaction(() => {
199
+ db.prepare('DELETE FROM mindlore_fts WHERE path = ?').run(filePath);
200
+ insertFtsRow(db, { path: filePath, ...ftsData, project: getProjectName() });
201
+ db.prepare(
202
+ `INSERT INTO file_hashes (path, content_hash, last_indexed)
203
+ VALUES (?, ?, ?)
204
+ ON CONFLICT(path) DO UPDATE SET
205
+ content_hash = excluded.content_hash,
206
+ last_indexed = excluded.last_indexed`
207
+ ).run(filePath, hash, new Date().toISOString());
208
+ });
209
+ update();
210
+ indexed++;
211
+ }
212
+ }
213
+ if (indexed > 0) {
214
+ hookLog(`catch-up: ${indexed} file(s) indexed`);
215
+ }
216
+ } finally {
217
+ db.close();
218
+ }
219
+ }
220
+
83
221
  main();
@@ -6,7 +6,7 @@
6
6
  */
7
7
 
8
8
  const fs = require('fs');
9
- const { findMindloreDir, readConfig, DEFAULT_MODELS } = require('./lib/mindlore-common.cjs');
9
+ const { findMindloreDir, readConfig, DEFAULT_MODELS, hookLog } = require('./lib/mindlore-common.cjs');
10
10
 
11
11
  const SKILL_KEYS = Object.keys(DEFAULT_MODELS).filter((k) => k !== 'default');
12
12
  const MARKER_REGEX = new RegExp(`\\[mindlore:(${SKILL_KEYS.join('|')})\\]`);
@@ -51,4 +51,4 @@ function main() {
51
51
  process.stdout.write(JSON.stringify(output));
52
52
  }
53
53
 
54
- main();
54
+ try { main(); } catch (err) { hookLog('model-router', 'error', err?.message ?? String(err)); }
@@ -14,7 +14,7 @@
14
14
 
15
15
  const fs = require('fs');
16
16
  const path = require('path');
17
- const { findMindloreDir, getLatestDelta } = require('./lib/mindlore-common.cjs');
17
+ const { findMindloreDir, getLatestDelta, hookLog } = require('./lib/mindlore-common.cjs');
18
18
 
19
19
  function main() {
20
20
  const baseDir = findMindloreDir();
@@ -43,4 +43,4 @@ function main() {
43
43
  }
44
44
  }
45
45
 
46
- main();
46
+ try { main(); } catch (err) { hookLog('post-compact', 'error', err?.message ?? String(err)); }
@@ -13,7 +13,7 @@
13
13
 
14
14
  const fs = require('fs');
15
15
  const path = require('path');
16
- const { findMindloreDir, getProjectName } = require('./lib/mindlore-common.cjs');
16
+ const { findMindloreDir, getProjectName, hookLog } = require('./lib/mindlore-common.cjs');
17
17
 
18
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
19
  const PROSE_EXTS = new Set(['.md', '.txt', '.rst', '.adoc']);
@@ -103,4 +103,4 @@ function main() {
103
103
  });
104
104
  }
105
105
 
106
- main();
106
+ try { main(); } catch (err) { hookLog('post-read', 'error', err?.message ?? String(err)); }
@@ -11,7 +11,7 @@
11
11
 
12
12
  const fs = require('fs');
13
13
  const path = require('path');
14
- const { findMindloreDir } = require('./lib/mindlore-common.cjs');
14
+ const { findMindloreDir, hookLog } = require('./lib/mindlore-common.cjs');
15
15
 
16
16
  function main() {
17
17
  const baseDir = findMindloreDir();
@@ -45,4 +45,4 @@ function main() {
45
45
  process.stdout.write('[Mindlore: pre-compact FTS5 flush complete]\n');
46
46
  }
47
47
 
48
- main();
48
+ try { main(); } catch (err) { hookLog('pre-compact', 'error', err?.message ?? String(err)); }