cf-memory-mcp 3.51.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.
@@ -4091,6 +4091,14 @@ function parseCliArgs(rest) {
4091
4091
  flags.repo_path = rest[++i];
4092
4092
  } else if (a.startsWith('--repo=')) {
4093
4093
  flags.repo_path = a.slice('--repo='.length);
4094
+ } else if (a === '--project-id') {
4095
+ flags.project_id = rest[++i];
4096
+ } else if (a.startsWith('--project-id=')) {
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);
4094
4102
  } else if (a === '--since') {
4095
4103
  flags.since = rest[++i];
4096
4104
  } else if (a.startsWith('--since=')) {
@@ -4121,9 +4129,20 @@ async function runResumeCli() {
4121
4129
  // Optional positional session_id argument.
4122
4130
  const sessionArg = positional[0];
4123
4131
  if (sessionArg) args.session_id_hint = sessionArg;
4124
- const fake = { params: { name: 'retrieve_context', arguments: {} } };
4125
- await server.maybeFillProjectId(fake);
4126
- if (fake.params.arguments.project_id) args.project_id = fake.params.arguments.project_id;
4132
+ // Cross-laptop overrides: --repo and --project-id let users bypass
4133
+ // cwd-based matching when paths differ across machines. --project-id
4134
+ // is the most reliable cross-machine anchor since it survives
4135
+ // path differences.
4136
+ if (flags.repo_path) {
4137
+ args.repo_path = flags.repo_path;
4138
+ // Don't auto-fill project_id from the local cwd when --repo
4139
+ // overrides (would otherwise short-circuit to a wrong session).
4140
+ } else {
4141
+ const fake = { params: { name: 'retrieve_context', arguments: {} } };
4142
+ await server.maybeFillProjectId(fake);
4143
+ if (fake.params.arguments.project_id) args.project_id = fake.params.arguments.project_id;
4144
+ }
4145
+ if (flags.project_id) args.project_id = flags.project_id;
4127
4146
 
4128
4147
  const response = await server.makeRequest({
4129
4148
  jsonrpc: '2.0',
@@ -4700,6 +4719,8 @@ async function runListCli() {
4700
4719
  await server.maybeFillProjectId(fake);
4701
4720
  if (fake.params.arguments.project_id) args.project_id = fake.params.arguments.project_id;
4702
4721
  }
4722
+ // --project-id explicit override (overrides any auto-fill above).
4723
+ if (flags.project_id) args.project_id = flags.project_id;
4703
4724
 
4704
4725
  const response = await server.makeRequest({
4705
4726
  jsonrpc: '2.0',
@@ -4879,6 +4900,9 @@ async function runHistoryCli() {
4879
4900
  await server.maybeFillProjectId(fake);
4880
4901
  if (fake.params.arguments.project_id) args.project_id = fake.params.arguments.project_id;
4881
4902
  }
4903
+ // --project-id overrides cwd-derived project_id. Useful when
4904
+ // resuming a project you haven't indexed on this machine.
4905
+ if (flags.project_id) args.project_id = flags.project_id;
4882
4906
 
4883
4907
  const response = await server.makeRequest({
4884
4908
  jsonrpc: '2.0',
@@ -4888,7 +4912,13 @@ async function runHistoryCli() {
4888
4912
  });
4889
4913
  const text = response?.result?.content?.[0]?.text;
4890
4914
  const payload = JSON.parse(text || '{}');
4891
- 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
+ }
4892
4922
  // Chronological order (oldest first).
4893
4923
  all.sort((a, b) => {
4894
4924
  const ta = new Date(a.ended_at || a.started_at).getTime();
@@ -5282,6 +5312,8 @@ const PER_COMMAND_HELP = {
5282
5312
  resume: `cf-memory-mcp resume [<session-id-prefix>] [--md path] [<extract-flag>] [--json]
5283
5313
  Print the prior resume handoff (markdown by default).
5284
5314
  <session-id-prefix> Optional: pick a specific session (>=8 char prefix or full UUID).
5315
+ --repo <path> Override cwd's repo (for cross-laptop or workspace-switch).
5316
+ --project-id <id> Override cwd's project_id (best for cross-laptop continuity).
5285
5317
  --md <path> Write the markdown to a file instead of stdout.
5286
5318
  --copy Pipe the markdown to the platform clipboard (pbcopy/xclip/wl-copy/clip).
5287
5319
  --card Compact 2-3 line status card (for terminal status widgets).
@@ -5366,12 +5398,13 @@ const PER_COMMAND_HELP = {
5366
5398
  Print all CF_MEMORY_* env vars the bridge reads, with their current
5367
5399
  values + descriptions. Useful for discovering knobs.
5368
5400
  --json, -j Emit JSON for scripts.`,
5369
- 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]
5370
5402
  Print all handoffs for the current cwd in chronological order (oldest
5371
5403
  first). Unlike 'list' (status-ranked, default 5), this is a flat
5372
5404
  timeline view.
5373
5405
  --limit N Max number of handoffs (default 25, max 50).
5374
5406
  --repo PATH Override the cwd's repo.
5407
+ --branch BR Filter to a specific branch.
5375
5408
  --since ISO Lower bound on handoff timestamp.
5376
5409
  --json, -j Emit JSON.`,
5377
5410
  link: `cf-memory-mcp link <child-id-or-prefix> --parent <parent-id-or-prefix> [--json]
@@ -5448,6 +5481,7 @@ async function runDoctorCli() {
5448
5481
  if (diskWritable) add('disk cache writable', true, path.dirname(cachePath));
5449
5482
 
5450
5483
  // 4. Worker reachable?
5484
+ let handoffStatsForHint = null;
5451
5485
  if (API_KEY) {
5452
5486
  const t0 = Date.now();
5453
5487
  let workerReachable = false;
@@ -5464,6 +5498,19 @@ async function runDoctorCli() {
5464
5498
  workerReachable ? `${workerMs}ms` : `failed`,
5465
5499
  `curl -I ${BASE_URL}/health # check the worker URL and your network`);
5466
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
+
5467
5514
  // 5. Project indexed?
5468
5515
  if (workerReachable) {
5469
5516
  const fake = { params: { name: 'retrieve_context', arguments: {} } };
@@ -5485,33 +5532,36 @@ async function runDoctorCli() {
5485
5532
  });
5486
5533
  const probeText = probeRes?.result?.content?.[0]?.text;
5487
5534
  const probePayload = JSON.parse(probeText || '{}');
5488
- const hasHandoff = !!probePayload.resume_handoff;
5489
- add('resume handoff available', hasHandoff,
5490
- hasHandoff
5491
- ? `${(probePayload.resume_handoff.session_id||'').slice(0,8)} (${probePayload.resume_handoff.handoff_age_minutes}m old)`
5492
- : 'none for this context',
5493
- '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);
5494
5558
  } catch (_) { /* skip */ }
5495
5559
  }
5496
5560
  }
5497
5561
  }
5498
5562
 
5499
- // Optional: handoff stats summary at the bottom (visibility into
5500
- // how much resume context is captured for the current user).
5501
- let handoffStats = null;
5502
- if (API_KEY) {
5503
- try {
5504
- const statsRes = await server.makeRequestOnce({
5505
- jsonrpc: '2.0', id: `doctor-stats-${Date.now()}`,
5506
- method: 'tools/call', params: { name: 'get_stats', arguments: {} },
5507
- });
5508
- const statsText = statsRes?.result?.content?.[0]?.text;
5509
- if (statsText) {
5510
- const statsPayload = JSON.parse(statsText);
5511
- if (statsPayload?.handoffs) handoffStats = statsPayload.handoffs;
5512
- }
5513
- } catch (_) { /* skip on failure */ }
5514
- }
5563
+ // Reuse the stats already fetched for the cross-laptop hint above.
5564
+ const handoffStats = handoffStatsForHint;
5515
5565
 
5516
5566
  if (flags.json) {
5517
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.51.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": {