cf-memory-mcp 3.52.0 → 3.53.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.
@@ -4095,6 +4095,10 @@ function parseCliArgs(rest) {
4095
4095
  flags.project_id = rest[++i];
4096
4096
  } else if (a.startsWith('--project-id=')) {
4097
4097
  flags.project_id = a.slice('--project-id='.length);
4098
+ } else if (a === '--branch') {
4099
+ flags.branch = rest[++i];
4100
+ } else if (a.startsWith('--branch=')) {
4101
+ flags.branch = a.slice('--branch='.length);
4098
4102
  } else if (a === '--since') {
4099
4103
  flags.since = rest[++i];
4100
4104
  } else if (a.startsWith('--since=')) {
@@ -4908,7 +4912,13 @@ async function runHistoryCli() {
4908
4912
  });
4909
4913
  const text = response?.result?.content?.[0]?.text;
4910
4914
  const payload = JSON.parse(text || '{}');
4911
- const all = (payload.recent_handoffs || []).slice();
4915
+ let all = (payload.recent_handoffs || []).slice();
4916
+ // Client-side branch filter: server doesn't filter recent_handoffs
4917
+ // by branch (only uses it for confidence scoring on the primary
4918
+ // match), so we apply the filter here.
4919
+ if (flags.branch) {
4920
+ all = all.filter(h => h.branch === flags.branch);
4921
+ }
4912
4922
  // Chronological order (oldest first).
4913
4923
  all.sort((a, b) => {
4914
4924
  const ta = new Date(a.ended_at || a.started_at).getTime();
@@ -5388,12 +5398,13 @@ const PER_COMMAND_HELP = {
5388
5398
  Print all CF_MEMORY_* env vars the bridge reads, with their current
5389
5399
  values + descriptions. Useful for discovering knobs.
5390
5400
  --json, -j Emit JSON for scripts.`,
5391
- history: `cf-memory-mcp history [--limit N] [--repo PATH] [--since ISO] [--json]
5401
+ history: `cf-memory-mcp history [--limit N] [--repo PATH] [--branch BR] [--since ISO] [--json]
5392
5402
  Print all handoffs for the current cwd in chronological order (oldest
5393
5403
  first). Unlike 'list' (status-ranked, default 5), this is a flat
5394
5404
  timeline view.
5395
5405
  --limit N Max number of handoffs (default 25, max 50).
5396
5406
  --repo PATH Override the cwd's repo.
5407
+ --branch BR Filter to a specific branch.
5397
5408
  --since ISO Lower bound on handoff timestamp.
5398
5409
  --json, -j Emit JSON.`,
5399
5410
  link: `cf-memory-mcp link <child-id-or-prefix> --parent <parent-id-or-prefix> [--json]
@@ -5470,6 +5481,7 @@ async function runDoctorCli() {
5470
5481
  if (diskWritable) add('disk cache writable', true, path.dirname(cachePath));
5471
5482
 
5472
5483
  // 4. Worker reachable?
5484
+ let handoffStatsForHint = null;
5473
5485
  if (API_KEY) {
5474
5486
  const t0 = Date.now();
5475
5487
  let workerReachable = false;
@@ -5486,6 +5498,19 @@ async function runDoctorCli() {
5486
5498
  workerReachable ? `${workerMs}ms` : `failed`,
5487
5499
  `curl -I ${BASE_URL}/health # check the worker URL and your network`);
5488
5500
 
5501
+ // Pre-fetch handoff stats so the "resume handoff available" check
5502
+ // can suggest cross-laptop workflows when relevant.
5503
+ if (workerReachable) {
5504
+ try {
5505
+ const statsRes = await server.makeRequestOnce({
5506
+ jsonrpc: '2.0', id: `doctor-stats-pre-${Date.now()}`,
5507
+ method: 'tools/call', params: { name: 'get_stats', arguments: {} },
5508
+ });
5509
+ const statsText = statsRes?.result?.content?.[0]?.text;
5510
+ if (statsText) handoffStatsForHint = JSON.parse(statsText)?.handoffs || null;
5511
+ } catch (_) { /* skip */ }
5512
+ }
5513
+
5489
5514
  // 5. Project indexed?
5490
5515
  if (workerReachable) {
5491
5516
  const fake = { params: { name: 'retrieve_context', arguments: {} } };
@@ -5507,33 +5532,36 @@ async function runDoctorCli() {
5507
5532
  });
5508
5533
  const probeText = probeRes?.result?.content?.[0]?.text;
5509
5534
  const probePayload = JSON.parse(probeText || '{}');
5510
- const hasHandoff = !!probePayload.resume_handoff;
5511
- add('resume handoff available', hasHandoff,
5512
- hasHandoff
5513
- ? `${(probePayload.resume_handoff.session_id||'').slice(0,8)} (${probePayload.resume_handoff.handoff_age_minutes}m old)`
5514
- : 'none for this context',
5515
- 'cf-memory-mcp checkpoint "what you are working on" # or end_session({handoff:...}) in your MCP client');
5535
+ const handoff = probePayload.resume_handoff;
5536
+ const hasHandoff = !!handoff;
5537
+ // Cross-laptop / wrong-repo detection: the returned
5538
+ // handoff is for a DIFFERENT repo than cwd's. Most
5539
+ // commonly this is a tier-4 fallback or repo_path_prefix
5540
+ // hit. Either way the user is probably resuming the
5541
+ // "wrong" thread by accident.
5542
+ const handoffRepo = handoff?.handoff?.repo_path;
5543
+ const seemsCrossLaptop = hasHandoff
5544
+ && handoffRepo
5545
+ && handoffRepo !== meta.repo_path;
5546
+ let fixHint = 'cf-memory-mcp checkpoint "what you are working on" # or end_session({handoff:...}) in your MCP client';
5547
+ let ok = hasHandoff;
5548
+ let detail = hasHandoff
5549
+ ? `${(handoff.session_id||'').slice(0,8)} (${handoff.handoff_age_minutes}m old)`
5550
+ : 'none for this context';
5551
+ if (seemsCrossLaptop) {
5552
+ // Soft warning — we matched, but cross-repo.
5553
+ ok = false;
5554
+ detail = `cross-repo match (cwd=${meta.repo_path}, handoff repo=${handoffRepo}); confidence ${handoff.match_confidence}`;
5555
+ fixHint = `Cross-laptop or path-mismatch case. Try: cf-memory-mcp resume --repo "${handoffRepo}" # or use --project-id from "cfm list"`;
5556
+ }
5557
+ add('resume handoff available', ok, detail, fixHint);
5516
5558
  } catch (_) { /* skip */ }
5517
5559
  }
5518
5560
  }
5519
5561
  }
5520
5562
 
5521
- // Optional: handoff stats summary at the bottom (visibility into
5522
- // how much resume context is captured for the current user).
5523
- let handoffStats = null;
5524
- if (API_KEY) {
5525
- try {
5526
- const statsRes = await server.makeRequestOnce({
5527
- jsonrpc: '2.0', id: `doctor-stats-${Date.now()}`,
5528
- method: 'tools/call', params: { name: 'get_stats', arguments: {} },
5529
- });
5530
- const statsText = statsRes?.result?.content?.[0]?.text;
5531
- if (statsText) {
5532
- const statsPayload = JSON.parse(statsText);
5533
- if (statsPayload?.handoffs) handoffStats = statsPayload.handoffs;
5534
- }
5535
- } catch (_) { /* skip on failure */ }
5536
- }
5563
+ // Reuse the stats already fetched for the cross-laptop hint above.
5564
+ const handoffStats = handoffStatsForHint;
5537
5565
 
5538
5566
  if (flags.json) {
5539
5567
  process.stdout.write(JSON.stringify({ checks, ...(handoffStats ? { handoffs: handoffStats } : {}) }, null, 2) + '\n');
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cf-memory-mcp",
3
- "version": "3.52.0",
3
+ "version": "3.53.0",
4
4
  "description": "Cloudflare-hosted MCP server for code indexing, retrieval, and assistant memory with a direct remote MCP endpoint and local stdio bridge.",
5
5
  "main": "bin/cf-memory-mcp.js",
6
6
  "bin": {