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.
- package/bin/cf-memory-mcp.js +96 -28
- package/package.json +1 -1
package/bin/cf-memory-mcp.js
CHANGED
|
@@ -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
|
|
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
|
-
|
|
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
|
|
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
|
|
5511
|
-
|
|
5512
|
-
|
|
5513
|
-
|
|
5514
|
-
|
|
5515
|
-
|
|
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
|
-
//
|
|
5522
|
-
|
|
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.
|
|
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": {
|