cf-memory-mcp 3.52.0 → 3.54.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.
@@ -4030,6 +4030,8 @@ function parseCliArgs(rest) {
4030
4030
  flags.files_only = true;
4031
4031
  } else if (a === '--anchors-only') {
4032
4032
  flags.anchors_only = true;
4033
+ } else if (a === '--absolute') {
4034
+ flags.absolute = true;
4033
4035
  } else if (a === '--decisions-only') {
4034
4036
  flags.decisions_only = true;
4035
4037
  } else if (a === '--blockers-only') {
@@ -4095,6 +4097,10 @@ function parseCliArgs(rest) {
4095
4097
  flags.project_id = rest[++i];
4096
4098
  } else if (a.startsWith('--project-id=')) {
4097
4099
  flags.project_id = a.slice('--project-id='.length);
4100
+ } else if (a === '--branch') {
4101
+ flags.branch = rest[++i];
4102
+ } else if (a.startsWith('--branch=')) {
4103
+ flags.branch = a.slice('--branch='.length);
4098
4104
  } else if (a === '--since') {
4099
4105
  flags.since = rest[++i];
4100
4106
  } else if (a.startsWith('--since=')) {
@@ -4192,11 +4198,15 @@ async function runResumeCli() {
4192
4198
  // --files-only: list files_touched paths, one per line. Designed
4193
4199
  // for xargs / cat pipelines:
4194
4200
  // $ cat $(cf-memory-mcp resume --files-only)
4201
+ // --absolute: prepend handoff.repo_path so paths work from any cwd.
4195
4202
  if (flags.files_only) {
4196
4203
  const files = payload.resume_handoff?.handoff?.files_touched || [];
4197
4204
  if (files.length === 0) process.exit(3);
4205
+ const repoRoot = payload.resume_handoff?.handoff?.repo_path;
4206
+ const toAbs = (p) => (flags.absolute && repoRoot && !path.isAbsolute(p))
4207
+ ? path.join(repoRoot, p) : p;
4198
4208
  for (const f of files) {
4199
- if (f.path) process.stdout.write(f.path + '\n');
4209
+ if (f.path) process.stdout.write(toAbs(f.path) + '\n');
4200
4210
  }
4201
4211
  process.exit(0);
4202
4212
  }
@@ -4208,8 +4218,12 @@ async function runResumeCli() {
4208
4218
  || payload.resume_handoff?.handoff?.code_anchors
4209
4219
  || [];
4210
4220
  if (anchors.length === 0) process.exit(3);
4221
+ const repoRoot = payload.resume_handoff?.handoff?.repo_path;
4222
+ const toAbs = (p) => (flags.absolute && repoRoot && !path.isAbsolute(p))
4223
+ ? path.join(repoRoot, p) : p;
4211
4224
  for (const a of anchors) {
4212
- const where = a.lines ? `${a.file_path}:${a.lines}` : a.file_path;
4225
+ const filePath = toAbs(a.file_path);
4226
+ const where = a.lines ? `${filePath}:${a.lines}` : filePath;
4213
4227
  const sym = a.name ? ` ${a.name}` : '';
4214
4228
  const staleMarker = a.stale ? ' [STALE]' : '';
4215
4229
  process.stdout.write(`${where}${sym}${staleMarker}\n`);
@@ -4908,7 +4922,13 @@ async function runHistoryCli() {
4908
4922
  });
4909
4923
  const text = response?.result?.content?.[0]?.text;
4910
4924
  const payload = JSON.parse(text || '{}');
4911
- const all = (payload.recent_handoffs || []).slice();
4925
+ let all = (payload.recent_handoffs || []).slice();
4926
+ // Client-side branch filter: server doesn't filter recent_handoffs
4927
+ // by branch (only uses it for confidence scoring on the primary
4928
+ // match), so we apply the filter here.
4929
+ if (flags.branch) {
4930
+ all = all.filter(h => h.branch === flags.branch);
4931
+ }
4912
4932
  // Chronological order (oldest first).
4913
4933
  all.sort((a, b) => {
4914
4934
  const ta = new Date(a.ended_at || a.started_at).getTime();
@@ -5137,6 +5157,31 @@ function runCompletionCli() {
5137
5157
  const shell = process.argv[3] || 'bash';
5138
5158
  const install = process.argv.includes('--install');
5139
5159
  const uninstall = process.argv.includes('--uninstall');
5160
+ const allShells = process.argv.includes('--all-shells');
5161
+
5162
+ // --install --all-shells: iterate every shell whose config dir is
5163
+ // detectable and install completion for each. Convenience for users
5164
+ // who use multiple shells. Re-spawns runCompletionCli per shell so
5165
+ // we get clean per-shell install messages.
5166
+ if (install && allShells) {
5167
+ const shells = ['bash', 'zsh', 'fish', 'powershell'];
5168
+ const { execFileSync } = require('child_process');
5169
+ let installed = 0;
5170
+ for (const s of shells) {
5171
+ try {
5172
+ const out = execFileSync(process.execPath, [__filename, 'completion', s, '--install'], {
5173
+ encoding: 'utf8', stdio: ['ignore', 'pipe', 'pipe'],
5174
+ });
5175
+ if (out) process.stdout.write(out);
5176
+ process.stderr.write(` installed ${s}\n`);
5177
+ installed++;
5178
+ } catch (err) {
5179
+ process.stderr.write(` ${s}: ${err.message.split('\n')[0]}\n`);
5180
+ }
5181
+ }
5182
+ process.stderr.write(`\nDone. Installed completion for ${installed} shell${installed === 1 ? '' : 's'}.\n`);
5183
+ process.exit(installed > 0 ? 0 : 1);
5184
+ }
5140
5185
  const commands = ['resume', 'list', 'history', 'checkpoint', 'link', 'explain', 'status', 'clean', 'export', 'import', 'delete', 'doctor', 'env', 'completion'];
5141
5186
  const flags = ['--json', '-j', '--limit', '-n', '--md', '--all', '--force', '-f', '--version', '-v', '--help', '-h', '--diagnose'];
5142
5187
 
@@ -5312,6 +5357,8 @@ const PER_COMMAND_HELP = {
5312
5357
  --next-steps All next_steps, numbered.
5313
5358
  --files-only files_touched paths, one per line (xargs-friendly).
5314
5359
  --anchors-only code_anchors as "file:lines symbol".
5360
+ --absolute With --files-only/--anchors-only: prepend repo_path
5361
+ so paths work from any cwd.
5315
5362
  --decisions-only decisions, one per line.
5316
5363
  --blockers-only open blockers, one per line.
5317
5364
  --chain Walk parent_session_id back; show the thread history.
@@ -5372,8 +5419,11 @@ const PER_COMMAND_HELP = {
5372
5419
  cache writability, worker reachability, project indexing, resume
5373
5420
  availability. Exit nonzero if any check fails.
5374
5421
  --json, -j Emit a JSON list of checks.`,
5375
- completion: `cf-memory-mcp completion [bash|zsh|fish|powershell]
5376
- Output shell completion script. Pipe to your shell's completion dir.`,
5422
+ completion: `cf-memory-mcp completion [bash|zsh|fish|powershell] [--install [--all-shells] | --uninstall]
5423
+ Output shell completion script (or install/uninstall it).
5424
+ --install Write to user-local config dir (no sudo).
5425
+ --uninstall Remove a previously-installed script.
5426
+ --all-shells With --install: install for every shell at once.`,
5377
5427
  delete: `cf-memory-mcp delete <session-id-or-prefix> [--json]
5378
5428
  cf-memory-mcp delete [--status STATUS] [--older-than 30d] [--repo PATH] --yes [--json]
5379
5429
  Delete session row(s) and any session_summary memories tied to them.
@@ -5388,12 +5438,13 @@ const PER_COMMAND_HELP = {
5388
5438
  Print all CF_MEMORY_* env vars the bridge reads, with their current
5389
5439
  values + descriptions. Useful for discovering knobs.
5390
5440
  --json, -j Emit JSON for scripts.`,
5391
- history: `cf-memory-mcp history [--limit N] [--repo PATH] [--since ISO] [--json]
5441
+ history: `cf-memory-mcp history [--limit N] [--repo PATH] [--branch BR] [--since ISO] [--json]
5392
5442
  Print all handoffs for the current cwd in chronological order (oldest
5393
5443
  first). Unlike 'list' (status-ranked, default 5), this is a flat
5394
5444
  timeline view.
5395
5445
  --limit N Max number of handoffs (default 25, max 50).
5396
5446
  --repo PATH Override the cwd's repo.
5447
+ --branch BR Filter to a specific branch.
5397
5448
  --since ISO Lower bound on handoff timestamp.
5398
5449
  --json, -j Emit JSON.`,
5399
5450
  link: `cf-memory-mcp link <child-id-or-prefix> --parent <parent-id-or-prefix> [--json]
@@ -5470,6 +5521,7 @@ async function runDoctorCli() {
5470
5521
  if (diskWritable) add('disk cache writable', true, path.dirname(cachePath));
5471
5522
 
5472
5523
  // 4. Worker reachable?
5524
+ let handoffStatsForHint = null;
5473
5525
  if (API_KEY) {
5474
5526
  const t0 = Date.now();
5475
5527
  let workerReachable = false;
@@ -5486,6 +5538,19 @@ async function runDoctorCli() {
5486
5538
  workerReachable ? `${workerMs}ms` : `failed`,
5487
5539
  `curl -I ${BASE_URL}/health # check the worker URL and your network`);
5488
5540
 
5541
+ // Pre-fetch handoff stats so the "resume handoff available" check
5542
+ // can suggest cross-laptop workflows when relevant.
5543
+ if (workerReachable) {
5544
+ try {
5545
+ const statsRes = await server.makeRequestOnce({
5546
+ jsonrpc: '2.0', id: `doctor-stats-pre-${Date.now()}`,
5547
+ method: 'tools/call', params: { name: 'get_stats', arguments: {} },
5548
+ });
5549
+ const statsText = statsRes?.result?.content?.[0]?.text;
5550
+ if (statsText) handoffStatsForHint = JSON.parse(statsText)?.handoffs || null;
5551
+ } catch (_) { /* skip */ }
5552
+ }
5553
+
5489
5554
  // 5. Project indexed?
5490
5555
  if (workerReachable) {
5491
5556
  const fake = { params: { name: 'retrieve_context', arguments: {} } };
@@ -5507,33 +5572,36 @@ async function runDoctorCli() {
5507
5572
  });
5508
5573
  const probeText = probeRes?.result?.content?.[0]?.text;
5509
5574
  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');
5575
+ const handoff = probePayload.resume_handoff;
5576
+ const hasHandoff = !!handoff;
5577
+ // Cross-laptop / wrong-repo detection: the returned
5578
+ // handoff is for a DIFFERENT repo than cwd's. Most
5579
+ // commonly this is a tier-4 fallback or repo_path_prefix
5580
+ // hit. Either way the user is probably resuming the
5581
+ // "wrong" thread by accident.
5582
+ const handoffRepo = handoff?.handoff?.repo_path;
5583
+ const seemsCrossLaptop = hasHandoff
5584
+ && handoffRepo
5585
+ && handoffRepo !== meta.repo_path;
5586
+ let fixHint = 'cf-memory-mcp checkpoint "what you are working on" # or end_session({handoff:...}) in your MCP client';
5587
+ let ok = hasHandoff;
5588
+ let detail = hasHandoff
5589
+ ? `${(handoff.session_id||'').slice(0,8)} (${handoff.handoff_age_minutes}m old)`
5590
+ : 'none for this context';
5591
+ if (seemsCrossLaptop) {
5592
+ // Soft warning — we matched, but cross-repo.
5593
+ ok = false;
5594
+ detail = `cross-repo match (cwd=${meta.repo_path}, handoff repo=${handoffRepo}); confidence ${handoff.match_confidence}`;
5595
+ fixHint = `Cross-laptop or path-mismatch case. Try: cf-memory-mcp resume --repo "${handoffRepo}" # or use --project-id from "cfm list"`;
5596
+ }
5597
+ add('resume handoff available', ok, detail, fixHint);
5516
5598
  } catch (_) { /* skip */ }
5517
5599
  }
5518
5600
  }
5519
5601
  }
5520
5602
 
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
- }
5603
+ // Reuse the stats already fetched for the cross-laptop hint above.
5604
+ const handoffStats = handoffStatsForHint;
5537
5605
 
5538
5606
  if (flags.json) {
5539
5607
  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.54.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": {