@cachly-dev/mcp-server 0.10.118 → 0.10.120
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/README.md +7 -3
- package/dist/src/editor.d.ts +9 -0
- package/dist/src/editor.js +63 -0
- package/dist/src/editor.js.map +1 -0
- package/dist/src/funnel-milestones.d.ts +5 -0
- package/dist/src/funnel-milestones.js +48 -0
- package/dist/src/funnel-milestones.js.map +1 -0
- package/dist/src/handlers/brain.js +8 -1
- package/dist/src/handlers/brain.js.map +1 -1
- package/dist/src/index.d.ts +19 -0
- package/dist/src/index.js +104 -15
- package/dist/src/index.js.map +1 -1
- package/glama.json +1 -1
- package/package.json +1 -1
- package/server.json +2 -2
package/README.md
CHANGED
|
@@ -266,7 +266,7 @@ npx @cachly-dev/mcp-server@latest learn-git # Auto-learn lessons from recent git
|
|
|
266
266
|
```
|
|
267
267
|
|
|
268
268
|
> **Tip — auto-learn on every merged PR:** run `learn-git` in CI via the
|
|
269
|
-
> [cachly-brain-setup GitHub Action](https://github.com/cachly-dev/cachly
|
|
269
|
+
> [cachly-brain-setup GitHub Action](https://github.com/cachly-dev/cachly-action)
|
|
270
270
|
> with `mode: learn`. Each merged PR teaches your Brain automatically.
|
|
271
271
|
|
|
272
272
|
---
|
|
@@ -282,6 +282,10 @@ known cause. Ready-to-paste templates live in
|
|
|
282
282
|
outcome to your Brain. Requires `CACHLY_JWT` + `CACHLY_BRAIN_INSTANCE_ID` secrets.
|
|
283
283
|
- **GitLab CI** — copy [`brain-from-ci-gitlab.yml`](./src/ci-integration/brain-from-ci-gitlab.yml)
|
|
284
284
|
into your pipeline: two `.post` jobs (`on_success` / `on_failure`) with `allow_failure: true`.
|
|
285
|
+
Want more than outcome pushes? The full GitLab template
|
|
286
|
+
[`cachly.gitlab-ci.yml`](https://github.com/cachly-dev/cachly-action/blob/main/templates/cachly.gitlab-ci.yml)
|
|
287
|
+
adds hidden jobs for `learn` / `scan` / `confirm` — pull it in with
|
|
288
|
+
`include: remote:` (it is an includable template, not a CI/CD Catalog component).
|
|
285
289
|
- **Anything else** — [`push-ci-outcome.mjs`](./src/ci-integration/push-ci-outcome.mjs) is a
|
|
286
290
|
standalone Node.js helper with zero dependencies. It always exits 0 — your CI never
|
|
287
291
|
fails because of a Brain push.
|
|
@@ -291,7 +295,7 @@ MCP tool — bulk-ingests past outcomes the same way `brain_from_git` ingests co
|
|
|
291
295
|
|
|
292
296
|
---
|
|
293
297
|
|
|
294
|
-
## MCP Tools (
|
|
298
|
+
## MCP Tools (126 total)
|
|
295
299
|
|
|
296
300
|
The full tool catalog is generated from `sdk/mcp/src/tools.ts`. Cross-surface
|
|
297
301
|
coverage is tracked in [`../../docs/generated/surface-parity.md`](../../docs/generated/surface-parity.md),
|
|
@@ -551,7 +555,7 @@ keep talking to your backend on every editor launch. All data stays in your infr
|
|
|
551
555
|
| Feature | Tool | What it does |
|
|
552
556
|
|---|---|---|
|
|
553
557
|
| Autonomous hygiene | `brain_hygiene` | Sweeps stale lessons, flags provisional, archives orphans |
|
|
554
|
-
| PR risk scan | `
|
|
558
|
+
| PR risk scan | `cachly-action` `scan` / `predict` modes | Matches PR title, body and changed files against Brain lessons via the `/scan` API — posts a PR comment with risk score before CI runs |
|
|
555
559
|
| Multi-agent arbitration | `brain_conflicts` · `brain_resolve_conflict` | Detects + resolves conflicting lessons across agents |
|
|
556
560
|
| Plans dashboard | `brain_plan` | Persistent plans in the UI with step tracking and brain-viz overlay |
|
|
557
561
|
| Privacy federation | `brain_contribute_signal` · `brain_import_meta` | Share patterns without sharing data — k-anonymous global commons |
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export type Editor = 'claude' | 'cursor' | 'windsurf' | 'copilot' | 'vscode' | 'jetbrains' | 'zed' | 'unknown';
|
|
2
|
+
type Env = Record<string, string | undefined>;
|
|
3
|
+
/**
|
|
4
|
+
* Best-effort host detection. Order matters: forks of VS Code (Cursor,
|
|
5
|
+
* Windsurf) also set TERM_PROGRAM=vscode, so their fork-specific signals are
|
|
6
|
+
* checked BEFORE the generic vscode fallback.
|
|
7
|
+
*/
|
|
8
|
+
export declare function detectEditor(env?: Env): Editor;
|
|
9
|
+
export {};
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
// Editor / host detection for telemetry attribution.
|
|
2
|
+
//
|
|
3
|
+
// The evening funnel report showed `unknown: 345` vs `claude: 96` / `vscode: 25`
|
|
4
|
+
// — the largest bucket was unattributed. The old detector only checked four
|
|
5
|
+
// env vars (CURSOR_TRACE_ID, WINDSURF_SESSION_ID, GITHUB_COPILOT_WORKSPACE,
|
|
6
|
+
// CLAUDE_CODE_ENTRYPOINT), so every other host — VS Code running the server as
|
|
7
|
+
// a native MCP stdio child, JetBrains, Zed, plain terminals — fell through to
|
|
8
|
+
// "unknown". This widens detection to the stable signals each host actually
|
|
9
|
+
// sets, ordered most-specific first, and stays a pure function of the env so it
|
|
10
|
+
// is exhaustively unit-testable.
|
|
11
|
+
/**
|
|
12
|
+
* Best-effort host detection. Order matters: forks of VS Code (Cursor,
|
|
13
|
+
* Windsurf) also set TERM_PROGRAM=vscode, so their fork-specific signals are
|
|
14
|
+
* checked BEFORE the generic vscode fallback.
|
|
15
|
+
*/
|
|
16
|
+
export function detectEditor(env = process.env) {
|
|
17
|
+
const has = (k) => typeof env[k] === 'string' && env[k] !== '';
|
|
18
|
+
// ── Claude Code ─────────────────────────────────────────────────────────
|
|
19
|
+
// CLAUDE_CODE_ENTRYPOINT is the long-standing signal; CLAUDECODE=1 is set by
|
|
20
|
+
// newer builds. Either identifies the Claude Code host.
|
|
21
|
+
if (has('CLAUDE_CODE_ENTRYPOINT') || env.CLAUDECODE === '1')
|
|
22
|
+
return 'claude';
|
|
23
|
+
// ── VS Code forks (must precede the generic vscode check) ────────────────
|
|
24
|
+
if (has('CURSOR_TRACE_ID') || env.TERM_PROGRAM === 'cursor')
|
|
25
|
+
return 'cursor';
|
|
26
|
+
if (has('WINDSURF_SESSION_ID') || env.TERM_PROGRAM === 'windsurf')
|
|
27
|
+
return 'windsurf';
|
|
28
|
+
// ── GitHub Copilot (agent workspace) ─────────────────────────────────────
|
|
29
|
+
if (has('GITHUB_COPILOT_WORKSPACE') || has('COPILOT_AGENT_ID'))
|
|
30
|
+
return 'copilot';
|
|
31
|
+
// ── Zed ──────────────────────────────────────────────────────────────────
|
|
32
|
+
if (env.TERM_PROGRAM === 'zed' || has('ZED_TERM'))
|
|
33
|
+
return 'zed';
|
|
34
|
+
// ── JetBrains (IntelliJ/PyCharm/GoLand/… integrated terminal) ────────────
|
|
35
|
+
if (env.TERMINAL_EMULATOR === 'JetBrains-JediTerm' ||
|
|
36
|
+
has('__INTELLIJ_COMMAND_HISTFILE__') ||
|
|
37
|
+
has('IDEA_INITIAL_DIRECTORY')) {
|
|
38
|
+
return 'jetbrains';
|
|
39
|
+
}
|
|
40
|
+
// ── VS Code (generic — after the forks above) ────────────────────────────
|
|
41
|
+
// TERM_PROGRAM=vscode covers the integrated terminal; the macOS bundle id
|
|
42
|
+
// and VSCODE_* handshake vars cover the native MCP stdio child (no terminal).
|
|
43
|
+
if (env.TERM_PROGRAM === 'vscode' ||
|
|
44
|
+
has('VSCODE_PID') ||
|
|
45
|
+
has('VSCODE_CWD') ||
|
|
46
|
+
has('VSCODE_IPC_HOOK') ||
|
|
47
|
+
has('VSCODE_IPC_HOOK_CLI') ||
|
|
48
|
+
has('VSCODE_GIT_IPC_HANDLE') ||
|
|
49
|
+
isVSCodeBundle(env.__CFBundleIdentifier)) {
|
|
50
|
+
return 'vscode';
|
|
51
|
+
}
|
|
52
|
+
return 'unknown';
|
|
53
|
+
}
|
|
54
|
+
/** macOS sets __CFBundleIdentifier to the launching app's bundle id. */
|
|
55
|
+
function isVSCodeBundle(bundle) {
|
|
56
|
+
if (!bundle)
|
|
57
|
+
return false;
|
|
58
|
+
return (bundle === 'com.microsoft.VSCode' ||
|
|
59
|
+
bundle === 'com.microsoft.VSCodeInsiders' ||
|
|
60
|
+
bundle === 'com.visualstudio.code.oss' ||
|
|
61
|
+
bundle.startsWith('com.microsoft.VSCode'));
|
|
62
|
+
}
|
|
63
|
+
//# sourceMappingURL=editor.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"editor.js","sourceRoot":"","sources":["../../src/editor.ts"],"names":[],"mappings":"AAAA,qDAAqD;AACrD,EAAE;AACF,iFAAiF;AACjF,4EAA4E;AAC5E,4EAA4E;AAC5E,+EAA+E;AAC/E,8EAA8E;AAC9E,4EAA4E;AAC5E,gFAAgF;AAChF,iCAAiC;AAcjC;;;;GAIG;AACH,MAAM,UAAU,YAAY,CAAC,MAAW,OAAO,CAAC,GAAG;IACjD,MAAM,GAAG,GAAG,CAAC,CAAS,EAAE,EAAE,CAAC,OAAO,GAAG,CAAC,CAAC,CAAC,KAAK,QAAQ,IAAI,GAAG,CAAC,CAAC,CAAC,KAAK,EAAE,CAAC;IAEvE,2EAA2E;IAC3E,6EAA6E;IAC7E,wDAAwD;IACxD,IAAI,GAAG,CAAC,wBAAwB,CAAC,IAAI,GAAG,CAAC,UAAU,KAAK,GAAG;QAAE,OAAO,QAAQ,CAAC;IAE7E,4EAA4E;IAC5E,IAAI,GAAG,CAAC,iBAAiB,CAAC,IAAI,GAAG,CAAC,YAAY,KAAK,QAAQ;QAAE,OAAO,QAAQ,CAAC;IAC7E,IAAI,GAAG,CAAC,qBAAqB,CAAC,IAAI,GAAG,CAAC,YAAY,KAAK,UAAU;QAAE,OAAO,UAAU,CAAC;IAErF,4EAA4E;IAC5E,IAAI,GAAG,CAAC,0BAA0B,CAAC,IAAI,GAAG,CAAC,kBAAkB,CAAC;QAAE,OAAO,SAAS,CAAC;IAEjF,4EAA4E;IAC5E,IAAI,GAAG,CAAC,YAAY,KAAK,KAAK,IAAI,GAAG,CAAC,UAAU,CAAC;QAAE,OAAO,KAAK,CAAC;IAEhE,4EAA4E;IAC5E,IACE,GAAG,CAAC,iBAAiB,KAAK,oBAAoB;QAC9C,GAAG,CAAC,+BAA+B,CAAC;QACpC,GAAG,CAAC,wBAAwB,CAAC,EAC7B,CAAC;QACD,OAAO,WAAW,CAAC;IACrB,CAAC;IAED,4EAA4E;IAC5E,0EAA0E;IAC1E,8EAA8E;IAC9E,IACE,GAAG,CAAC,YAAY,KAAK,QAAQ;QAC7B,GAAG,CAAC,YAAY,CAAC;QACjB,GAAG,CAAC,YAAY,CAAC;QACjB,GAAG,CAAC,iBAAiB,CAAC;QACtB,GAAG,CAAC,qBAAqB,CAAC;QAC1B,GAAG,CAAC,uBAAuB,CAAC;QAC5B,cAAc,CAAC,GAAG,CAAC,oBAAoB,CAAC,EACxC,CAAC;QACD,OAAO,QAAQ,CAAC;IAClB,CAAC;IAED,OAAO,SAAS,CAAC;AACnB,CAAC;AAED,wEAAwE;AACxE,SAAS,cAAc,CAAC,MAA0B;IAChD,IAAI,CAAC,MAAM;QAAE,OAAO,KAAK,CAAC;IAC1B,OAAO,CACL,MAAM,KAAK,sBAAsB;QACjC,MAAM,KAAK,8BAA8B;QACzC,MAAM,KAAK,2BAA2B;QACtC,MAAM,CAAC,UAAU,CAAC,sBAAsB,CAAC,CAC1C,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export declare function defaultMilestonePath(): string;
|
|
2
|
+
/** True when this milestone was already sent from this machine. */
|
|
3
|
+
export declare function milestoneSent(name: string, path?: string): boolean;
|
|
4
|
+
/** Record a milestone (idempotent). Never throws. */
|
|
5
|
+
export declare function markMilestoneSent(name: string, path?: string): void;
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
// Persistent once-per-machine funnel milestones.
|
|
2
|
+
//
|
|
3
|
+
// Milestones like first_call_success were guarded by an in-memory flag only —
|
|
4
|
+
// but ambient hooks spawn a FRESH server process per prompt, so every hook
|
|
5
|
+
// invocation re-sent "first call" and the backend saw 66 "first" calls from
|
|
6
|
+
// one user (and Telegram pinged the founder on every one). Persisting the
|
|
7
|
+
// marker in ~/.cachly/ makes "first" mean first-on-this-machine across
|
|
8
|
+
// process restarts. Best-effort: any filesystem problem degrades to the old
|
|
9
|
+
// in-memory behaviour (the backend now dedupes too), never throws.
|
|
10
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
|
|
11
|
+
import { homedir } from 'node:os';
|
|
12
|
+
import { dirname, join } from 'node:path';
|
|
13
|
+
export function defaultMilestonePath() {
|
|
14
|
+
return join(homedir(), '.cachly', 'funnel-milestones.json');
|
|
15
|
+
}
|
|
16
|
+
function readAll(path) {
|
|
17
|
+
try {
|
|
18
|
+
if (!existsSync(path))
|
|
19
|
+
return {};
|
|
20
|
+
const parsed = JSON.parse(readFileSync(path, 'utf-8'));
|
|
21
|
+
if (parsed && typeof parsed === 'object' && !Array.isArray(parsed)) {
|
|
22
|
+
return parsed;
|
|
23
|
+
}
|
|
24
|
+
return {};
|
|
25
|
+
}
|
|
26
|
+
catch {
|
|
27
|
+
return {}; // corrupt file → treat as empty, next mark rewrites it
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
/** True when this milestone was already sent from this machine. */
|
|
31
|
+
export function milestoneSent(name, path = defaultMilestonePath()) {
|
|
32
|
+
return name in readAll(path);
|
|
33
|
+
}
|
|
34
|
+
/** Record a milestone (idempotent). Never throws. */
|
|
35
|
+
export function markMilestoneSent(name, path = defaultMilestonePath()) {
|
|
36
|
+
try {
|
|
37
|
+
const all = readAll(path);
|
|
38
|
+
if (name in all)
|
|
39
|
+
return;
|
|
40
|
+
all[name] = new Date().toISOString();
|
|
41
|
+
mkdirSync(dirname(path), { recursive: true });
|
|
42
|
+
writeFileSync(path, JSON.stringify(all, null, 2) + '\n', 'utf-8');
|
|
43
|
+
}
|
|
44
|
+
catch {
|
|
45
|
+
/* best-effort — the server-side dedup is the safety net */
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
//# sourceMappingURL=funnel-milestones.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"funnel-milestones.js","sourceRoot":"","sources":["../../src/funnel-milestones.ts"],"names":[],"mappings":"AAAA,iDAAiD;AACjD,EAAE;AACF,8EAA8E;AAC9E,2EAA2E;AAC3E,4EAA4E;AAC5E,0EAA0E;AAC1E,uEAAuE;AACvE,4EAA4E;AAC5E,mEAAmE;AAEnE,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAC7E,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAClC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAE1C,MAAM,UAAU,oBAAoB;IAClC,OAAO,IAAI,CAAC,OAAO,EAAE,EAAE,SAAS,EAAE,wBAAwB,CAAC,CAAC;AAC9D,CAAC;AAED,SAAS,OAAO,CAAC,IAAY;IAC3B,IAAI,CAAC;QACH,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC;YAAE,OAAO,EAAE,CAAC;QACjC,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,IAAI,EAAE,OAAO,CAAC,CAAY,CAAC;QAClE,IAAI,MAAM,IAAI,OAAO,MAAM,KAAK,QAAQ,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC;YACnE,OAAO,MAAgC,CAAC;QAC1C,CAAC;QACD,OAAO,EAAE,CAAC;IACZ,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,CAAC,CAAC,uDAAuD;IACpE,CAAC;AACH,CAAC;AAED,mEAAmE;AACnE,MAAM,UAAU,aAAa,CAAC,IAAY,EAAE,OAAe,oBAAoB,EAAE;IAC/E,OAAO,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;AAC/B,CAAC;AAED,qDAAqD;AACrD,MAAM,UAAU,iBAAiB,CAAC,IAAY,EAAE,OAAe,oBAAoB,EAAE;IACnF,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;QAC1B,IAAI,IAAI,IAAI,GAAG;YAAE,OAAO;QACxB,GAAG,CAAC,IAAI,CAAC,GAAG,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;QACrC,SAAS,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QAC9C,aAAa,CAAC,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,GAAG,EAAE,IAAI,EAAE,CAAC,CAAC,GAAG,IAAI,EAAE,OAAO,CAAC,CAAC;IACpE,CAAC;IAAC,MAAM,CAAC;QACP,2DAA2D;IAC7D,CAAC;AACH,CAAC"}
|
|
@@ -7,8 +7,15 @@ import { keywordSearch, tokenize, splitMultiQuery, levenshtein, indexVocab as _i
|
|
|
7
7
|
import { rerankByQuality } from '../rerank.js';
|
|
8
8
|
import { computeEmbedding, hasEmbedProvider } from '../embeddings.js';
|
|
9
9
|
// ── Changelog (shown once per version in session_start) ──────────────────────
|
|
10
|
-
const MCP_VERSION = '0.10.
|
|
10
|
+
const MCP_VERSION = '0.10.119';
|
|
11
11
|
const WHATS_NEW = {
|
|
12
|
+
'0.10.119': [
|
|
13
|
+
`🔍 **Explainable briefings — provenance in every warning**`,
|
|
14
|
+
` 📎 \`/briefing\` warnings now carry \`outcome\`, \`author\`, \`learned_at\` and \`matched_on\` (the context tokens that triggered the warning) — editors can show WHY a fix hint fired, not just what it claims.`,
|
|
15
|
+
` 🧰 VS Code 0.12.0 uses this: severity + confidence in the toast, full lesson card with provenance, "Not helpful" per-file suppression, reviewable CLS auto-learns.`,
|
|
16
|
+
` 🧹 CI docs now describe exactly what ships: scan mode uses the REST \`/scan\` endpoint, the GitLab template covers learn/scan/confirm.`,
|
|
17
|
+
` 📊 126 MCP tools`,
|
|
18
|
+
],
|
|
12
19
|
'0.10.115': [
|
|
13
20
|
`🔧 **\`brain_get_pref\` + \`brain_set_pref\` — persistent Brain preferences**`,
|
|
14
21
|
` 📖 \`brain_get_pref\` reads back any stored preference (omit key to list all).`,
|