@cachly-dev/mcp-server 0.10.89 → 0.10.90

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/dist/src/index.js CHANGED
@@ -1754,6 +1754,84 @@ if (process.argv[2] === 'upgrade') {
1754
1754
  }
1755
1755
  process.exit(0);
1756
1756
  }
1757
+ // ── CLI: cachly bench ─────────────────────────────────────────────────────────
1758
+ // Usage: npx @cachly-dev/mcp-server@latest bench [--json] [corpus.json]
1759
+ // Runs the three-ranker recall quality benchmark (cachly vs BM25 vs flat-file)
1760
+ // on the bundled external corpus. No auth required. One shareable headline.
1761
+ if (process.argv[2] === 'bench') {
1762
+ const { fileURLToPath } = await import('node:url');
1763
+ const { dirname: _bd, resolve: _br } = await import('node:path');
1764
+ const { loadExternalCorpus, runExternalBenchmark } = await import('./bench/external-corpus.js');
1765
+ const asJson = process.argv.includes('--json');
1766
+ const customPath = process.argv.slice(3).find(a => !a.startsWith('--'));
1767
+ const here = _bd(fileURLToPath(import.meta.url));
1768
+ const corpusPath = customPath
1769
+ ? _br(process.cwd(), customPath)
1770
+ : _br(here, 'bench', 'external', 'sample-corpus.json');
1771
+ try {
1772
+ const corpus = await loadExternalCorpus(corpusPath);
1773
+ const r = await runExternalBenchmark(corpus);
1774
+ if (asJson) {
1775
+ console.log(JSON.stringify({
1776
+ corpus: corpus.name,
1777
+ lessons: r.corpusSize,
1778
+ queries: r.queryCount,
1779
+ cachly_p1: Math.round(r.cachly.precisionAt1 * 1000) / 10,
1780
+ flatfile_p1: Math.round(r.flatfile.precisionAt1 * 1000) / 10,
1781
+ cachly_mrr: Math.round(r.cachly.mrr * 1000) / 10,
1782
+ flatfile_mrr: Math.round(r.flatfile.mrr * 1000) / 10,
1783
+ p1_lift_vs_flatfile_pct: Math.round((r.cachly.precisionAt1 - r.flatfile.precisionAt1) * 1000) / 10,
1784
+ mrr_lift_vs_flatfile_pct: Math.round((r.cachly.mrr - r.flatfile.mrr) * 1000) / 10,
1785
+ }, null, 2));
1786
+ process.exit(0);
1787
+ }
1788
+ const p1Lift = ((r.cachly.precisionAt1 - r.flatfile.precisionAt1) * 100).toFixed(1);
1789
+ const mrrLift = ((r.cachly.mrr - r.flatfile.mrr) * 100).toFixed(1);
1790
+ const p1Bm25 = ((r.cachly.precisionAt1 - r.baseline.precisionAt1) * 100).toFixed(1);
1791
+ const p1Sign = r.cachly.precisionAt1 >= r.flatfile.precisionAt1 ? '\x1b[32m+' : '\x1b[31m';
1792
+ const mrrSign = r.cachly.mrr >= r.flatfile.mrr ? '\x1b[32m+' : '\x1b[31m';
1793
+ console.log('');
1794
+ console.log('\x1b[35m ╔══════════════════════════════════════════════════════════════╗\x1b[0m');
1795
+ console.log('\x1b[35m ║\x1b[0m \x1b[1m🧠 cachly recall quality benchmark\x1b[0m \x1b[35m║\x1b[0m');
1796
+ console.log('\x1b[35m ╚══════════════════════════════════════════════════════════════╝\x1b[0m');
1797
+ console.log('');
1798
+ console.log(` Corpus : ${corpus.name ?? corpusPath}`);
1799
+ console.log(` Dataset: ${r.corpusSize} lessons · ${r.queryCount} queries · 10 engineering domains`);
1800
+ console.log(` (k8s, DB, auth, CI, frontend, API, payments, observability, Node.js, infra)`);
1801
+ console.log('');
1802
+ console.log(' \x1b[1mPrecision@1\x1b[0m (finds the right lesson at rank #1):');
1803
+ console.log(` flat-file memory : \x1b[33m${(r.flatfile.precisionAt1 * 100).toFixed(1)}%\x1b[0m (naive keyword overlap — no quality signal)`);
1804
+ console.log(` raw BM25 : \x1b[33m${(r.baseline.precisionAt1 * 100).toFixed(1)}%\x1b[0m (keyword ranking)`);
1805
+ console.log(` cachly : \x1b[1m\x1b[32m${(r.cachly.precisionAt1 * 100).toFixed(1)}%\x1b[0m (BM25 + quality reranking)`);
1806
+ console.log('');
1807
+ console.log(' \x1b[1mMean Reciprocal Rank\x1b[0m (average rank of the right answer):');
1808
+ console.log(` flat-file memory : \x1b[33m${(r.flatfile.mrr * 100).toFixed(1)}%\x1b[0m`);
1809
+ console.log(` raw BM25 : \x1b[33m${(r.baseline.mrr * 100).toFixed(1)}%\x1b[0m`);
1810
+ console.log(` cachly : \x1b[1m\x1b[32m${(r.cachly.mrr * 100).toFixed(1)}%\x1b[0m`);
1811
+ console.log('');
1812
+ console.log(' \x1b[1mLift vs flat-file memory\x1b[0m (what you get by switching from plain memory files):');
1813
+ console.log(` Precision@1 : ${p1Sign}${p1Lift}%\x1b[0m`);
1814
+ console.log(` MRR : ${mrrSign}${mrrLift}%\x1b[0m`);
1815
+ console.log(` vs BM25 : \x1b[32m+${p1Bm25}%\x1b[0m Precision@1 (quality reranking on top of keyword search)`);
1816
+ console.log('');
1817
+ console.log(' \x1b[2mFlat-file = lexical overlap over raw text files (how Anthropic/Claude Memory works).\x1b[0m');
1818
+ console.log(' \x1b[2mAdversarial distractors included — failure lessons that share the exact query vocabulary.\x1b[0m');
1819
+ console.log(' \x1b[2mcachly ignores them because they have low confidence / failed outcome.\x1b[0m');
1820
+ console.log('');
1821
+ console.log(' Share your results:');
1822
+ console.log(` \x1b[36m"My Brain: Precision@1 ${(r.cachly.precisionAt1 * 100).toFixed(1)}% (${p1Sign}${p1Lift}% vs flat-file memory, ${r.queryCount} queries)"\x1b[0m`);
1823
+ console.log('');
1824
+ console.log(' Run on a custom corpus:');
1825
+ console.log(' \x1b[2m npx @cachly-dev/mcp-server@latest bench my-incidents.json\x1b[0m');
1826
+ console.log(' \x1b[2m npx @cachly-dev/mcp-server@latest bench --json # machine-readable\x1b[0m');
1827
+ console.log('');
1828
+ }
1829
+ catch (e) {
1830
+ console.error(`\n❌ bench failed: ${e.message}\n`);
1831
+ process.exit(1);
1832
+ }
1833
+ process.exit(0);
1834
+ }
1757
1835
  // ── CLI: cachly demo ──────────────────────────────────────────────────────────
1758
1836
  // Usage: npx @cachly-dev/mcp-server@latest demo
1759
1837
  // Zero-signup. Reads local git history and shows what the AI Brain would know.
@@ -2130,6 +2208,7 @@ if (!process.argv[2] && process.stdout.isTTY) {
2130
2208
  console.log(' \x1b[36m npx @cachly-dev/mcp-server@latest badge\x1b[0m ← README badge for your Brain');
2131
2209
  console.log(' \x1b[36m npx @cachly-dev/mcp-server@latest invite\x1b[0m ← Invite a teammate');
2132
2210
  console.log(' \x1b[36m npx @cachly-dev/mcp-server@latest join <token>\x1b[0m ← Accept a Brain invite');
2211
+ console.log(' \x1b[36m npx @cachly-dev/mcp-server@latest bench\x1b[0m ← Recall quality vs flat-file memory');
2133
2212
  console.log(' \x1b[36m npx @cachly-dev/mcp-server@latest upgrade\x1b[0m ← Check for updates');
2134
2213
  console.log('');
2135
2214
  console.log(' \x1b[90mWorks with: Claude Code · Cursor · Windsurf · GitHub Copilot · Cline · Zed\x1b[0m');
@@ -3312,7 +3391,7 @@ if (!JWT) {
3312
3391
  // • Editor as an MCP stdio server (non-TTY) → ONE stderr hint, then KEEP RUNNING
3313
3392
  // so tools/list works and the first tool call starts the browser sign-in.
3314
3393
  // Skip entirely for CLI subcommands that intentionally run without credentials.
3315
- const _cliNoAuthCommands = ['demo', 'share', 'publish', 'health', 'setup', 'autopilot', 'init', 'digest', 'invite', 'badge', 'join', 'upgrade', 'tool-specs', 'openapi'];
3394
+ const _cliNoAuthCommands = ['demo', 'share', 'publish', 'health', 'setup', 'autopilot', 'init', 'digest', 'invite', 'badge', 'join', 'upgrade', 'bench', 'tool-specs', 'openapi'];
3316
3395
  if (!JWT && !_cliNoAuthCommands.includes(process.argv[2] ?? '')) {
3317
3396
  const runningInTerminal = !process.argv[2] && process.stdout.isTTY === true && _isMain;
3318
3397
  if (runningInTerminal) {