mindlore 0.6.9 → 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 (152) hide show
  1. package/README.md +31 -4
  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/cc-memory-bulk-sync.d.ts.map +1 -1
  7. package/dist/scripts/cc-memory-bulk-sync.js +2 -1
  8. package/dist/scripts/cc-memory-bulk-sync.js.map +1 -1
  9. package/dist/scripts/cc-session-sync.d.ts.map +1 -1
  10. package/dist/scripts/cc-session-sync.js +3 -2
  11. package/dist/scripts/cc-session-sync.js.map +1 -1
  12. package/dist/scripts/init.js +4 -4
  13. package/dist/scripts/init.js.map +1 -1
  14. package/dist/scripts/lib/constants.d.ts +1 -2
  15. package/dist/scripts/lib/constants.d.ts.map +1 -1
  16. package/dist/scripts/lib/constants.js +2 -22
  17. package/dist/scripts/lib/constants.js.map +1 -1
  18. package/dist/scripts/lib/err-msg.d.ts +2 -0
  19. package/dist/scripts/lib/err-msg.d.ts.map +1 -0
  20. package/dist/scripts/lib/err-msg.js +7 -0
  21. package/dist/scripts/lib/err-msg.js.map +1 -0
  22. package/dist/scripts/lib/mcp-namespace.d.ts +2 -0
  23. package/dist/scripts/lib/mcp-namespace.d.ts.map +1 -0
  24. package/dist/scripts/lib/mcp-namespace.js +21 -0
  25. package/dist/scripts/lib/mcp-namespace.js.map +1 -0
  26. package/dist/scripts/lib/mcp-telemetry.d.ts +11 -0
  27. package/dist/scripts/lib/mcp-telemetry.d.ts.map +1 -0
  28. package/dist/scripts/lib/mcp-telemetry.js +37 -0
  29. package/dist/scripts/lib/mcp-telemetry.js.map +1 -0
  30. package/dist/scripts/lib/mcp-tools.d.ts +10 -0
  31. package/dist/scripts/lib/mcp-tools.d.ts.map +1 -0
  32. package/dist/scripts/lib/mcp-tools.js +121 -0
  33. package/dist/scripts/lib/mcp-tools.js.map +1 -0
  34. package/dist/scripts/lib/rrf.d.ts.map +1 -1
  35. package/dist/scripts/lib/rrf.js +2 -1
  36. package/dist/scripts/lib/rrf.js.map +1 -1
  37. package/dist/scripts/lib/search-engine.d.ts +1 -0
  38. package/dist/scripts/lib/search-engine.d.ts.map +1 -1
  39. package/dist/scripts/lib/search-engine.js +9 -5
  40. package/dist/scripts/lib/search-engine.js.map +1 -1
  41. package/dist/scripts/lib/slugify.d.ts +2 -0
  42. package/dist/scripts/lib/slugify.d.ts.map +1 -0
  43. package/dist/scripts/lib/slugify.js +13 -0
  44. package/dist/scripts/lib/slugify.js.map +1 -0
  45. package/dist/scripts/lib/smart-snippet.d.ts +9 -0
  46. package/dist/scripts/lib/smart-snippet.d.ts.map +1 -0
  47. package/dist/scripts/lib/smart-snippet.js +47 -0
  48. package/dist/scripts/lib/smart-snippet.js.map +1 -0
  49. package/dist/scripts/lib/tool-adapters/brief-adapter.d.ts +15 -0
  50. package/dist/scripts/lib/tool-adapters/brief-adapter.d.ts.map +1 -0
  51. package/dist/scripts/lib/tool-adapters/brief-adapter.js +66 -0
  52. package/dist/scripts/lib/tool-adapters/brief-adapter.js.map +1 -0
  53. package/dist/scripts/lib/tool-adapters/decide-adapter.d.ts +31 -0
  54. package/dist/scripts/lib/tool-adapters/decide-adapter.d.ts.map +1 -0
  55. package/dist/scripts/lib/tool-adapters/decide-adapter.js +71 -0
  56. package/dist/scripts/lib/tool-adapters/decide-adapter.js.map +1 -0
  57. package/dist/scripts/lib/tool-adapters/ingest-adapter.d.ts +16 -0
  58. package/dist/scripts/lib/tool-adapters/ingest-adapter.d.ts.map +1 -0
  59. package/dist/scripts/lib/tool-adapters/ingest-adapter.js +58 -0
  60. package/dist/scripts/lib/tool-adapters/ingest-adapter.js.map +1 -0
  61. package/dist/scripts/lib/tool-adapters/recall-adapter.d.ts +20 -0
  62. package/dist/scripts/lib/tool-adapters/recall-adapter.d.ts.map +1 -0
  63. package/dist/scripts/lib/tool-adapters/recall-adapter.js +69 -0
  64. package/dist/scripts/lib/tool-adapters/recall-adapter.js.map +1 -0
  65. package/dist/scripts/lib/tool-adapters/search-adapter.d.ts +22 -0
  66. package/dist/scripts/lib/tool-adapters/search-adapter.d.ts.map +1 -0
  67. package/dist/scripts/lib/tool-adapters/search-adapter.js +32 -0
  68. package/dist/scripts/lib/tool-adapters/search-adapter.js.map +1 -0
  69. package/dist/scripts/lib/tool-adapters/stats-adapter.d.ts +15 -0
  70. package/dist/scripts/lib/tool-adapters/stats-adapter.d.ts.map +1 -0
  71. package/dist/scripts/lib/tool-adapters/stats-adapter.js +66 -0
  72. package/dist/scripts/lib/tool-adapters/stats-adapter.js.map +1 -0
  73. package/dist/scripts/maintain-cleanup.d.ts.map +1 -1
  74. package/dist/scripts/maintain-cleanup.js +3 -2
  75. package/dist/scripts/maintain-cleanup.js.map +1 -1
  76. package/dist/scripts/mcp-server.d.ts +3 -0
  77. package/dist/scripts/mcp-server.d.ts.map +1 -0
  78. package/dist/scripts/mcp-server.js +85 -0
  79. package/dist/scripts/mcp-server.js.map +1 -0
  80. package/dist/scripts/mindlore-doctor.d.ts.map +1 -1
  81. package/dist/scripts/mindlore-doctor.js +4 -6
  82. package/dist/scripts/mindlore-doctor.js.map +1 -1
  83. package/dist/scripts/mindlore-fts5-index.js +2 -2
  84. package/dist/scripts/mindlore-fts5-index.js.map +1 -1
  85. package/dist/scripts/mindlore-health-check.d.ts.map +1 -1
  86. package/dist/scripts/mindlore-health-check.js +2 -2
  87. package/dist/scripts/mindlore-health-check.js.map +1 -1
  88. package/dist/scripts/validate-manifest-cli.js +2 -2
  89. package/dist/scripts/validate-manifest-cli.js.map +1 -1
  90. package/dist/tests/err-msg.test.d.ts +2 -0
  91. package/dist/tests/err-msg.test.d.ts.map +1 -0
  92. package/dist/tests/err-msg.test.js +24 -0
  93. package/dist/tests/err-msg.test.js.map +1 -0
  94. package/dist/tests/hook-smoke.test.js +1 -1
  95. package/dist/tests/hook-smoke.test.js.map +1 -1
  96. package/dist/tests/manifest-v2.test.js +0 -7
  97. package/dist/tests/manifest-v2.test.js.map +1 -1
  98. package/dist/tests/mcp-server.test.d.ts +2 -0
  99. package/dist/tests/mcp-server.test.d.ts.map +1 -0
  100. package/dist/tests/mcp-server.test.js +118 -0
  101. package/dist/tests/mcp-server.test.js.map +1 -0
  102. package/dist/tests/mcp-tools.test.d.ts +2 -0
  103. package/dist/tests/mcp-tools.test.d.ts.map +1 -0
  104. package/dist/tests/mcp-tools.test.js +243 -0
  105. package/dist/tests/mcp-tools.test.js.map +1 -0
  106. package/dist/tests/search-hook.test.js +1 -1
  107. package/dist/tests/search-hook.test.js.map +1 -1
  108. package/dist/tests/smart-snippet.test.d.ts +2 -0
  109. package/dist/tests/smart-snippet.test.d.ts.map +1 -0
  110. package/dist/tests/smart-snippet.test.js +67 -0
  111. package/dist/tests/smart-snippet.test.js.map +1 -0
  112. package/hooks/cc-memory-bulk-sync.cjs +592 -0
  113. package/hooks/cc-session-sync.cjs +842 -0
  114. package/hooks/hooks.json +149 -0
  115. package/hooks/lib/mindlore-common.cjs +2 -2
  116. package/hooks/lib/secure-io.cjs +17 -0
  117. package/hooks/mindlore-cwd-changed.cjs +19 -34
  118. package/hooks/mindlore-decision-detector.cjs +40 -31
  119. package/hooks/mindlore-dont-repeat.cjs +57 -115
  120. package/hooks/mindlore-fts5-sync.cjs +15 -44
  121. package/hooks/mindlore-index.cjs +100 -101
  122. package/hooks/mindlore-model-router.cjs +20 -32
  123. package/hooks/mindlore-post-compact.cjs +26 -42
  124. package/hooks/mindlore-post-read.cjs +35 -60
  125. package/hooks/mindlore-pre-compact.cjs +55 -73
  126. package/hooks/mindlore-read-guard.cjs +28 -51
  127. package/hooks/mindlore-research-guard.cjs +63 -101
  128. package/hooks/mindlore-search.cjs +1142 -93
  129. package/hooks/mindlore-session-end.cjs +155 -276
  130. package/hooks/mindlore-session-focus.cjs +639 -110
  131. package/hooks/src/lib/constants.cjs +15 -0
  132. package/hooks/src/lib/mindlore-common.cjs +975 -0
  133. package/hooks/src/lib/mindlore-common.d.cts +72 -0
  134. package/hooks/src/lib/secure-io.cjs +17 -0
  135. package/hooks/src/lib/types.d.ts +58 -0
  136. package/hooks/src/mindlore-cwd-changed.cjs +57 -0
  137. package/hooks/src/mindlore-decision-detector.cjs +54 -0
  138. package/hooks/src/mindlore-dont-repeat.cjs +222 -0
  139. package/hooks/src/mindlore-fts5-sync.cjs +98 -0
  140. package/hooks/src/mindlore-index.cjs +230 -0
  141. package/hooks/src/mindlore-model-router.cjs +54 -0
  142. package/hooks/src/mindlore-post-compact.cjs +69 -0
  143. package/hooks/src/mindlore-post-read.cjs +106 -0
  144. package/hooks/src/mindlore-pre-compact.cjs +154 -0
  145. package/hooks/src/mindlore-read-guard.cjs +105 -0
  146. package/hooks/src/mindlore-research-guard.cjs +176 -0
  147. package/hooks/src/mindlore-search.cjs +200 -0
  148. package/hooks/src/mindlore-session-end.cjs +511 -0
  149. package/hooks/src/mindlore-session-focus.cjs +256 -0
  150. package/package.json +8 -3
  151. package/plugin.json +7 -1
  152. package/templates/config.json +1 -1
@@ -0,0 +1,149 @@
1
+ {
2
+ "description": "Mindlore lifecycle hooks — session context injection, FTS5 search, decision detection, file sync, compaction handling, read/write guards, model routing",
3
+ "hooks": {
4
+ "SessionStart": [
5
+ {
6
+ "matcher": "",
7
+ "hooks": [
8
+ {
9
+ "type": "command",
10
+ "command": "node \"${CLAUDE_PLUGIN_ROOT}/hooks/mindlore-session-focus.cjs\""
11
+ }
12
+ ]
13
+ }
14
+ ],
15
+ "UserPromptSubmit": [
16
+ {
17
+ "matcher": "",
18
+ "hooks": [
19
+ {
20
+ "type": "command",
21
+ "command": "node \"${CLAUDE_PLUGIN_ROOT}/hooks/mindlore-search.cjs\""
22
+ }
23
+ ]
24
+ },
25
+ {
26
+ "matcher": "",
27
+ "hooks": [
28
+ {
29
+ "type": "command",
30
+ "command": "node \"${CLAUDE_PLUGIN_ROOT}/hooks/mindlore-decision-detector.cjs\""
31
+ }
32
+ ]
33
+ }
34
+ ],
35
+ "FileChanged": [
36
+ {
37
+ "matcher": "",
38
+ "hooks": [
39
+ {
40
+ "type": "command",
41
+ "command": "node \"${CLAUDE_PLUGIN_ROOT}/hooks/mindlore-index.cjs\""
42
+ }
43
+ ]
44
+ },
45
+ {
46
+ "matcher": "",
47
+ "hooks": [
48
+ {
49
+ "type": "command",
50
+ "command": "node \"${CLAUDE_PLUGIN_ROOT}/hooks/mindlore-fts5-sync.cjs\""
51
+ }
52
+ ]
53
+ }
54
+ ],
55
+ "SessionEnd": [
56
+ {
57
+ "matcher": "",
58
+ "hooks": [
59
+ {
60
+ "type": "command",
61
+ "command": "node \"${CLAUDE_PLUGIN_ROOT}/hooks/mindlore-session-end.cjs\""
62
+ }
63
+ ]
64
+ }
65
+ ],
66
+ "PreCompact": [
67
+ {
68
+ "matcher": "",
69
+ "hooks": [
70
+ {
71
+ "type": "command",
72
+ "command": "node \"${CLAUDE_PLUGIN_ROOT}/hooks/mindlore-pre-compact.cjs\""
73
+ }
74
+ ]
75
+ }
76
+ ],
77
+ "PostCompact": [
78
+ {
79
+ "matcher": "",
80
+ "hooks": [
81
+ {
82
+ "type": "command",
83
+ "command": "node \"${CLAUDE_PLUGIN_ROOT}/hooks/mindlore-post-compact.cjs\""
84
+ }
85
+ ]
86
+ }
87
+ ],
88
+ "PreToolUse": [
89
+ {
90
+ "matcher": "Read",
91
+ "hooks": [
92
+ {
93
+ "type": "command",
94
+ "command": "node \"${CLAUDE_PLUGIN_ROOT}/hooks/mindlore-read-guard.cjs\""
95
+ }
96
+ ]
97
+ },
98
+ {
99
+ "matcher": "Write|Edit",
100
+ "hooks": [
101
+ {
102
+ "type": "command",
103
+ "command": "node \"${CLAUDE_PLUGIN_ROOT}/hooks/mindlore-dont-repeat.cjs\""
104
+ }
105
+ ]
106
+ },
107
+ {
108
+ "matcher": "Agent",
109
+ "hooks": [
110
+ {
111
+ "type": "command",
112
+ "command": "node \"${CLAUDE_PLUGIN_ROOT}/hooks/mindlore-model-router.cjs\""
113
+ }
114
+ ]
115
+ },
116
+ {
117
+ "matcher": "Agent",
118
+ "hooks": [
119
+ {
120
+ "type": "command",
121
+ "command": "node \"${CLAUDE_PLUGIN_ROOT}/hooks/mindlore-research-guard.cjs\""
122
+ }
123
+ ]
124
+ }
125
+ ],
126
+ "PostToolUse": [
127
+ {
128
+ "matcher": "Read",
129
+ "hooks": [
130
+ {
131
+ "type": "command",
132
+ "command": "node \"${CLAUDE_PLUGIN_ROOT}/hooks/mindlore-post-read.cjs\""
133
+ }
134
+ ]
135
+ }
136
+ ],
137
+ "CwdChanged": [
138
+ {
139
+ "matcher": "",
140
+ "hooks": [
141
+ {
142
+ "type": "command",
143
+ "command": "node \"${CLAUDE_PLUGIN_ROOT}/hooks/mindlore-cwd-changed.cjs\""
144
+ }
145
+ ]
146
+ }
147
+ ]
148
+ }
149
+ }
@@ -10,7 +10,7 @@ const path = require('path');
10
10
  const crypto = require('crypto');
11
11
  const os = require('os');
12
12
  const { EPISODE_KINDS, isValidKind, DB_BUSY_TIMEOUT_MS } = require('./constants.cjs');
13
- const { safeMkdir, safeWriteFile } = require('../../dist/scripts/lib/secure-io.js');
13
+ const { safeMkdir, safeWriteFile } = require('./secure-io.cjs');
14
14
 
15
15
  const MINDLORE_DIR = '.mindlore';
16
16
  const DB_NAME = 'mindlore.db';
@@ -644,7 +644,7 @@ function resolveWin32Bin(name) {
644
644
  if (process.platform === 'win32') {
645
645
  try {
646
646
  return require('child_process')
647
- .execSync(`where ${name}`, { encoding: 'utf8', timeout: 3000, windowsHide: true })
647
+ .execFileSync('where', [name], { encoding: 'utf8', timeout: 3000, windowsHide: true })
648
648
  .trim().split('\n')[0].trim();
649
649
  } catch (_e) { /* fall through */ }
650
650
  }
@@ -0,0 +1,17 @@
1
+ 'use strict';
2
+
3
+ const fs = require('fs');
4
+
5
+ function safeMkdir(dirPath) {
6
+ fs.mkdirSync(dirPath, { recursive: true, mode: 0o700 });
7
+ }
8
+
9
+ function safeWriteFile(filePath, data) {
10
+ fs.writeFileSync(filePath, data, { encoding: 'utf8', mode: 0o600 });
11
+ }
12
+
13
+ function safeWriteJson(filePath, obj) {
14
+ safeWriteFile(filePath, JSON.stringify(obj, null, 2) + '\n');
15
+ }
16
+
17
+ module.exports = { safeMkdir, safeWriteFile, safeWriteJson };
@@ -1,57 +1,42 @@
1
1
  #!/usr/bin/env node
2
- 'use strict';
3
-
4
- /**
5
- * mindlore-cwd-changed — CwdChanged hook
6
- *
7
- * Fires when user changes working directory.
8
- * CwdChanged has NO inject to Claude — stdout is swallowed, stderr shown to user.
9
- *
10
- * Side effects:
11
- * 1. Detect scope (global ~/.mindlore/ or none)
12
- * 2. Write scope state to .mindlore/diary/_scope.json for session-focus to read
13
- * 3. Show user-facing message via stderr
14
- */
15
-
16
- const fs = require('fs');
17
- const path = require('path');
18
- const { findMindloreDir, globalDir, hookLog, withTelemetry } = require('./lib/mindlore-common.cjs');
2
+ "use strict";
19
3
 
4
+ // hooks/src/mindlore-cwd-changed.cjs
5
+ var fs = require("fs");
6
+ var path = require("path");
7
+ var { findMindloreDir, globalDir, hookLog, withTelemetry } = require("./lib/mindlore-common.cjs");
20
8
  function main() {
21
9
  const cwd = process.cwd();
22
10
  const activeDir = findMindloreDir();
23
- const scope = !activeDir ? 'none' : activeDir.startsWith(globalDir()) ? 'global' : 'project';
24
-
11
+ const scope = !activeDir ? "none" : activeDir.startsWith(globalDir()) ? "global" : "project";
25
12
  if (activeDir) {
26
- const diaryDir = path.join(activeDir, 'diary');
13
+ const diaryDir = path.join(activeDir, "diary");
27
14
  if (!fs.existsSync(diaryDir)) {
28
15
  fs.mkdirSync(diaryDir, { recursive: true });
29
16
  }
30
-
31
- // Dirty-check: skip write if scope hasn't changed
32
- const scopePath = path.join(diaryDir, '_scope.json');
17
+ const scopePath = path.join(diaryDir, "_scope.json");
33
18
  if (fs.existsSync(scopePath)) {
34
19
  try {
35
- const existing = JSON.parse(fs.readFileSync(scopePath, 'utf8'));
20
+ const existing = JSON.parse(fs.readFileSync(scopePath, "utf8"));
36
21
  if (existing.cwd === cwd && existing.scope === scope) return;
37
22
  } catch (_err) {
38
- // corrupt file — overwrite
39
23
  }
40
24
  }
41
-
42
25
  fs.writeFileSync(scopePath, JSON.stringify({
43
26
  scope,
44
27
  dir: activeDir,
45
28
  cwd,
46
- timestamp: new Date().toISOString(),
47
- }, null, 2), 'utf8');
29
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
30
+ }, null, 2), "utf8");
48
31
  }
49
-
50
- if (scope === 'none') {
51
- process.stderr.write(`[Mindlore] Bu projede mindlore kurulu degil. npx mindlore init calistirin.\n`);
32
+ if (scope === "none") {
33
+ process.stderr.write(`[Mindlore] Bu projede mindlore kurulu degil. npx mindlore init calistirin.
34
+ `);
52
35
  } else {
53
- process.stderr.write(`[Mindlore scope: ${scope}] ${activeDir}\n`);
36
+ process.stderr.write(`[Mindlore scope: ${scope}] ${activeDir}
37
+ `);
54
38
  }
55
39
  }
56
-
57
- withTelemetry('mindlore-cwd-changed', main).catch(err => { hookLog('cwd-changed', 'error', err?.message ?? String(err)); });
40
+ withTelemetry("mindlore-cwd-changed", main).catch((err) => {
41
+ hookLog("cwd-changed", "error", err?.message ?? String(err));
42
+ });
@@ -1,29 +1,41 @@
1
1
  #!/usr/bin/env node
2
- 'use strict';
3
-
4
- /**
5
- * mindlore-decision-detector UserPromptSubmit hook
6
- *
7
- * Detects decision signals in user messages (TR + EN).
8
- * Outputs a suggestion to record the decision via /mindlore-decide.
9
- * Does NOT block (exit 0) — advisory only.
10
- */
11
-
12
- const { findMindloreDir, readHookStdin, hookLog, withTelemetry } = require('./lib/mindlore-common.cjs');
13
-
14
- const SIGNALS_TR = [
15
- 'karar verdik', 'karar verildi', 'kararlastirdik', 'kararlaştırdık',
16
- 'şunu seçtik', 'sunu sectik', 'bunu yapmayalım', 'bunu yapmayalim',
17
- 'yerine', 'tercih ettik', 'onaylandi', 'onaylandı', 'kesinleşti', 'kesinlesti',
18
- 'vazgeçtik', 'vazgectik', 'iptal ettik',
2
+ "use strict";
3
+
4
+ // hooks/src/mindlore-decision-detector.cjs
5
+ var { findMindloreDir, readHookStdin, hookLog, withTelemetry } = require("./lib/mindlore-common.cjs");
6
+ var SIGNALS_TR = [
7
+ "karar verdik",
8
+ "karar verildi",
9
+ "kararlastirdik",
10
+ "kararla\u015Ft\u0131rd\u0131k",
11
+ "\u015Funu se\xE7tik",
12
+ "sunu sectik",
13
+ "bunu yapmayal\u0131m",
14
+ "bunu yapmayalim",
15
+ "yerine",
16
+ "tercih ettik",
17
+ "onaylandi",
18
+ "onayland\u0131",
19
+ "kesinle\u015Fti",
20
+ "kesinlesti",
21
+ "vazge\xE7tik",
22
+ "vazgectik",
23
+ "iptal ettik"
19
24
  ];
20
-
21
- const SIGNALS_EN = [
22
- 'decided', 'decision made', "let's go with", 'lets go with',
23
- "we'll use", 'well use', 'approved', 'settled on',
24
- 'going with', 'chosen', 'finalized', 'rejected',
25
+ var SIGNALS_EN = [
26
+ "decided",
27
+ "decision made",
28
+ "let's go with",
29
+ "lets go with",
30
+ "we'll use",
31
+ "well use",
32
+ "approved",
33
+ "settled on",
34
+ "going with",
35
+ "chosen",
36
+ "finalized",
37
+ "rejected"
25
38
  ];
26
-
27
39
  function detectDecision(text) {
28
40
  const lower = text.toLowerCase();
29
41
  for (const signal of SIGNALS_TR) {
@@ -34,21 +46,18 @@ function detectDecision(text) {
34
46
  }
35
47
  return null;
36
48
  }
37
-
38
49
  function main() {
39
50
  const baseDir = findMindloreDir();
40
51
  if (!baseDir) return;
41
-
42
- const userText = readHookStdin(['prompt', 'content', 'message']);
52
+ const userText = readHookStdin(["prompt", "content", "message"]);
43
53
  if (!userText || userText.length < 10) return;
44
-
45
54
  const signal = detectDecision(userText);
46
55
  if (signal) {
47
- process.stdout.write(`[Mindlore: Karar sinyali tespit edildi ("${signal}") /mindlore-decide record ile kaydetmek ister misin?]\n`);
56
+ process.stdout.write(`[Mindlore: Karar sinyali tespit edildi ("${signal}") \u2014 /mindlore-decide record ile kaydetmek ister misin?]
57
+ `);
48
58
  }
49
59
  }
50
-
51
- withTelemetry('mindlore-decision-detector', main).catch(err => {
52
- hookLog('mindlore-decision-detector', 'error', err?.message ?? String(err));
60
+ withTelemetry("mindlore-decision-detector", main).catch((err) => {
61
+ hookLog("mindlore-decision-detector", "error", err?.message ?? String(err));
53
62
  process.exit(0);
54
63
  });
@@ -1,58 +1,34 @@
1
1
  #!/usr/bin/env node
2
- 'use strict';
3
-
4
- /**
5
- * mindlore-dont-repeat — PreToolUse hook (matcher: "Write|Edit")
6
- *
7
- * Checks code being written against negative rules (DON'T, NEVER, AVOID, YAPMA, etc.)
8
- * found in LESSONS files and Mindlore learnings/.
9
- *
10
- * Sources checked (in order):
11
- * 1. ~/.claude/lessons/global.md (global rules)
12
- * 2. ./LESSONS.md (project-level rules, if exists)
13
- * 3. .mindlore/learnings/*.md (Mindlore learnings, if exists)
14
- *
15
- * Advisory only (exit 0) — does not block, injects additionalContext warning.
16
- */
17
-
18
- const fs = require('fs');
19
- const path = require('path');
20
- const os = require('os');
21
- const { findMindloreDir, getProjectName, hookLog, withTelemetrySync } = require('./lib/mindlore-common.cjs');
22
-
23
- /**
24
- * File-persisted pattern cache — survives across process invocations.
25
- * Cache file: .mindlore/diary/_pattern-cache.json
26
- * Each entry keyed by source file path, stores mtimeMs + extracted patterns.
27
- * On hit: stat only, no readFile+parse. On miss: read, parse, update cache.
28
- */
29
-
30
- let cacheDirty = false;
31
-
2
+ "use strict";
3
+
4
+ // hooks/src/mindlore-dont-repeat.cjs
5
+ var fs = require("fs");
6
+ var path = require("path");
7
+ var os = require("os");
8
+ var { findMindloreDir, getProjectName, hookLog, withTelemetrySync } = require("./lib/mindlore-common.cjs");
9
+ var cacheDirty = false;
32
10
  function readCache(cachePath) {
33
11
  if (!cachePath) return {};
34
12
  try {
35
- return JSON.parse(fs.readFileSync(cachePath, 'utf8'));
13
+ return JSON.parse(fs.readFileSync(cachePath, "utf8"));
36
14
  } catch (_err) {
37
15
  return {};
38
16
  }
39
17
  }
40
-
41
18
  function writeCache(cachePath, cache) {
42
19
  if (!cachePath || !cacheDirty) return;
43
20
  try {
44
- fs.writeFileSync(cachePath, JSON.stringify(cache), 'utf8');
45
- } catch (_err) { /* write failure is non-fatal */ }
21
+ fs.writeFileSync(cachePath, JSON.stringify(cache), "utf8");
22
+ } catch (_err) {
23
+ }
46
24
  }
47
-
48
25
  function loadPatterns(filePath, cache) {
49
26
  try {
50
27
  const stat = fs.statSync(filePath);
51
28
  const mtimeMs = stat.mtimeMs;
52
29
  const cached = cache[filePath];
53
30
  if (cached && cached.mtimeMs === mtimeMs) return cached.patterns;
54
-
55
- const patterns = extractNegativePatterns(fs.readFileSync(filePath, 'utf8'));
31
+ const patterns = extractNegativePatterns(fs.readFileSync(filePath, "utf8"));
56
32
  cache[filePath] = { mtimeMs, patterns };
57
33
  cacheDirty = true;
58
34
  return patterns;
@@ -60,42 +36,30 @@ function loadPatterns(filePath, cache) {
60
36
  return [];
61
37
  }
62
38
  }
63
-
64
39
  function extractNegativePatterns(content) {
65
40
  const patterns = [];
66
- const lines = content.split('\n');
67
-
41
+ const lines = content.split("\n");
68
42
  for (const line of lines) {
69
43
  const trimmed = line.trim();
70
- // Multi-language: TR (YAPMA, KRITIK) + EN (DON'T, NEVER, AVOID, DO NOT)
71
44
  const isNegativeRule = /^-\s*(YAPMA|KRITIK|DON'?T|NEVER|AVOID|DO NOT):/i.test(trimmed);
72
45
  if (!isNegativeRule) continue;
73
-
74
- // Extract backtick-quoted code patterns: `pattern`
75
46
  const backtickMatches = trimmed.match(/`([^`]+)`/g);
76
47
  if (backtickMatches) {
77
48
  for (const match of backtickMatches) {
78
49
  const pattern = match.slice(1, -1).trim();
79
- // Skip short/generic patterns — too many false positives
80
50
  if (pattern.length < 8) continue;
81
51
  if (/^[^a-zA-Z0-9]+$/.test(pattern)) continue;
82
- // Skip single words (too generic: "node", "bash", "any")
83
52
  if (/^\w+$/.test(pattern) && pattern.length < 12) continue;
84
- // Skip file extensions and paths
85
53
  if (/^\.\w{1,5}$/.test(pattern)) continue;
86
- if (pattern.startsWith('/') || pattern.startsWith('~')) continue;
87
- if (pattern.includes('.md') || pattern.includes('.json')) continue;
88
- // Skip common false-positive patterns
54
+ if (pattern.startsWith("/") || pattern.startsWith("~")) continue;
55
+ if (pattern.includes(".md") || pattern.includes(".json")) continue;
89
56
  if (/^(node|bash|npm|git|process|require|import|export|const|let|var)$/i.test(pattern)) continue;
90
-
91
57
  patterns.push({
92
58
  pattern,
93
- rule: trimmed.substring(0, 120),
59
+ rule: trimmed.substring(0, 120)
94
60
  });
95
61
  }
96
62
  }
97
-
98
- // Extract "quoted strings" as patterns
99
63
  const quoteMatches = trimmed.match(/"([^"]+)"/g);
100
64
  if (quoteMatches) {
101
65
  for (const match of quoteMatches) {
@@ -103,120 +67,98 @@ function extractNegativePatterns(content) {
103
67
  if (quoted.length < 4) continue;
104
68
  patterns.push({
105
69
  pattern: quoted,
106
- rule: trimmed.substring(0, 120),
70
+ rule: trimmed.substring(0, 120)
107
71
  });
108
72
  }
109
73
  }
110
74
  }
111
-
112
75
  return patterns;
113
76
  }
114
-
115
77
  function checkContent(content, patterns) {
116
78
  const matches = [];
117
79
  for (const p of patterns) {
118
80
  try {
119
- const escaped = p.pattern.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
120
- const regex = new RegExp(escaped, 'i');
81
+ const escaped = p.pattern.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
82
+ const regex = new RegExp(escaped, "i");
121
83
  if (regex.test(content)) {
122
84
  matches.push(p);
123
85
  }
124
- } catch { /* skip invalid patterns */ }
86
+ } catch {
87
+ }
125
88
  }
126
89
  return matches;
127
90
  }
128
-
129
91
  function main() {
130
- let input = '';
131
- const stdinTimeout = setTimeout(() => process.exit(0), 3000);
132
- process.stdin.setEncoding('utf8');
133
- process.stdin.on('error', () => process.exit(0));
134
- process.stdin.on('data', chunk => input += chunk);
135
- process.stdin.on('end', () => {
92
+ let input = "";
93
+ const stdinTimeout = setTimeout(() => process.exit(0), 3e3);
94
+ process.stdin.setEncoding("utf8");
95
+ process.stdin.on("error", () => process.exit(0));
96
+ process.stdin.on("data", (chunk) => input += chunk);
97
+ process.stdin.on("end", () => {
136
98
  clearTimeout(stdinTimeout);
137
99
  try {
138
- const data = JSON.parse(input || '{}');
139
- const toolName = data.tool_name || '';
140
-
141
- if (!['Write', 'Edit'].includes(toolName)) {
100
+ const data = JSON.parse(input || "{}");
101
+ const toolName = data.tool_name || "";
102
+ if (!["Write", "Edit"].includes(toolName)) {
142
103
  return process.exit(0);
143
104
  }
144
-
145
105
  const toolInput = data.tool_input || {};
146
- const filePath = toolInput.file_path || '';
147
-
148
- // Skip non-code files
106
+ const filePath = toolInput.file_path || "";
149
107
  if (!filePath) return process.exit(0);
150
108
  const ext = path.extname(filePath).toLowerCase();
151
- const codeExts = ['.ts', '.tsx', '.js', '.jsx', '.cjs', '.mjs', '.py', '.go', '.rs', '.java', '.c', '.cpp', '.h', '.sh', '.yaml', '.yml'];
109
+ const codeExts = [".ts", ".tsx", ".js", ".jsx", ".cjs", ".mjs", ".py", ".go", ".rs", ".java", ".c", ".cpp", ".h", ".sh", ".yaml", ".yml"];
152
110
  if (!codeExts.includes(ext)) return process.exit(0);
153
-
154
- // Skip rule files themselves
155
111
  const basename = path.basename(filePath);
156
- if (basename === 'LESSONS.md' || basename === 'global.md' || basename === 'CLAUDE.md') {
112
+ if (basename === "LESSONS.md" || basename === "global.md" || basename === "CLAUDE.md") {
157
113
  return process.exit(0);
158
114
  }
159
-
160
- // Collect content being written (skip old_string — that's code being removed, not added)
161
115
  const allContent = [
162
- toolInput.content || '',
163
- toolInput.new_string || '',
164
- ].join('\n');
165
-
116
+ toolInput.content || "",
117
+ toolInput.new_string || ""
118
+ ].join("\n");
166
119
  if (allContent.trim().length < 10) return process.exit(0);
167
-
168
- // Load patterns from all sources (file-persisted mtime cache)
169
120
  const mindloreDir = findMindloreDir();
170
- const cachePath = mindloreDir ? path.join(mindloreDir, 'diary', `_pattern-cache-${getProjectName()}.json`) : null;
121
+ const cachePath = mindloreDir ? path.join(mindloreDir, "diary", `_pattern-cache-${getProjectName()}.json`) : null;
171
122
  const cache = readCache(cachePath);
172
123
  const allPatterns = [];
173
124
  const cwd = process.cwd();
174
-
175
- // 1. Global lessons
176
- allPatterns.push(...loadPatterns(path.join(os.homedir(), '.claude', 'lessons', 'global.md'), cache));
177
-
178
- // 2. Project LESSONS.md
179
- allPatterns.push(...loadPatterns(path.join(cwd, 'LESSONS.md'), cache));
180
-
181
- // 3. Mindlore learnings/ directory
125
+ allPatterns.push(...loadPatterns(path.join(os.homedir(), ".claude", "lessons", "global.md"), cache));
126
+ allPatterns.push(...loadPatterns(path.join(cwd, "LESSONS.md"), cache));
182
127
  if (mindloreDir) {
183
- const learningsDir = path.join(mindloreDir, 'learnings');
128
+ const learningsDir = path.join(mindloreDir, "learnings");
184
129
  try {
185
- const files = fs.readdirSync(learningsDir).filter(f => f.endsWith('.md'));
130
+ const files = fs.readdirSync(learningsDir).filter((f) => f.endsWith(".md"));
186
131
  for (const file of files) {
187
132
  allPatterns.push(...loadPatterns(path.join(learningsDir, file), cache));
188
133
  }
189
- } catch (_err) { /* learnings/ doesn't exist yet */ }
134
+ } catch (_err) {
135
+ }
190
136
  }
191
-
192
137
  writeCache(cachePath, cache);
193
-
194
138
  if (allPatterns.length === 0) return process.exit(0);
195
-
196
- // Check content against patterns
197
139
  const matches = checkContent(allContent, allPatterns);
198
140
  if (matches.length === 0) return process.exit(0);
199
-
200
- // Build warning — max 3 matches shown
201
141
  const shown = matches.slice(0, 3);
202
- const warning = shown.map(m =>
203
- ` - Pattern: \`${m.pattern}\` ${m.rule}`
204
- ).join('\n');
205
- const extra = matches.length > 3 ? `\n ... and ${matches.length - 3} more` : '';
206
-
207
- const msg = `[Mindlore: ${matches.length} dont-repeat rule violation detected]\n${warning}${extra}`;
208
-
142
+ const warning = shown.map(
143
+ (m) => ` - Pattern: \`${m.pattern}\` \u2192 ${m.rule}`
144
+ ).join("\n");
145
+ const extra = matches.length > 3 ? `
146
+ ... and ${matches.length - 3} more` : "";
147
+ const msg = `[Mindlore: ${matches.length} dont-repeat rule violation detected]
148
+ ${warning}${extra}`;
209
149
  process.stdout.write(JSON.stringify({
210
150
  hookSpecificOutput: {
211
- hookEventName: 'PreToolUse',
151
+ hookEventName: "PreToolUse",
212
152
  additionalContext: msg
213
153
  }
214
154
  }));
215
155
  } catch {
216
- // Silent fail
217
156
  }
218
157
  process.exit(0);
219
158
  });
220
159
  }
221
-
222
- try { withTelemetrySync('mindlore-dont-repeat', main); } catch (err) { hookLog('dont-repeat', 'error', err?.message ?? String(err)); }
160
+ try {
161
+ withTelemetrySync("mindlore-dont-repeat", main);
162
+ } catch (err) {
163
+ hookLog("dont-repeat", "error", err?.message ?? String(err));
164
+ }