brain-cache 0.4.2 → 3.0.0

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 (31) hide show
  1. package/.claude/skills/brain-cache/SKILL.md +52 -0
  2. package/README.md +49 -100
  3. package/dist/{askCodebase-BZIXS3EV.js → askCodebase-EE32B7BP.js} +9 -9
  4. package/dist/buildContext-GWVDAYH6.js +14 -0
  5. package/dist/{chunk-Y7BU7IYX.js → chunk-3HQRTLBH.js} +70 -6
  6. package/dist/{chunk-ZKVZTDND.js → chunk-4IOR54GU.js} +2 -1
  7. package/dist/chunk-6C2OYMKD.js +16 -0
  8. package/dist/{workflows-KYCBR7TC.js → chunk-CY34XQ2O.js} +115 -24
  9. package/dist/chunk-DFFMV3RR.js +171 -0
  10. package/dist/{chunk-PJQNHMQH.js → chunk-DPH5X5HL.js} +1 -1
  11. package/dist/{chunk-FQL4HV4R.js → chunk-HRJ3OT6Q.js} +1 -1
  12. package/dist/chunk-KMRPAVMM.js +967 -0
  13. package/dist/{chunk-KQZSBRRH.js → chunk-RKPICQU7.js} +1 -1
  14. package/dist/{chunk-EEC7KYPY.js → chunk-TXLCXXKY.js} +7 -8
  15. package/dist/claude-md-section-K47HUTE4.js +38 -0
  16. package/dist/cli.js +13 -9
  17. package/dist/{doctor-KRNLXE4R.js → doctor-FCET2MNJ.js} +3 -3
  18. package/dist/{embedder-ZLHAZZUI.js → embedder-HVEXDJAU.js} +2 -2
  19. package/dist/{init-QNN5H3DR.js → init-2E4JMZZC.js} +71 -6
  20. package/dist/mcp.js +1450 -130
  21. package/dist/{search-O4CFAH45.js → search-7ISZ7EXI.js} +16 -15
  22. package/dist/{status-7MT4IROA.js → status-VKTSG2SN.js} +3 -3
  23. package/dist/statusline-script-NFUDFOWK.js +95 -0
  24. package/dist/watch-QPMAB62P.js +128 -0
  25. package/dist/workflows-MWEY7OAI.js +14 -0
  26. package/package.json +5 -1
  27. package/dist/buildContext-APWOPZMJ.js +0 -14
  28. package/dist/chunk-JZQWPHAQ.js +0 -103
  29. package/dist/chunk-SBSMKI4B.js +0 -109
  30. package/dist/chunk-ZGYLHFHJ.js +0 -17
  31. package/dist/claude-md-section-6ZJ3TMO4.js +0 -34
@@ -1,24 +1,24 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  RETRIEVAL_STRATEGIES,
4
- classifyQueryIntent,
4
+ classifyRetrievalMode,
5
5
  deduplicateChunks,
6
6
  searchChunks
7
- } from "./chunk-SBSMKI4B.js";
7
+ } from "./chunk-DFFMV3RR.js";
8
+ import {
9
+ openDatabase,
10
+ readIndexState
11
+ } from "./chunk-3HQRTLBH.js";
8
12
  import {
9
13
  embedBatchWithRetry
10
- } from "./chunk-KQZSBRRH.js";
14
+ } from "./chunk-RKPICQU7.js";
11
15
  import {
12
16
  isOllamaRunning
13
- } from "./chunk-FQL4HV4R.js";
14
- import {
15
- openDatabase,
16
- readIndexState
17
- } from "./chunk-Y7BU7IYX.js";
17
+ } from "./chunk-HRJ3OT6Q.js";
18
18
  import {
19
19
  readProfile
20
- } from "./chunk-PJQNHMQH.js";
21
- import "./chunk-EEC7KYPY.js";
20
+ } from "./chunk-DPH5X5HL.js";
21
+ import "./chunk-TXLCXXKY.js";
22
22
 
23
23
  // src/workflows/search.ts
24
24
  import { resolve } from "path";
@@ -52,18 +52,19 @@ async function runSearch(query, opts) {
52
52
  `Index is empty at ${rootDir}. No source files were indexed.`
53
53
  );
54
54
  }
55
- const intent = classifyQueryIntent(query);
55
+ const mode = classifyRetrievalMode(query);
56
56
  const strategy = {
57
- limit: opts?.limit ?? RETRIEVAL_STRATEGIES[intent].limit,
58
- distanceThreshold: RETRIEVAL_STRATEGIES[intent].distanceThreshold
57
+ limit: opts?.limit ?? RETRIEVAL_STRATEGIES[mode].limit,
58
+ distanceThreshold: RETRIEVAL_STRATEGIES[mode].distanceThreshold,
59
+ keywordBoostWeight: RETRIEVAL_STRATEGIES[mode].keywordBoostWeight
59
60
  };
60
61
  process.stderr.write(
61
- `brain-cache: searching (intent=${intent}, limit=${strategy.limit})
62
+ `brain-cache: searching (mode=${mode}, limit=${strategy.limit})
62
63
  `
63
64
  );
64
65
  const { embeddings: vectors } = await embedBatchWithRetry(indexState.embeddingModel, [query]);
65
66
  const queryVector = vectors[0];
66
- const results = await searchChunks(table, queryVector, strategy);
67
+ const results = await searchChunks(table, queryVector, strategy, query);
67
68
  const deduped = deduplicateChunks(results);
68
69
  process.stderr.write(
69
70
  `brain-cache: found ${deduped.length} chunks (${results.length} before dedup)
@@ -1,11 +1,11 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  readIndexState
4
- } from "./chunk-Y7BU7IYX.js";
4
+ } from "./chunk-3HQRTLBH.js";
5
5
  import {
6
6
  readProfile
7
- } from "./chunk-PJQNHMQH.js";
8
- import "./chunk-EEC7KYPY.js";
7
+ } from "./chunk-DPH5X5HL.js";
8
+ import "./chunk-TXLCXXKY.js";
9
9
 
10
10
  // src/workflows/status.ts
11
11
  import { resolve } from "path";
@@ -0,0 +1,95 @@
1
+ #!/usr/bin/env node
2
+
3
+ // src/lib/statusline-script.ts
4
+ var STATUSLINE_SCRIPT_CONTENT = `#!/usr/bin/env node
5
+ import { readFileSync } from 'node:fs';
6
+ import { join } from 'node:path';
7
+ import { homedir } from 'node:os';
8
+
9
+ const STATS_PATH = join(homedir(), '.brain-cache', 'session-stats.json');
10
+ const STATS_TTL_MS = 2 * 60 * 60 * 1000; // 2 hours \u2014 must match sessionStats.ts STATS_TTL_MS
11
+ export const IDLE_OUTPUT = 'brain-cache idle\\n';
12
+
13
+ /**
14
+ * Formats a token count into a human-readable string.
15
+ * - < 1000: plain number (e.g. "500")
16
+ * - >= 1000 and < 1,000,000: rounded k suffix (e.g. "2k")
17
+ * - >= 1,000,000: M suffix with one decimal (e.g. "1.5M")
18
+ *
19
+ * @param {number} n
20
+ * @returns {string}
21
+ */
22
+ export function formatTokenCount(n) {
23
+ if (n >= 1_000_000) return \`\${(n / 1_000_000).toFixed(1)}M\`;
24
+ if (n >= 1_000) return \`\${Math.round(n / 1_000)}k\`;
25
+ return String(n);
26
+ }
27
+
28
+ /**
29
+ * Reads and validates stats from a given file path.
30
+ * Returns null if the file does not exist, is malformed, has no lastUpdatedAt,
31
+ * is older than STATS_TTL_MS, or has estimatedWithoutBraincache <= 0.
32
+ *
33
+ * @param {string} filePath
34
+ * @returns {import('../services/sessionStats.js').SessionStats | null}
35
+ */
36
+ export function _readStatsFromPath(filePath) {
37
+ try {
38
+ const raw = readFileSync(filePath, 'utf-8');
39
+ const stats = JSON.parse(raw);
40
+ if (!stats.lastUpdatedAt) return null;
41
+ const age = Date.now() - Date.parse(stats.lastUpdatedAt);
42
+ if (age > STATS_TTL_MS) return null;
43
+ if (!stats.estimatedWithoutBraincache || stats.estimatedWithoutBraincache <= 0) return null;
44
+ return stats;
45
+ } catch {
46
+ return null;
47
+ }
48
+ }
49
+
50
+ /**
51
+ * Reads session stats from the default stats path (~/.brain-cache/session-stats.json).
52
+ * Returns null if the file does not exist, is invalid, or is expired.
53
+ *
54
+ * @returns {import('../services/sessionStats.js').SessionStats | null}
55
+ */
56
+ export function readStats() {
57
+ return _readStatsFromPath(STATS_PATH);
58
+ }
59
+
60
+ /**
61
+ * Renders the status line output string.
62
+ * Returns IDLE_OUTPUT when stats is null, savings <= 0, or pct <= 0.
63
+ * Otherwise returns a formatted savings string.
64
+ *
65
+ * @param {import('../services/sessionStats.js').SessionStats | null} stats
66
+ * @returns {string}
67
+ */
68
+ export function renderOutput(stats) {
69
+ if (!stats) return IDLE_OUTPUT;
70
+ const saved = stats.estimatedWithoutBraincache - stats.tokensSent;
71
+ const pct = Math.round((1 - stats.tokensSent / stats.estimatedWithoutBraincache) * 100);
72
+ if (pct <= 0 || saved <= 0) return IDLE_OUTPUT;
73
+ return \`brain-cache \\u2193\${pct}% \${formatTokenCount(saved)} saved\\n\`;
74
+ }
75
+
76
+ // Stdin/stdout protocol \u2014 only when executed directly (not imported for testing)
77
+ if (import.meta.url === \`file://\${process.argv[1]}\`) {
78
+ const stdinTimeout = setTimeout(() => process.exit(0), 3000);
79
+ let input = '';
80
+ process.stdin.setEncoding('utf8');
81
+ process.stdin.on('data', chunk => { input += chunk; });
82
+ process.stdin.on('end', () => {
83
+ clearTimeout(stdinTimeout);
84
+ try {
85
+ const stats = readStats();
86
+ process.stdout.write(renderOutput(stats));
87
+ } catch {
88
+ process.stdout.write(IDLE_OUTPUT);
89
+ }
90
+ });
91
+ }
92
+ `;
93
+ export {
94
+ STATUSLINE_SCRIPT_CONTENT
95
+ };
@@ -0,0 +1,128 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ ALWAYS_EXCLUDE_GLOBS,
4
+ loadIgnorePatterns,
5
+ runIndex
6
+ } from "./chunk-CY34XQ2O.js";
7
+ import "./chunk-4IOR54GU.js";
8
+ import "./chunk-3HQRTLBH.js";
9
+ import "./chunk-6C2OYMKD.js";
10
+ import "./chunk-RKPICQU7.js";
11
+ import "./chunk-HRJ3OT6Q.js";
12
+ import "./chunk-DPH5X5HL.js";
13
+ import "./chunk-TXLCXXKY.js";
14
+
15
+ // src/workflows/watch.ts
16
+ import { resolve, join } from "path";
17
+ import { open, unlink, mkdir } from "fs/promises";
18
+
19
+ // src/services/fileWatcher.ts
20
+ import { watch } from "chokidar";
21
+ import { relative } from "path";
22
+ import ignore from "ignore";
23
+ async function createWatcher(projectRoot) {
24
+ const userPatterns = await loadIgnorePatterns(projectRoot);
25
+ const ig = ignore();
26
+ ig.add(userPatterns);
27
+ ig.add(ALWAYS_EXCLUDE_GLOBS);
28
+ const ignored = (filePath) => {
29
+ if (filePath.includes("/.brain-cache/") || filePath.endsWith("/.brain-cache")) {
30
+ return true;
31
+ }
32
+ const rel = relative(projectRoot, filePath);
33
+ if (!rel || rel.startsWith("..")) {
34
+ return false;
35
+ }
36
+ return ig.ignores(rel);
37
+ };
38
+ return watch(projectRoot, {
39
+ persistent: true,
40
+ ignoreInitial: true,
41
+ ignored
42
+ });
43
+ }
44
+
45
+ // src/workflows/watch.ts
46
+ var debounceTimer = null;
47
+ var pendingFiles = /* @__PURE__ */ new Set();
48
+ function resetState() {
49
+ if (debounceTimer !== null) {
50
+ clearTimeout(debounceTimer);
51
+ debounceTimer = null;
52
+ }
53
+ pendingFiles.clear();
54
+ }
55
+ async function acquireIndexLock(projectRoot) {
56
+ const lockPath = join(projectRoot, ".brain-cache", ".indexing");
57
+ await mkdir(join(projectRoot, ".brain-cache"), { recursive: true });
58
+ try {
59
+ const handle = await open(lockPath, "wx");
60
+ await handle.close();
61
+ return true;
62
+ } catch {
63
+ return false;
64
+ }
65
+ }
66
+ async function releaseIndexLock(projectRoot) {
67
+ await unlink(join(projectRoot, ".brain-cache", ".indexing")).catch(() => void 0);
68
+ }
69
+ function scheduleReindex(filePath, eventType, projectRoot) {
70
+ process.stderr.write(`brain-cache: ${eventType} ${filePath}
71
+ `);
72
+ pendingFiles.add(filePath);
73
+ if (debounceTimer !== null) {
74
+ clearTimeout(debounceTimer);
75
+ }
76
+ debounceTimer = setTimeout(async () => {
77
+ debounceTimer = null;
78
+ pendingFiles.clear();
79
+ const locked = await acquireIndexLock(projectRoot);
80
+ if (!locked) {
81
+ process.stderr.write("brain-cache: skipping reindex \u2014 another index process is running\n");
82
+ return;
83
+ }
84
+ try {
85
+ await runIndex(projectRoot);
86
+ } finally {
87
+ await releaseIndexLock(projectRoot);
88
+ }
89
+ }, 500);
90
+ }
91
+ async function runWatch(targetPath) {
92
+ const rootDir = resolve(targetPath ?? ".");
93
+ process.stderr.write(`brain-cache: watching ${rootDir}
94
+ `);
95
+ const watcher = await createWatcher(rootDir);
96
+ watcher.on("add", (fp) => scheduleReindex(fp, "add", rootDir));
97
+ watcher.on("change", (fp) => scheduleReindex(fp, "change", rootDir));
98
+ watcher.on("unlink", (fp) => scheduleReindex(fp, "unlink", rootDir));
99
+ watcher.on("addDir", (fp) => scheduleReindex(fp, "addDir", rootDir));
100
+ watcher.on("unlinkDir", (fp) => scheduleReindex(fp, "unlinkDir", rootDir));
101
+ watcher.on("error", (err) => {
102
+ process.stderr.write(`brain-cache: watcher error: ${String(err)}
103
+ `);
104
+ });
105
+ watcher.on("ready", () => {
106
+ process.stderr.write("brain-cache: watcher ready\n");
107
+ });
108
+ const cleanup = async () => {
109
+ if (debounceTimer !== null) {
110
+ clearTimeout(debounceTimer);
111
+ debounceTimer = null;
112
+ }
113
+ pendingFiles.clear();
114
+ await watcher.close();
115
+ process.exit(0);
116
+ };
117
+ process.once("SIGINT", cleanup);
118
+ process.once("SIGTERM", cleanup);
119
+ await new Promise(() => {
120
+ });
121
+ }
122
+ export {
123
+ acquireIndexLock,
124
+ releaseIndexLock,
125
+ resetState,
126
+ runWatch,
127
+ scheduleReindex
128
+ };
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ runIndex
4
+ } from "./chunk-CY34XQ2O.js";
5
+ import "./chunk-4IOR54GU.js";
6
+ import "./chunk-3HQRTLBH.js";
7
+ import "./chunk-6C2OYMKD.js";
8
+ import "./chunk-RKPICQU7.js";
9
+ import "./chunk-HRJ3OT6Q.js";
10
+ import "./chunk-DPH5X5HL.js";
11
+ import "./chunk-TXLCXXKY.js";
12
+ export {
13
+ runIndex
14
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "brain-cache",
3
- "version": "0.4.2",
3
+ "version": "3.0.0",
4
4
  "description": "Local MCP-first context engine for Claude. Index your codebase, retrieve only what matters, and cut token usage.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -9,6 +9,7 @@
9
9
  },
10
10
  "files": [
11
11
  "dist/",
12
+ ".claude/skills/",
12
13
  "README.md",
13
14
  "LICENSE"
14
15
  ],
@@ -37,7 +38,9 @@
37
38
  "@lancedb/lancedb": "^0.27.1",
38
39
  "@modelcontextprotocol/sdk": "^1.29.0",
39
40
  "apache-arrow": "^18.1.0",
41
+ "chokidar": "^5.0.0",
40
42
  "commander": "14.0.3",
43
+ "dedent": "^1.7.2",
41
44
  "fast-glob": "^3.3.3",
42
45
  "ignore": "^7.0.5",
43
46
  "ollama": "^0.6.3",
@@ -50,6 +53,7 @@
50
53
  "zod": "^4.3.6"
51
54
  },
52
55
  "devDependencies": {
56
+ "@types/dedent": "^0.7.2",
53
57
  "@types/node": "^22.0.0",
54
58
  "pino-pretty": "^11.0.0",
55
59
  "tsup": "^8.0.0",
@@ -1,14 +0,0 @@
1
- #!/usr/bin/env node
2
- import {
3
- runBuildContext
4
- } from "./chunk-JZQWPHAQ.js";
5
- import "./chunk-ZKVZTDND.js";
6
- import "./chunk-SBSMKI4B.js";
7
- import "./chunk-KQZSBRRH.js";
8
- import "./chunk-FQL4HV4R.js";
9
- import "./chunk-Y7BU7IYX.js";
10
- import "./chunk-PJQNHMQH.js";
11
- import "./chunk-EEC7KYPY.js";
12
- export {
13
- runBuildContext
14
- };
@@ -1,103 +0,0 @@
1
- #!/usr/bin/env node
2
- import {
3
- assembleContext,
4
- countChunkTokens
5
- } from "./chunk-ZKVZTDND.js";
6
- import {
7
- RETRIEVAL_STRATEGIES,
8
- classifyQueryIntent,
9
- deduplicateChunks,
10
- searchChunks
11
- } from "./chunk-SBSMKI4B.js";
12
- import {
13
- embedBatchWithRetry
14
- } from "./chunk-KQZSBRRH.js";
15
- import {
16
- isOllamaRunning
17
- } from "./chunk-FQL4HV4R.js";
18
- import {
19
- openDatabase,
20
- readIndexState
21
- } from "./chunk-Y7BU7IYX.js";
22
- import {
23
- readProfile
24
- } from "./chunk-PJQNHMQH.js";
25
- import {
26
- DEFAULT_TOKEN_BUDGET,
27
- TOOL_CALL_OVERHEAD_TOKENS
28
- } from "./chunk-EEC7KYPY.js";
29
-
30
- // src/workflows/buildContext.ts
31
- import { readFile } from "fs/promises";
32
- import { resolve } from "path";
33
- async function runBuildContext(query, opts) {
34
- const profile = await readProfile();
35
- if (profile === null) {
36
- throw new Error("No profile found. Run 'brain-cache init' first.");
37
- }
38
- const running = await isOllamaRunning();
39
- if (!running) {
40
- throw new Error("Ollama is not running. Start it with 'ollama serve' or run 'brain-cache init'.");
41
- }
42
- const rootDir = resolve(opts?.path ?? ".");
43
- const indexState = await readIndexState(rootDir);
44
- if (indexState === null) {
45
- throw new Error(`No index found at ${rootDir}. Run 'brain-cache index' first.`);
46
- }
47
- const db = await openDatabase(rootDir);
48
- const tableNames = await db.tableNames();
49
- if (!tableNames.includes("chunks")) {
50
- throw new Error("No chunks table found. Run 'brain-cache index' first.");
51
- }
52
- const table = await db.openTable("chunks");
53
- const intent = classifyQueryIntent(query);
54
- const strategy = {
55
- limit: opts?.limit ?? RETRIEVAL_STRATEGIES[intent].limit,
56
- distanceThreshold: RETRIEVAL_STRATEGIES[intent].distanceThreshold
57
- };
58
- const maxTokens = opts?.maxTokens ?? DEFAULT_TOKEN_BUDGET;
59
- process.stderr.write(
60
- `brain-cache: building context (intent=${intent}, budget=${maxTokens} tokens)
61
- `
62
- );
63
- const { embeddings: vectors } = await embedBatchWithRetry(indexState.embeddingModel, [query]);
64
- const queryVector = vectors[0];
65
- const results = await searchChunks(table, queryVector, strategy);
66
- const deduped = deduplicateChunks(results);
67
- const assembled = assembleContext(deduped, { maxTokens });
68
- const uniqueFiles = [...new Set(assembled.chunks.map((c) => c.filePath))];
69
- const numFiles = uniqueFiles.length;
70
- let fileContentTokens = 0;
71
- for (const filePath of uniqueFiles) {
72
- try {
73
- const fileContent = await readFile(filePath, "utf-8");
74
- fileContentTokens += countChunkTokens(fileContent);
75
- } catch {
76
- }
77
- }
78
- const toolCalls = 1 + numFiles;
79
- const toolCallOverhead = toolCalls * TOOL_CALL_OVERHEAD_TOKENS;
80
- const estimatedWithoutBraincache = fileContentTokens + toolCallOverhead;
81
- const reductionPct = estimatedWithoutBraincache > 0 ? Math.max(0, Math.round((1 - assembled.tokenCount / estimatedWithoutBraincache) * 100)) : 0;
82
- const result = {
83
- content: assembled.content,
84
- chunks: assembled.chunks,
85
- metadata: {
86
- tokensSent: assembled.tokenCount,
87
- estimatedWithoutBraincache,
88
- reductionPct,
89
- filesInContext: numFiles,
90
- localTasksPerformed: ["embed_query", "vector_search", "dedup", "token_budget"],
91
- cloudCallsMade: 0
92
- }
93
- };
94
- process.stderr.write(
95
- `brain-cache: context assembled (${assembled.tokenCount} tokens, ${reductionPct}% reduction, ${assembled.chunks.length} chunks)
96
- `
97
- );
98
- return result;
99
- }
100
-
101
- export {
102
- runBuildContext
103
- };
@@ -1,109 +0,0 @@
1
- #!/usr/bin/env node
2
- import {
3
- DEFAULT_DISTANCE_THRESHOLD,
4
- DEFAULT_SEARCH_LIMIT,
5
- DIAGNOSTIC_DISTANCE_THRESHOLD,
6
- DIAGNOSTIC_SEARCH_LIMIT,
7
- childLogger
8
- } from "./chunk-EEC7KYPY.js";
9
-
10
- // src/services/retriever.ts
11
- var log = childLogger("retriever");
12
- var DIAGNOSTIC_KEYWORDS = [
13
- "why",
14
- "broken",
15
- "error",
16
- "bug",
17
- "fail",
18
- "crash",
19
- "exception",
20
- "undefined",
21
- "null",
22
- "wrong",
23
- "issue",
24
- "problem",
25
- "causes",
26
- "caused",
27
- "debug",
28
- "fix",
29
- "incorrect",
30
- "unexpected"
31
- ];
32
- var DIAGNOSTIC_BIGRAMS = [
33
- "stack trace",
34
- "null pointer",
35
- "not defined",
36
- "type error",
37
- "reference error",
38
- "syntax error",
39
- "runtime error",
40
- "segmentation fault",
41
- "not working",
42
- "throws exception"
43
- ];
44
- var DIAGNOSTIC_EXCLUSIONS = [
45
- "error handler",
46
- "error handling",
47
- "error boundary",
48
- "error type",
49
- "error message",
50
- "error code",
51
- "error class",
52
- "null object",
53
- "null check",
54
- "null pattern",
55
- "undefined behavior",
56
- "fix the style",
57
- "fix the format",
58
- "fix the lint",
59
- "fix the config",
60
- "fix the setup"
61
- ];
62
- function classifyQueryIntent(query) {
63
- const lower = query.toLowerCase();
64
- if (DIAGNOSTIC_BIGRAMS.some((bg) => lower.includes(bg))) {
65
- return "diagnostic";
66
- }
67
- const hasKeyword = DIAGNOSTIC_KEYWORDS.some((kw) => lower.includes(kw));
68
- if (hasKeyword) {
69
- const isExcluded = DIAGNOSTIC_EXCLUSIONS.some((ex) => lower.includes(ex));
70
- if (!isExcluded) {
71
- return "diagnostic";
72
- }
73
- }
74
- return "knowledge";
75
- }
76
- var RETRIEVAL_STRATEGIES = {
77
- diagnostic: { limit: DIAGNOSTIC_SEARCH_LIMIT, distanceThreshold: DIAGNOSTIC_DISTANCE_THRESHOLD },
78
- knowledge: { limit: DEFAULT_SEARCH_LIMIT, distanceThreshold: DEFAULT_DISTANCE_THRESHOLD }
79
- };
80
- async function searchChunks(table, queryVector, opts) {
81
- log.debug({ limit: opts.limit, distanceThreshold: opts.distanceThreshold }, "Searching chunks");
82
- const rows = await table.query().nearestTo(queryVector).distanceType("cosine").limit(opts.limit).toArray();
83
- return rows.filter((r) => r._distance <= opts.distanceThreshold).map((r) => ({
84
- id: r.id,
85
- filePath: r.file_path,
86
- chunkType: r.chunk_type,
87
- scope: r.scope,
88
- name: r.name,
89
- content: r.content,
90
- startLine: r.start_line,
91
- endLine: r.end_line,
92
- similarity: 1 - r._distance
93
- })).sort((a, b) => b.similarity - a.similarity);
94
- }
95
- function deduplicateChunks(chunks) {
96
- const seen = /* @__PURE__ */ new Set();
97
- return chunks.filter((c) => {
98
- if (seen.has(c.id)) return false;
99
- seen.add(c.id);
100
- return true;
101
- });
102
- }
103
-
104
- export {
105
- classifyQueryIntent,
106
- RETRIEVAL_STRATEGIES,
107
- searchChunks,
108
- deduplicateChunks
109
- };
@@ -1,17 +0,0 @@
1
- #!/usr/bin/env node
2
-
3
- // src/lib/format.ts
4
- function formatTokenSavings(input) {
5
- const PAD = 27;
6
- const fileSuffix = input.filesInContext !== 1 ? "s" : "";
7
- const lines = [
8
- ["Tokens sent to Claude:", input.tokensSent.toLocaleString()],
9
- ["Estimated without:", `~${input.estimatedWithout.toLocaleString()} (${input.filesInContext} file${fileSuffix} + overhead)`],
10
- ["Reduction:", `${input.reductionPct}%`]
11
- ];
12
- return lines.map(([label, value]) => `${label.padEnd(PAD)}${value}`).join("\n");
13
- }
14
-
15
- export {
16
- formatTokenSavings
17
- };
@@ -1,34 +0,0 @@
1
- #!/usr/bin/env node
2
-
3
- // src/lib/claude-md-section.ts
4
- var CLAUDE_MD_SECTION = `
5
- ## Brain-Cache MCP Tools
6
-
7
- Use brain-cache tools before reading files or using Grep/Glob for codebase questions.
8
-
9
- ### build_context (use for understanding)
10
-
11
- Call \`mcp__brain-cache__build_context\` with the user's question before reading individual files. It retrieves semantically relevant code from across the repo, deduplicates results, and fits them to a token budget.
12
-
13
- Use for questions like:
14
- - "How does X work?" / "Explain X end to end"
15
- - "Walk me through the flow of X"
16
- - "What does this page/feature/component do?"
17
- - "Explain the architecture" / "How is the project structured?"
18
- - "What happens when Y is called?"
19
- - Any question that requires understanding code across multiple files
20
- - Debugging unfamiliar code paths or understanding error flows
21
-
22
- This returns better results with fewer tokens than reading files individually.
23
-
24
- ### search_codebase (use for locating)
25
-
26
- Call \`mcp__brain-cache__search_codebase\` to find functions, types, definitions, or implementations by meaning rather than keyword match.
27
-
28
- ### doctor (use for diagnosing)
29
-
30
- Call \`mcp__brain-cache__doctor\` when any brain-cache tool fails or returns unexpected results.
31
- `;
32
- export {
33
- CLAUDE_MD_SECTION
34
- };