mindlore 0.6.1 → 0.6.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 (80) hide show
  1. package/dist/scripts/cc-session-sync.d.ts +1 -0
  2. package/dist/scripts/cc-session-sync.d.ts.map +1 -1
  3. package/dist/scripts/cc-session-sync.js +55 -0
  4. package/dist/scripts/cc-session-sync.js.map +1 -1
  5. package/dist/scripts/fetch-raw.js +38 -2
  6. package/dist/scripts/fetch-raw.js.map +1 -1
  7. package/dist/scripts/init.js +47 -43
  8. package/dist/scripts/init.js.map +1 -1
  9. package/dist/scripts/lib/episodes.d.ts +1 -1
  10. package/dist/scripts/lib/episodes.d.ts.map +1 -1
  11. package/dist/scripts/lib/episodes.js +1 -1
  12. package/dist/scripts/lib/episodes.js.map +1 -1
  13. package/dist/scripts/lib/migrations-v062.d.ts +3 -0
  14. package/dist/scripts/lib/migrations-v062.d.ts.map +1 -0
  15. package/dist/scripts/lib/migrations-v062.js +35 -0
  16. package/dist/scripts/lib/migrations-v062.js.map +1 -0
  17. package/dist/scripts/lib/session-payload.d.ts.map +1 -1
  18. package/dist/scripts/lib/session-payload.js +12 -0
  19. package/dist/scripts/lib/session-payload.js.map +1 -1
  20. package/dist/scripts/lib/triage.d.ts +18 -0
  21. package/dist/scripts/lib/triage.d.ts.map +1 -0
  22. package/dist/scripts/lib/triage.js +81 -0
  23. package/dist/scripts/lib/triage.js.map +1 -0
  24. package/dist/scripts/mindlore-doctor.d.ts +1 -0
  25. package/dist/scripts/mindlore-doctor.d.ts.map +1 -1
  26. package/dist/scripts/mindlore-doctor.js +26 -1
  27. package/dist/scripts/mindlore-doctor.js.map +1 -1
  28. package/dist/scripts/mindlore-health-check.d.ts +10 -0
  29. package/dist/scripts/mindlore-health-check.d.ts.map +1 -1
  30. package/dist/scripts/mindlore-health-check.js +26 -1
  31. package/dist/scripts/mindlore-health-check.js.map +1 -1
  32. package/dist/scripts/mindlore-perf.d.ts +3 -3
  33. package/dist/scripts/mindlore-perf.d.ts.map +1 -1
  34. package/dist/scripts/mindlore-perf.js +10 -8
  35. package/dist/scripts/mindlore-perf.js.map +1 -1
  36. package/dist/tests/compaction-snapshot.test.d.ts +2 -0
  37. package/dist/tests/compaction-snapshot.test.d.ts.map +1 -0
  38. package/dist/tests/compaction-snapshot.test.js +55 -0
  39. package/dist/tests/compaction-snapshot.test.js.map +1 -0
  40. package/dist/tests/diary.test.js +3 -3
  41. package/dist/tests/diary.test.js.map +1 -1
  42. package/dist/tests/doctor.test.js +13 -0
  43. package/dist/tests/doctor.test.js.map +1 -1
  44. package/dist/tests/fetch-raw.test.js +12 -5
  45. package/dist/tests/fetch-raw.test.js.map +1 -1
  46. package/dist/tests/health-check-memory.test.js +27 -0
  47. package/dist/tests/health-check-memory.test.js.map +1 -1
  48. package/dist/tests/helpers/db.d.ts.map +1 -1
  49. package/dist/tests/helpers/db.js +3 -1
  50. package/dist/tests/helpers/db.js.map +1 -1
  51. package/dist/tests/init.test.js +20 -0
  52. package/dist/tests/init.test.js.map +1 -1
  53. package/dist/tests/migrations-v062.test.d.ts +2 -0
  54. package/dist/tests/migrations-v062.test.d.ts.map +1 -0
  55. package/dist/tests/migrations-v062.test.js +61 -0
  56. package/dist/tests/migrations-v062.test.js.map +1 -0
  57. package/dist/tests/nomination.test.js +1 -1
  58. package/dist/tests/savings.test.d.ts +2 -0
  59. package/dist/tests/savings.test.d.ts.map +1 -0
  60. package/dist/tests/savings.test.js +87 -0
  61. package/dist/tests/savings.test.js.map +1 -0
  62. package/dist/tests/session-summary.test.d.ts +2 -0
  63. package/dist/tests/session-summary.test.d.ts.map +1 -0
  64. package/dist/tests/session-summary.test.js +102 -0
  65. package/dist/tests/session-summary.test.js.map +1 -0
  66. package/dist/tests/telemetry-perf.test.js +7 -0
  67. package/dist/tests/telemetry-perf.test.js.map +1 -1
  68. package/dist/tests/triage.test.d.ts +2 -0
  69. package/dist/tests/triage.test.d.ts.map +1 -0
  70. package/dist/tests/triage.test.js +69 -0
  71. package/dist/tests/triage.test.js.map +1 -0
  72. package/hooks/lib/mindlore-common.cjs +18 -10
  73. package/hooks/mindlore-post-compact.cjs +23 -3
  74. package/hooks/mindlore-pre-compact.cjs +77 -3
  75. package/hooks/mindlore-session-end.cjs +27 -17
  76. package/hooks/mindlore-session-focus.cjs +104 -68
  77. package/package.json +5 -4
  78. package/plugin.json +1 -1
  79. package/skills/mindlore-maintain/SKILL.md +1 -0
  80. package/templates/config.json +1 -1
@@ -0,0 +1,102 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const cc_session_sync_js_1 = require("../scripts/cc-session-sync.js");
4
+ const DECISION_KEYWORDS_TR = ['karar:', 'ertele', 'seçtik', 'yapma:'];
5
+ const DECISION_KEYWORDS_EN = ['decision:', 'defer', 'chose', 'skip:'];
6
+ const ALL_KEYWORDS = [...DECISION_KEYWORDS_TR, ...DECISION_KEYWORDS_EN];
7
+ describe('session summary extraction', () => {
8
+ it('extracts intent from first user message', () => {
9
+ const transcript = [
10
+ '---',
11
+ 'type: raw',
12
+ 'project: mindlore',
13
+ '---',
14
+ '',
15
+ '## User',
16
+ '',
17
+ 'v0.6.2 spec yaz',
18
+ '',
19
+ '## Assistant',
20
+ '',
21
+ 'Tamam, başlıyorum.',
22
+ '',
23
+ ].join('\n');
24
+ const summary = (0, cc_session_sync_js_1.extractSessionSummary)(transcript);
25
+ expect(summary).toContain('v0.6.2 spec yaz');
26
+ });
27
+ it('detects decision keywords', () => {
28
+ const transcript = [
29
+ '---',
30
+ 'type: raw',
31
+ '---',
32
+ '',
33
+ '## User',
34
+ '',
35
+ 'karar: tek DB kullan',
36
+ '',
37
+ ].join('\n');
38
+ const summary = (0, cc_session_sync_js_1.extractSessionSummary)(transcript);
39
+ expect(summary).toContain('karar');
40
+ });
41
+ it('detects english decision keywords', () => {
42
+ const transcript = [
43
+ '---',
44
+ 'type: raw',
45
+ '---',
46
+ '',
47
+ '## User',
48
+ '',
49
+ 'decision: use single DB',
50
+ '',
51
+ ].join('\n');
52
+ const summary = (0, cc_session_sync_js_1.extractSessionSummary)(transcript);
53
+ expect(summary).toContain('decision');
54
+ });
55
+ it('rejects bare words without decision context', () => {
56
+ const text = 'implementasyon planı yazacağız';
57
+ const hasDecision = ALL_KEYWORDS.some(kw => text.toLowerCase().includes(kw));
58
+ expect(hasDecision).toBe(false);
59
+ });
60
+ it('includes last intent when different from first', () => {
61
+ const transcript = [
62
+ '---',
63
+ 'type: raw',
64
+ '---',
65
+ '',
66
+ '## User',
67
+ '',
68
+ 'brainstorming yap',
69
+ '',
70
+ '## Assistant',
71
+ '',
72
+ 'Tamam.',
73
+ '',
74
+ '## User',
75
+ '',
76
+ 'spec yaz',
77
+ '',
78
+ ].join('\n');
79
+ const summary = (0, cc_session_sync_js_1.extractSessionSummary)(transcript);
80
+ expect(summary).toContain('brainstorming');
81
+ expect(summary).toContain('spec yaz');
82
+ });
83
+ it('returns empty string for empty transcript', () => {
84
+ const summary = (0, cc_session_sync_js_1.extractSessionSummary)('');
85
+ expect(summary).toBe('');
86
+ });
87
+ it('handles transcript with no user messages', () => {
88
+ const transcript = [
89
+ '---',
90
+ 'type: raw',
91
+ '---',
92
+ '',
93
+ '## Assistant',
94
+ '',
95
+ 'Hello',
96
+ '',
97
+ ].join('\n');
98
+ const summary = (0, cc_session_sync_js_1.extractSessionSummary)(transcript);
99
+ expect(summary).toBe('');
100
+ });
101
+ });
102
+ //# sourceMappingURL=session-summary.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session-summary.test.js","sourceRoot":"","sources":["../../tests/session-summary.test.ts"],"names":[],"mappings":"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"}
@@ -54,5 +54,12 @@ describe('mindlore-perf', () => {
54
54
  expect(groups['search']).toHaveLength(3);
55
55
  expect(groups['session-focus']).toHaveLength(2);
56
56
  });
57
+ it('withTelemetry works when fn returns undefined (S1 regression)', async () => {
58
+ const commonPath = require.resolve('../hooks/lib/mindlore-common.cjs');
59
+ delete require.cache[commonPath];
60
+ const { withTelemetry } = require(commonPath);
61
+ const fn = () => { };
62
+ await expect(withTelemetry('test-no-return', fn)).resolves.not.toThrow();
63
+ });
57
64
  });
58
65
  //# sourceMappingURL=telemetry-perf.test.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"telemetry-perf.test.js","sourceRoot":"","sources":["../../tests/telemetry-perf.test.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"telemetry-perf.test.js","sourceRoot":"","sources":["../../tests/telemetry-perf.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=triage.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"triage.test.d.ts","sourceRoot":"","sources":["../../tests/triage.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,69 @@
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 db_js_1 = require("./helpers/db.js");
10
+ const triage_js_1 = require("../scripts/lib/triage.js");
11
+ describe('raw inbox triage', () => {
12
+ let tmpDir;
13
+ beforeEach(() => {
14
+ tmpDir = fs_1.default.mkdtempSync(path_1.default.join(os_1.default.tmpdir(), 'triage-'));
15
+ fs_1.default.mkdirSync(path_1.default.join(tmpDir, 'raw'), { recursive: true });
16
+ fs_1.default.mkdirSync(path_1.default.join(tmpDir, 'sources'), { recursive: true });
17
+ });
18
+ afterEach(() => {
19
+ fs_1.default.rmSync(tmpDir, { recursive: true, force: true });
20
+ });
21
+ it('lists unpromoted raw files', () => {
22
+ fs_1.default.writeFileSync(path_1.default.join(tmpDir, 'raw', 'orphan.md'), '# Title\nContent here');
23
+ fs_1.default.writeFileSync(path_1.default.join(tmpDir, 'sources', 'existing.md'), '# Existing');
24
+ const result = (0, triage_js_1.listUnpromoted)(tmpDir);
25
+ expect(result.some(r => r.name === 'orphan.md')).toBe(true);
26
+ expect(result.some(r => r.name === 'existing.md')).toBe(false);
27
+ });
28
+ it('returns empty for non-existent raw dir', () => {
29
+ const emptyDir = fs_1.default.mkdtempSync(path_1.default.join(os_1.default.tmpdir(), 'triage-empty-'));
30
+ const result = (0, triage_js_1.listUnpromoted)(emptyDir);
31
+ expect(result).toHaveLength(0);
32
+ fs_1.default.rmSync(emptyDir, { recursive: true, force: true });
33
+ });
34
+ it('extracts metadata from raw file', () => {
35
+ const content = [
36
+ '---',
37
+ 'url: https://example.com',
38
+ 'date_captured: 2026-04-01',
39
+ '---',
40
+ '# Main Title',
41
+ '## Section 1',
42
+ '## Section 2',
43
+ 'Some content here.',
44
+ ].join('\n');
45
+ fs_1.default.writeFileSync(path_1.default.join(tmpDir, 'raw', 'test.md'), content);
46
+ const meta = (0, triage_js_1.extractRawMetadata)(path_1.default.join(tmpDir, 'raw', 'test.md'));
47
+ expect(meta.title).toBe('Main Title');
48
+ expect(meta.url).toBe('https://example.com');
49
+ expect(meta.headings).toContain('Section 1');
50
+ expect(meta.line_count).toBe(8);
51
+ });
52
+ it('uses filename as fallback title', () => {
53
+ fs_1.default.writeFileSync(path_1.default.join(tmpDir, 'raw', 'no-heading.md'), 'Just some text');
54
+ const meta = (0, triage_js_1.extractRawMetadata)(path_1.default.join(tmpDir, 'raw', 'no-heading.md'));
55
+ expect(meta.title).toBe('no-heading');
56
+ });
57
+ it('caches metadata in raw_metadata table', () => {
58
+ fs_1.default.writeFileSync(path_1.default.join(tmpDir, 'raw', 'cached.md'), '# Cached\nContent');
59
+ const dbPath = path_1.default.join(tmpDir, 'mindlore.db');
60
+ const db = (0, db_js_1.createTestDbWithMigrations)(dbPath);
61
+ (0, triage_js_1.cacheRawMetadata)(db, path_1.default.join(tmpDir, 'raw', 'cached.md'), tmpDir);
62
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
63
+ const row = db.prepare("SELECT * FROM raw_metadata WHERE path = ?").get('raw/cached.md');
64
+ expect(row).toBeDefined();
65
+ expect(row?.title).toBe('Cached');
66
+ db.close();
67
+ });
68
+ });
69
+ //# sourceMappingURL=triage.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"triage.test.js","sourceRoot":"","sources":["../../tests/triage.test.ts"],"names":[],"mappings":"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"}
@@ -347,7 +347,7 @@ CREATE TABLE IF NOT EXISTS episodes (
347
347
  // ~625 tokens context budget for multi-session inject (~4 chars/token)
348
348
  const MULTI_SESSION_TOKEN_CAP_CHARS = 2500;
349
349
 
350
- const EPISODE_KINDS_CJS = ['session', 'decision', 'event', 'preference', 'learning', 'friction', 'discovery', 'nomination'];
350
+ const EPISODE_KINDS_CJS = ['session', 'decision', 'event', 'preference', 'learning', 'friction', 'discovery', 'nomination', 'session-summary'];
351
351
 
352
352
  /**
353
353
  * Valid episode statuses. CO-EVOLUTION: mirrors EPISODE_STATUSES in scripts/lib/episodes.ts
@@ -671,7 +671,7 @@ function _rotateFile(filePath, maxBytes, keepLines) {
671
671
 
672
672
  let _telDirEnsured = false;
673
673
 
674
- function _writeTelemetry(hookName, duration_ms, ok, extra) {
674
+ function _writeTelemetry({ hookName, duration_ms, ok, extra }) {
675
675
  try {
676
676
  if (!_telDirEnsured) {
677
677
  fs.mkdirSync(GLOBAL_MINDLORE_DIR, { recursive: true });
@@ -679,8 +679,11 @@ function _writeTelemetry(hookName, duration_ms, ok, extra) {
679
679
  }
680
680
  const telPath = path.join(GLOBAL_MINDLORE_DIR, 'telemetry.jsonl');
681
681
  const entry = { ts: new Date().toISOString(), hook: hookName, duration_ms, ok };
682
- if (extra?.injected_tokens) entry.injected_tokens = extra.injected_tokens;
683
- if (extra?.full_read_tokens) entry.full_read_tokens = extra.full_read_tokens;
682
+ if (extra && typeof extra === 'object') {
683
+ for (const key of ['inject_tokens', 'source_tokens', 'injected_tokens', 'full_read_tokens']) {
684
+ if (typeof extra[key] === 'number') entry[key] = extra[key];
685
+ }
686
+ }
684
687
  const line = JSON.stringify(entry) + '\n';
685
688
  _rotateFile(telPath, HOOK_LOG_MAX_BYTES, TELEMETRY_KEEP_LINES);
686
689
  fs.appendFileSync(telPath, line);
@@ -698,7 +701,8 @@ async function withTelemetry(hookName, fn) {
698
701
  ok = false;
699
702
  thrown = err;
700
703
  }
701
- _writeTelemetry(hookName, Date.now() - start, ok);
704
+ const extra = (result && typeof result === 'object') ? result : undefined;
705
+ _writeTelemetry({ hookName, duration_ms: Date.now() - start, ok, extra });
702
706
  if (thrown) throw thrown;
703
707
  return result;
704
708
  }
@@ -714,21 +718,25 @@ function withTelemetrySync(hookName, fn) {
714
718
  ok = false;
715
719
  thrown = err;
716
720
  }
717
- _writeTelemetry(hookName, Date.now() - start, ok);
721
+ const extra = (result && typeof result === 'object') ? result : undefined;
722
+ _writeTelemetry({ hookName, duration_ms: Date.now() - start, ok, extra });
718
723
  if (thrown) throw thrown;
719
724
  return result;
720
725
  }
721
726
 
722
- function withTimeoutDb(db, sql, params = [], timeoutMs = 3000) {
723
- if (!db) return [];
727
+ function withTimeoutDb(db, sql, params = [], { timeoutMs = 3000, mode = 'all' } = {}) {
728
+ if (!db) return mode === 'get' ? undefined : [];
724
729
  try {
725
730
  db.pragma(`busy_timeout = ${timeoutMs}`);
726
731
  const stmt = db.prepare(sql);
732
+ if (mode === 'get') {
733
+ return params.length > 0 ? stmt.get(...params) : stmt.get();
734
+ }
727
735
  return params.length > 0 ? stmt.all(...params) : stmt.all();
728
736
  } catch (err) {
729
737
  hookLog('timeout', 'warn', `DB query timeout/error: ${err.message}`);
730
- _writeTelemetry('db_timeout', 0, false);
731
- return [];
738
+ _writeTelemetry({ hookName: 'db_timeout', duration_ms: 0, ok: false });
739
+ return mode === 'get' ? undefined : [];
732
740
  }
733
741
  }
734
742
 
@@ -14,7 +14,7 @@
14
14
 
15
15
  const fs = require('fs');
16
16
  const path = require('path');
17
- const { findMindloreDir, getLatestDelta, hookLog, withTelemetry } = require('./lib/mindlore-common.cjs');
17
+ const { findMindloreDir, getLatestDelta, readConfig, hookLog, withTelemetry } = require('./lib/mindlore-common.cjs');
18
18
 
19
19
  function main() {
20
20
  const baseDir = findMindloreDir();
@@ -29,8 +29,8 @@ function main() {
29
29
  output.push(`[Mindlore INDEX (post-compact)]\n${content}`);
30
30
  }
31
31
 
32
- // Re-inject latest delta
33
32
  const diaryDir = path.join(baseDir, 'diary');
33
+
34
34
  const latestDelta = getLatestDelta(diaryDir);
35
35
  if (latestDelta) {
36
36
  const deltaContent = fs.readFileSync(latestDelta, 'utf8').trim();
@@ -38,8 +38,28 @@ function main() {
38
38
  output.push(`[Mindlore Delta (post-compact): ${deltaName}]\n${deltaContent}`);
39
39
  }
40
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
+
41
54
  if (output.length > 0) {
42
- process.stdout.write(output.join('\n\n') + '\n');
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');
43
63
  }
44
64
  }
45
65
 
@@ -11,7 +11,7 @@
11
11
 
12
12
  const fs = require('fs');
13
13
  const path = require('path');
14
- const { findMindloreDir, hookLog, withTelemetry } = require('./lib/mindlore-common.cjs');
14
+ const { findMindloreDir, openDatabase, hookLog, withTelemetry } = require('./lib/mindlore-common.cjs');
15
15
 
16
16
  function main() {
17
17
  const baseDir = findMindloreDir();
@@ -34,11 +34,10 @@ function main() {
34
34
 
35
35
  const now = new Date();
36
36
  const iso = now.toISOString();
37
+ const ts = iso.replace(/[:.]/g, '-');
37
38
 
38
- // Write pre-compact episode
39
39
  const episodesDir = path.join(baseDir, 'episodes');
40
40
  try {
41
- const ts = iso.replace(/[:.]/g, '-');
42
41
  const episodePath = path.join(episodesDir, `pre-compact-${ts}.md`);
43
42
  const content = [
44
43
  '---',
@@ -61,6 +60,81 @@ function main() {
61
60
  fs.appendFileSync(logPath, entry, 'utf8');
62
61
  } catch (_err) { /* log file may not exist */ }
63
62
 
63
+ // Build compaction snapshot (#17)
64
+ const diaryDir = path.join(baseDir, 'diary');
65
+ try {
66
+ const sections = [];
67
+
68
+ try {
69
+ const dbPath = path.join(baseDir, 'mindlore.db');
70
+ const db = openDatabase(dbPath, { readonly: true });
71
+ if (db) {
72
+ try {
73
+ const episodes = db.prepare(
74
+ "SELECT kind, summary FROM episodes WHERE created_at > datetime('now', '-4 hours') ORDER BY created_at DESC LIMIT 20"
75
+ ).all();
76
+ if (episodes.length > 0) {
77
+ const grouped = {};
78
+ for (const ep of episodes) {
79
+ const kind = ep.kind || 'other';
80
+ if (!grouped[kind]) grouped[kind] = [];
81
+ grouped[kind].push(ep.summary);
82
+ }
83
+ sections.push('## Session Episodes');
84
+ for (const [kind, items] of Object.entries(grouped)) {
85
+ sections.push(`### ${kind}`);
86
+ for (const item of items) sections.push(`- ${item}`);
87
+ }
88
+ }
89
+ } finally {
90
+ db.close();
91
+ }
92
+ }
93
+ } catch (_err) { /* graceful skip */ }
94
+
95
+ try {
96
+ const { execSync } = require('child_process');
97
+ const diffStat = execSync('git diff --stat HEAD 2>/dev/null || echo ""', {
98
+ encoding: 'utf8', timeout: 2000, windowsHide: true,
99
+ }).trim();
100
+ if (diffStat) {
101
+ sections.push('## Changed Files (uncommitted)', '```', diffStat, '```');
102
+ }
103
+ } catch (_err) { /* no git */ }
104
+
105
+ try {
106
+ const plansDir = path.join(process.cwd(), '.claude', 'plans');
107
+ if (fs.existsSync(plansDir)) {
108
+ const plans = fs.readdirSync(plansDir).filter(f => f.endsWith('.md'));
109
+ if (plans.length > 0) {
110
+ const latestPlan = plans.sort().pop();
111
+ sections.push(`## Active Plan: ${latestPlan}`);
112
+ }
113
+ }
114
+ } catch (_err) { /* skip */ }
115
+
116
+ if (sections.length > 0) {
117
+ const snapshotContent = [
118
+ '---',
119
+ 'type: compaction-snapshot',
120
+ `date: ${iso.slice(0, 10)}`,
121
+ `project: ${path.basename(process.cwd())}`,
122
+ '---',
123
+ '',
124
+ ...sections,
125
+ ].join('\n');
126
+ fs.writeFileSync(path.join(diaryDir, `compaction-snapshot-${ts}.md`), snapshotContent);
127
+ }
128
+
129
+ const snapshots = fs.readdirSync(diaryDir)
130
+ .filter(f => f.startsWith('compaction-snapshot-'))
131
+ .sort();
132
+ while (snapshots.length > 5) {
133
+ const oldest = snapshots.shift();
134
+ if (oldest) fs.unlinkSync(path.join(diaryDir, oldest));
135
+ }
136
+ } catch (_err) { /* snapshot is best-effort */ }
137
+
64
138
  process.stdout.write('[Mindlore: pre-compact FTS5 flush complete]\n');
65
139
  }
66
140
 
@@ -221,6 +221,20 @@ function main() {
221
221
  fs.appendFileSync(logPath, logEntry, 'utf8');
222
222
  }
223
223
 
224
+ // Raw accumulation warning
225
+ try {
226
+ const rawDir = path.join(baseDir, 'raw');
227
+ const sourcesDir = path.join(baseDir, 'sources');
228
+ if (fs.existsSync(rawDir) && fs.existsSync(sourcesDir)) {
229
+ const rawFiles = fs.readdirSync(rawDir).filter(f => f.endsWith('.md'));
230
+ const sourceFiles = new Set(fs.readdirSync(sourcesDir).filter(f => f.endsWith('.md')));
231
+ const unpromoted = rawFiles.filter(f => !sourceFiles.has(f)).length;
232
+ if (unpromoted >= 5) {
233
+ process.stdout.write(`[Mindlore] ${unpromoted} raw dosya promote bekliyor — \`/mindlore-maintain triage\` ile listele\n`);
234
+ }
235
+ }
236
+ } catch (_err) { /* graceful skip */ }
237
+
224
238
  // Heavy ops: detach into child process so CC can exit immediately.
225
239
  // Fixes "Hook cancelled" when CC kills the hook before completion.
226
240
  // See: https://github.com/anthropics/claude-code/issues/41577
@@ -436,29 +450,25 @@ function syncObsidian(baseDir) {
436
450
  const destBase = path.join(vaultPath, 'mindlore');
437
451
  let exported = 0;
438
452
 
439
- for (const dir of EXPORT_DIRS) {
440
- const srcDir = path.join(baseDir, dir);
441
- if (!fs.existsSync(srcDir)) continue;
442
-
443
- const destDir = path.join(destBase, dir);
453
+ function walkAndExport(srcDir, destDir) {
454
+ if (!fs.existsSync(srcDir)) return;
444
455
  fs.mkdirSync(destDir, { recursive: true });
445
-
446
- for (const file of fs.readdirSync(srcDir).filter(f => f.endsWith('.md') && !f.startsWith('_'))) {
447
- if (exportMdFile(path.join(srcDir, file), path.join(destDir, file), convertFn)) exported++;
448
- }
449
-
450
- // One-level subdirectories (e.g. diary/mindlore/)
451
456
  for (const entry of fs.readdirSync(srcDir, { withFileTypes: true })) {
452
- if (!entry.isDirectory() || entry.name.startsWith('_') || entry.name.startsWith('.')) continue;
453
- const subSrc = path.join(srcDir, entry.name);
454
- const subDest = path.join(destDir, entry.name);
455
- fs.mkdirSync(subDest, { recursive: true });
456
- for (const file of fs.readdirSync(subSrc).filter(f => f.endsWith('.md') && !f.startsWith('_'))) {
457
- if (exportMdFile(path.join(subSrc, file), path.join(subDest, file), convertFn)) exported++;
457
+ if (entry.name.startsWith('_') || entry.name.startsWith('.')) continue;
458
+ const srcPath = path.join(srcDir, entry.name);
459
+ const destPath = path.join(destDir, entry.name);
460
+ if (entry.isDirectory()) {
461
+ walkAndExport(srcPath, destPath);
462
+ } else if (entry.isFile() && entry.name.endsWith('.md')) {
463
+ if (exportMdFile(srcPath, destPath, convertFn)) exported++;
458
464
  }
459
465
  }
460
466
  }
461
467
 
468
+ for (const dir of EXPORT_DIRS) {
469
+ walkAndExport(path.join(baseDir, dir), path.join(destBase, dir));
470
+ }
471
+
462
472
  for (const rootFile of ['INDEX.md', 'log.md']) {
463
473
  const srcPath = path.join(baseDir, rootFile);
464
474
  if (!fs.existsSync(srcPath)) continue;