@cachly-dev/mcp-server 0.10.62 → 0.10.64
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.
- package/CHANGELOG.md.tmp +0 -0
- package/PROGRESS.md +137 -0
- package/README.md +210 -180
- package/VISION_10X.md +133 -0
- package/dist/src/__tests__/brain-flow.test.js +72 -1
- package/dist/src/__tests__/brain-flow.test.js.map +1 -1
- package/dist/src/__tests__/utils.test.d.ts +1 -0
- package/dist/src/__tests__/utils.test.js +83 -0
- package/dist/src/__tests__/utils.test.js.map +1 -0
- package/dist/src/handlers/brain.js +106 -60
- package/dist/src/handlers/brain.js.map +1 -1
- package/dist/src/handlers/fedbrain.js +4 -2
- package/dist/src/handlers/fedbrain.js.map +1 -1
- package/dist/src/index.js +3 -3
- package/dist/src/tools.d.ts +17 -0
- package/dist/src/tools.js +16 -0
- package/dist/src/tools.js.map +1 -1
- package/dist/src/utils.d.ts +33 -0
- package/dist/src/utils.js +76 -0
- package/dist/src/utils.js.map +1 -1
- package/package.json +2 -2
- package/server.json +3 -3
- package/src/__tests__/brain-flow.test.ts +82 -1
- package/src/__tests__/utils.test.ts +81 -0
- package/src/handlers/brain.ts +118 -57
- package/src/handlers/fedbrain.ts +3 -2
- package/src/index.ts +3 -3
- package/src/tools.ts +17 -0
- package/src/utils.ts +80 -0
package/src/tools.ts
CHANGED
|
@@ -617,6 +617,7 @@ const TOOLS = [
|
|
|
617
617
|
instance_id: { type: 'string', description: 'UUID of the cache instance' },
|
|
618
618
|
query: { type: 'string', description: 'Natural language query to find relevant cached context' },
|
|
619
619
|
threshold: { type: 'number', description: 'Similarity threshold 0-1 (default: 0.78)' },
|
|
620
|
+
author: { type: 'string', description: 'Handle of the person doing the recall (optional). Enables team-knowledge-reuse tracking: when you recall a lesson written by a teammate, cachly counts it as cross-author reuse — the value only a shared brain delivers.' },
|
|
620
621
|
},
|
|
621
622
|
required: ['instance_id', 'query'],
|
|
622
623
|
},
|
|
@@ -893,6 +894,22 @@ const TOOLS = [
|
|
|
893
894
|
required: ['instance_id'],
|
|
894
895
|
},
|
|
895
896
|
},
|
|
897
|
+
{
|
|
898
|
+
name: 'brain_metrics',
|
|
899
|
+
description:
|
|
900
|
+
'Report the three decisive Brain metrics: (1) time-to-first-recall (onboarding friction), ' +
|
|
901
|
+
'(2) recall-lift vs. raw BM25 (the moat proof, from Cachly-Bench), and ' +
|
|
902
|
+
'(3) team-knowledge-reuse — what % of proven recalls used a teammate\'s lesson. ' +
|
|
903
|
+
'Use to track whether the Brain is delivering its core value. ' +
|
|
904
|
+
'Pass author="handle" to smart_recall so cross-author reuse can be measured.',
|
|
905
|
+
inputSchema: {
|
|
906
|
+
type: 'object',
|
|
907
|
+
properties: {
|
|
908
|
+
instance_id: { type: 'string', description: 'UUID of the cache instance' },
|
|
909
|
+
},
|
|
910
|
+
required: ['instance_id'],
|
|
911
|
+
},
|
|
912
|
+
},
|
|
896
913
|
{
|
|
897
914
|
name: 'sync_file_changes',
|
|
898
915
|
description:
|
package/src/utils.ts
CHANGED
|
@@ -8,3 +8,83 @@ export function safeJsonParse<T>(raw: string | null | undefined, fallback: T): T
|
|
|
8
8
|
return fallback;
|
|
9
9
|
}
|
|
10
10
|
}
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Runs a promise with a hard timeout. If it doesn't settle within `ms`, resolves
|
|
14
|
+
* with `fallback` instead of hanging. A memory tool must NEVER block the agent's
|
|
15
|
+
* turn — a slow Redis scan or unreachable backend degrades gracefully to a fallback.
|
|
16
|
+
*/
|
|
17
|
+
export async function withTimeout<T>(p: Promise<T>, ms: number, fallback: T): Promise<T> {
|
|
18
|
+
let timer: ReturnType<typeof setTimeout> | undefined;
|
|
19
|
+
const guard = new Promise<T>((resolve) => {
|
|
20
|
+
timer = setTimeout(() => resolve(fallback), ms);
|
|
21
|
+
});
|
|
22
|
+
try {
|
|
23
|
+
return await Promise.race([p, guard]);
|
|
24
|
+
} catch {
|
|
25
|
+
return fallback;
|
|
26
|
+
} finally {
|
|
27
|
+
if (timer) clearTimeout(timer);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// Minimal structural type for the Redis scan API we depend on — avoids importing
|
|
32
|
+
// the full ioredis type into a leaf utility module.
|
|
33
|
+
interface ScanCapable {
|
|
34
|
+
scanStream(opts: { match: string; count?: number }): {
|
|
35
|
+
on(event: 'data', cb: (batch: string[]) => void): void;
|
|
36
|
+
on(event: 'end', cb: () => void): void;
|
|
37
|
+
on(event: 'error', cb: (err: Error) => void): void;
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Collects keys matching a pattern with a hard cap and timeout. Prevents a tool
|
|
43
|
+
* from hanging on a huge keyspace: stops early at `max` keys, and resolves with
|
|
44
|
+
* whatever was gathered if the scan exceeds `timeoutMs`. Never rejects.
|
|
45
|
+
*/
|
|
46
|
+
export function scanKeys(
|
|
47
|
+
redis: ScanCapable,
|
|
48
|
+
pattern: string,
|
|
49
|
+
opts: { max?: number; count?: number; timeoutMs?: number } = {},
|
|
50
|
+
): Promise<string[]> {
|
|
51
|
+
const max = opts.max ?? 1000;
|
|
52
|
+
const timeoutMs = opts.timeoutMs ?? 3000;
|
|
53
|
+
return new Promise<string[]>((resolve) => {
|
|
54
|
+
const keys: string[] = [];
|
|
55
|
+
let done = false;
|
|
56
|
+
const finish = () => { if (!done) { done = true; resolve(keys); } };
|
|
57
|
+
const timer = setTimeout(finish, timeoutMs);
|
|
58
|
+
try {
|
|
59
|
+
const stream = redis.scanStream({ match: pattern, count: opts.count ?? 200 });
|
|
60
|
+
stream.on('data', (batch: string[]) => {
|
|
61
|
+
for (const k of batch) {
|
|
62
|
+
keys.push(k);
|
|
63
|
+
if (keys.length >= max) { clearTimeout(timer); finish(); return; }
|
|
64
|
+
}
|
|
65
|
+
});
|
|
66
|
+
stream.on('end', () => { clearTimeout(timer); finish(); });
|
|
67
|
+
stream.on('error', () => { clearTimeout(timer); finish(); });
|
|
68
|
+
} catch {
|
|
69
|
+
clearTimeout(timer);
|
|
70
|
+
finish();
|
|
71
|
+
}
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Normalizes a git path that may come from `--name-only` with rename notation,
|
|
77
|
+
* e.g. `src/{old => new}/file.ts` → `src/new/file.ts`, and `a => b` → `b`.
|
|
78
|
+
* Returns the post-rename path so file nodes don't fragment on renames.
|
|
79
|
+
*/
|
|
80
|
+
export function normalizeGitPath(raw: string): string {
|
|
81
|
+
let p = raw.trim();
|
|
82
|
+
if (!p) return p;
|
|
83
|
+
// Brace rename: dir/{old => new}/file → dir/new/file
|
|
84
|
+
p = p.replace(/\{[^}]*=>\s*([^}]*)\}/g, (_m, after: string) => after.trim());
|
|
85
|
+
// Bare rename: old => new → new
|
|
86
|
+
const arrow = p.split('=>');
|
|
87
|
+
if (arrow.length === 2) p = arrow[1]!.trim();
|
|
88
|
+
// Collapse any double slashes introduced by an empty rename segment.
|
|
89
|
+
return p.replace(/\/{2,}/g, '/').replace(/^\/|\/$/g, '');
|
|
90
|
+
}
|