cf-memory-mcp 3.50.0 → 3.52.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 +156 -5
- package/package.json +1 -1
package/bin/cf-memory-mcp.js
CHANGED
|
@@ -3919,7 +3919,7 @@ if (process.argv.includes('--version') || process.argv.includes('-v')) {
|
|
|
3919
3919
|
|
|
3920
3920
|
// Global --help only when no subcommand is present. With a subcommand, fall
|
|
3921
3921
|
// through to the per-command help dispatch below.
|
|
3922
|
-
const SUBCOMMANDS = new Set(['resume', 'list', 'checkpoint', 'status', 'clean', 'export', 'import', 'doctor', 'completion', 'delete', 'env', 'history', 'link']);
|
|
3922
|
+
const SUBCOMMANDS = new Set(['resume', 'list', 'checkpoint', 'status', 'clean', 'export', 'import', 'doctor', 'completion', 'delete', 'env', 'history', 'link', 'explain']);
|
|
3923
3923
|
const hasSubcommand = process.argv[2] && SUBCOMMANDS.has(process.argv[2]);
|
|
3924
3924
|
|
|
3925
3925
|
if (!hasSubcommand && (process.argv.includes('--help') || process.argv.includes('-h'))) {
|
|
@@ -3935,6 +3935,7 @@ Usage:
|
|
|
3935
3935
|
npx cf-memory-mcp history List all handoffs for cwd, chronological
|
|
3936
3936
|
npx cf-memory-mcp checkpoint ["<goal>"] Snapshot current state (keep_open)
|
|
3937
3937
|
npx cf-memory-mcp link <child> --parent <id> Retroactively link a child session to a parent
|
|
3938
|
+
npx cf-memory-mcp explain <id> Break down the handoff's quality_score
|
|
3938
3939
|
npx cf-memory-mcp status Show bridge state + server resume availability
|
|
3939
3940
|
npx cf-memory-mcp clean Delete local disk cache for current cwd
|
|
3940
3941
|
npx cf-memory-mcp clean --all Delete ALL local disk caches
|
|
@@ -4090,6 +4091,10 @@ function parseCliArgs(rest) {
|
|
|
4090
4091
|
flags.repo_path = rest[++i];
|
|
4091
4092
|
} else if (a.startsWith('--repo=')) {
|
|
4092
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);
|
|
4093
4098
|
} else if (a === '--since') {
|
|
4094
4099
|
flags.since = rest[++i];
|
|
4095
4100
|
} else if (a.startsWith('--since=')) {
|
|
@@ -4120,9 +4125,20 @@ async function runResumeCli() {
|
|
|
4120
4125
|
// Optional positional session_id argument.
|
|
4121
4126
|
const sessionArg = positional[0];
|
|
4122
4127
|
if (sessionArg) args.session_id_hint = sessionArg;
|
|
4123
|
-
|
|
4124
|
-
|
|
4125
|
-
|
|
4128
|
+
// Cross-laptop overrides: --repo and --project-id let users bypass
|
|
4129
|
+
// cwd-based matching when paths differ across machines. --project-id
|
|
4130
|
+
// is the most reliable cross-machine anchor since it survives
|
|
4131
|
+
// path differences.
|
|
4132
|
+
if (flags.repo_path) {
|
|
4133
|
+
args.repo_path = flags.repo_path;
|
|
4134
|
+
// Don't auto-fill project_id from the local cwd when --repo
|
|
4135
|
+
// overrides (would otherwise short-circuit to a wrong session).
|
|
4136
|
+
} else {
|
|
4137
|
+
const fake = { params: { name: 'retrieve_context', arguments: {} } };
|
|
4138
|
+
await server.maybeFillProjectId(fake);
|
|
4139
|
+
if (fake.params.arguments.project_id) args.project_id = fake.params.arguments.project_id;
|
|
4140
|
+
}
|
|
4141
|
+
if (flags.project_id) args.project_id = flags.project_id;
|
|
4126
4142
|
|
|
4127
4143
|
const response = await server.makeRequest({
|
|
4128
4144
|
jsonrpc: '2.0',
|
|
@@ -4576,6 +4592,102 @@ async function runResumeCli() {
|
|
|
4576
4592
|
}
|
|
4577
4593
|
}
|
|
4578
4594
|
|
|
4595
|
+
async function runExplainCli() {
|
|
4596
|
+
if (!API_KEY) {
|
|
4597
|
+
console.error('Error: CF_MEMORY_API_KEY environment variable is required');
|
|
4598
|
+
process.exit(1);
|
|
4599
|
+
}
|
|
4600
|
+
const { positional, flags } = parseCliArgs(process.argv.slice(3));
|
|
4601
|
+
const idArg = positional[0];
|
|
4602
|
+
if (!idArg) {
|
|
4603
|
+
console.error('Usage: cf-memory-mcp explain <session-id-or-prefix>');
|
|
4604
|
+
process.exit(1);
|
|
4605
|
+
}
|
|
4606
|
+
const server = new CFMemoryMCP();
|
|
4607
|
+
server.logDebug = () => {};
|
|
4608
|
+
try {
|
|
4609
|
+
const response = await server.makeRequest({
|
|
4610
|
+
jsonrpc: '2.0', id: `cli-explain-${Date.now()}`,
|
|
4611
|
+
method: 'tools/call',
|
|
4612
|
+
params: { name: 'get_context_bootstrap', arguments: { resume: true, session_id_hint: idArg } },
|
|
4613
|
+
});
|
|
4614
|
+
const text = response?.result?.content?.[0]?.text;
|
|
4615
|
+
const payload = JSON.parse(text || '{}');
|
|
4616
|
+
const envelope = payload.resume_handoff;
|
|
4617
|
+
if (!envelope?.handoff) {
|
|
4618
|
+
process.stderr.write((payload.empty_hint || `No handoff found for "${idArg}".`) + '\n');
|
|
4619
|
+
process.exit(3);
|
|
4620
|
+
}
|
|
4621
|
+
const h = envelope.handoff;
|
|
4622
|
+
const ageMin = envelope.handoff_age_minutes;
|
|
4623
|
+
const sizeBytes = envelope.handoff_size_bytes ?? 0;
|
|
4624
|
+
|
|
4625
|
+
// Mirror the server-side rubric (see computeQualityScore).
|
|
4626
|
+
const components = [];
|
|
4627
|
+
const add = (label, points, present, detail) =>
|
|
4628
|
+
components.push({ label, points, present, detail });
|
|
4629
|
+
|
|
4630
|
+
add('goal present (>=5 chars)', 0.20, !!(h.goal && h.goal.length >= 5), h.goal ? `"${h.goal.slice(0,40)}"` : 'missing');
|
|
4631
|
+
add('next_steps present', 0.20,
|
|
4632
|
+
Array.isArray(h.next_steps) && h.next_steps.length > 0,
|
|
4633
|
+
`${h.next_steps?.length ?? 0} step${(h.next_steps?.length ?? 0) === 1 ? '' : 's'}`);
|
|
4634
|
+
add('code_anchors present', 0.15,
|
|
4635
|
+
Array.isArray(h.code_anchors) && h.code_anchors.length > 0,
|
|
4636
|
+
`${h.code_anchors?.length ?? 0} anchor${(h.code_anchors?.length ?? 0) === 1 ? '' : 's'}`);
|
|
4637
|
+
add('files_touched present', 0.10,
|
|
4638
|
+
Array.isArray(h.files_touched) && h.files_touched.length > 0,
|
|
4639
|
+
`${h.files_touched?.length ?? 0} file${(h.files_touched?.length ?? 0) === 1 ? '' : 's'}`);
|
|
4640
|
+
add('decisions present', 0.10,
|
|
4641
|
+
Array.isArray(h.decisions) && h.decisions.length > 0,
|
|
4642
|
+
`${h.decisions?.length ?? 0} decision${(h.decisions?.length ?? 0) === 1 ? '' : 's'}`);
|
|
4643
|
+
|
|
4644
|
+
// Recency
|
|
4645
|
+
let recency = 0;
|
|
4646
|
+
let recencyDetail = '?';
|
|
4647
|
+
if (ageMin !== undefined) {
|
|
4648
|
+
if (ageMin <= 1440) recency = 0.15;
|
|
4649
|
+
else if (ageMin <= 10080) recency = 0.15 * (1 - (ageMin - 1440) / (10080 - 1440));
|
|
4650
|
+
recencyDetail = `${ageMin}m old`;
|
|
4651
|
+
}
|
|
4652
|
+
components.push({ label: 'recency (decays past 24h)', points: 0.15, present: recency > 0, detail: `${recencyDetail} → +${recency.toFixed(3)}` });
|
|
4653
|
+
|
|
4654
|
+
// Size in target band
|
|
4655
|
+
let sizeBonus = 0;
|
|
4656
|
+
let sizeDetail;
|
|
4657
|
+
if (sizeBytes >= 500 && sizeBytes <= 8000) { sizeBonus = 0.10; sizeDetail = 'in target 500-8000 byte range'; }
|
|
4658
|
+
else if (sizeBytes >= 200 && sizeBytes <= 16000) { sizeBonus = 0.05; sizeDetail = 'in extended 200-16000 byte range'; }
|
|
4659
|
+
else { sizeDetail = sizeBytes < 200 ? 'too small (<200 bytes)' : 'too large (>16000 bytes)'; }
|
|
4660
|
+
components.push({ label: 'size sweet spot', points: 0.10, present: sizeBonus > 0, detail: `${sizeBytes} bytes — ${sizeDetail}` });
|
|
4661
|
+
|
|
4662
|
+
if (flags.json) {
|
|
4663
|
+
process.stdout.write(JSON.stringify({
|
|
4664
|
+
session_id: envelope.session_id,
|
|
4665
|
+
quality_score: envelope.quality_score,
|
|
4666
|
+
components,
|
|
4667
|
+
}, null, 2) + '\n');
|
|
4668
|
+
process.exit(0);
|
|
4669
|
+
}
|
|
4670
|
+
const shortId = (envelope.session_id || '').slice(0, 8);
|
|
4671
|
+
process.stdout.write(`Quality breakdown for ${shortId} (final score: ${envelope.quality_score}):\n\n`);
|
|
4672
|
+
let earned = 0;
|
|
4673
|
+
for (const c of components) {
|
|
4674
|
+
const mark = c.present ? '✓' : '✗';
|
|
4675
|
+
const earned_pts = c.present ? c.points : 0;
|
|
4676
|
+
earned += earned_pts;
|
|
4677
|
+
process.stdout.write(` ${mark} ${c.label.padEnd(32)} +${c.points.toFixed(2)} ${c.detail || ''}\n`);
|
|
4678
|
+
}
|
|
4679
|
+
process.stdout.write(`\n Total earned: ${earned.toFixed(2)} / 1.00\n`);
|
|
4680
|
+
const missing = components.filter(c => !c.present);
|
|
4681
|
+
if (missing.length > 0) {
|
|
4682
|
+
process.stdout.write(`\n To improve: add ${missing.map(m => m.label).join(', ')}\n`);
|
|
4683
|
+
}
|
|
4684
|
+
process.exit(0);
|
|
4685
|
+
} catch (err) {
|
|
4686
|
+
console.error('explain command failed:', err.message);
|
|
4687
|
+
process.exit(1);
|
|
4688
|
+
}
|
|
4689
|
+
}
|
|
4690
|
+
|
|
4579
4691
|
async function runListCli() {
|
|
4580
4692
|
if (!API_KEY) {
|
|
4581
4693
|
console.error('Error: CF_MEMORY_API_KEY environment variable is required');
|
|
@@ -4603,6 +4715,8 @@ async function runListCli() {
|
|
|
4603
4715
|
await server.maybeFillProjectId(fake);
|
|
4604
4716
|
if (fake.params.arguments.project_id) args.project_id = fake.params.arguments.project_id;
|
|
4605
4717
|
}
|
|
4718
|
+
// --project-id explicit override (overrides any auto-fill above).
|
|
4719
|
+
if (flags.project_id) args.project_id = flags.project_id;
|
|
4606
4720
|
|
|
4607
4721
|
const response = await server.makeRequest({
|
|
4608
4722
|
jsonrpc: '2.0',
|
|
@@ -4782,6 +4896,9 @@ async function runHistoryCli() {
|
|
|
4782
4896
|
await server.maybeFillProjectId(fake);
|
|
4783
4897
|
if (fake.params.arguments.project_id) args.project_id = fake.params.arguments.project_id;
|
|
4784
4898
|
}
|
|
4899
|
+
// --project-id overrides cwd-derived project_id. Useful when
|
|
4900
|
+
// resuming a project you haven't indexed on this machine.
|
|
4901
|
+
if (flags.project_id) args.project_id = flags.project_id;
|
|
4785
4902
|
|
|
4786
4903
|
const response = await server.makeRequest({
|
|
4787
4904
|
jsonrpc: '2.0',
|
|
@@ -5019,7 +5136,8 @@ function runEnvCli() {
|
|
|
5019
5136
|
function runCompletionCli() {
|
|
5020
5137
|
const shell = process.argv[3] || 'bash';
|
|
5021
5138
|
const install = process.argv.includes('--install');
|
|
5022
|
-
const
|
|
5139
|
+
const uninstall = process.argv.includes('--uninstall');
|
|
5140
|
+
const commands = ['resume', 'list', 'history', 'checkpoint', 'link', 'explain', 'status', 'clean', 'export', 'import', 'delete', 'doctor', 'env', 'completion'];
|
|
5023
5141
|
const flags = ['--json', '-j', '--limit', '-n', '--md', '--all', '--force', '-f', '--version', '-v', '--help', '-h', '--diagnose'];
|
|
5024
5142
|
|
|
5025
5143
|
// Determine the install target for each shell. Use user-local paths
|
|
@@ -5047,6 +5165,27 @@ function runCompletionCli() {
|
|
|
5047
5165
|
}
|
|
5048
5166
|
return null;
|
|
5049
5167
|
})();
|
|
5168
|
+
// --uninstall: delete the previously-installed completion file. No
|
|
5169
|
+
// need to generate the script. Idempotent.
|
|
5170
|
+
if (uninstall) {
|
|
5171
|
+
if (!installTarget) {
|
|
5172
|
+
process.stderr.write(`No uninstall target for shell ${shell}.\n`);
|
|
5173
|
+
process.exit(1);
|
|
5174
|
+
}
|
|
5175
|
+
try {
|
|
5176
|
+
if (fs.existsSync(installTarget)) {
|
|
5177
|
+
fs.unlinkSync(installTarget);
|
|
5178
|
+
process.stderr.write(`Uninstalled ${shell} completion from ${installTarget}\n`);
|
|
5179
|
+
} else {
|
|
5180
|
+
process.stderr.write(`Nothing to uninstall (no file at ${installTarget}).\n`);
|
|
5181
|
+
}
|
|
5182
|
+
process.exit(0);
|
|
5183
|
+
} catch (err) {
|
|
5184
|
+
process.stderr.write(`Failed to uninstall: ${err.message}\n`);
|
|
5185
|
+
process.exit(1);
|
|
5186
|
+
}
|
|
5187
|
+
}
|
|
5188
|
+
|
|
5050
5189
|
// Buffer the output so we can either print or write-to-disk.
|
|
5051
5190
|
let output = '';
|
|
5052
5191
|
const emit = (chunk) => { output += chunk; };
|
|
@@ -5163,6 +5302,8 @@ const PER_COMMAND_HELP = {
|
|
|
5163
5302
|
resume: `cf-memory-mcp resume [<session-id-prefix>] [--md path] [<extract-flag>] [--json]
|
|
5164
5303
|
Print the prior resume handoff (markdown by default).
|
|
5165
5304
|
<session-id-prefix> Optional: pick a specific session (>=8 char prefix or full UUID).
|
|
5305
|
+
--repo <path> Override cwd's repo (for cross-laptop or workspace-switch).
|
|
5306
|
+
--project-id <id> Override cwd's project_id (best for cross-laptop continuity).
|
|
5166
5307
|
--md <path> Write the markdown to a file instead of stdout.
|
|
5167
5308
|
--copy Pipe the markdown to the platform clipboard (pbcopy/xclip/wl-copy/clip).
|
|
5168
5309
|
--card Compact 2-3 line status card (for terminal status widgets).
|
|
@@ -5260,6 +5401,11 @@ const PER_COMMAND_HELP = {
|
|
|
5260
5401
|
a parent in the chain. Useful when you forgot --force on checkpoint
|
|
5261
5402
|
or want to manually chain sessions across repos.
|
|
5262
5403
|
--json, -j Emit a JSON status object.`,
|
|
5404
|
+
explain: `cf-memory-mcp explain <session-id-or-prefix> [--json]
|
|
5405
|
+
Break down the handoff's quality_score into its rubric components,
|
|
5406
|
+
showing what's earned + what's missing. Lets users see why a handoff
|
|
5407
|
+
scored 0.5 vs 0.9 — and how to improve it.
|
|
5408
|
+
--json, -j Emit the breakdown as JSON.`,
|
|
5263
5409
|
};
|
|
5264
5410
|
|
|
5265
5411
|
function printPerCommandHelp(cmd) {
|
|
@@ -6045,6 +6191,11 @@ if (process.argv[2] === 'link') {
|
|
|
6045
6191
|
return;
|
|
6046
6192
|
}
|
|
6047
6193
|
|
|
6194
|
+
if (process.argv[2] === 'explain') {
|
|
6195
|
+
runExplainCli();
|
|
6196
|
+
return;
|
|
6197
|
+
}
|
|
6198
|
+
|
|
6048
6199
|
if (process.argv.includes('--diagnose')) {
|
|
6049
6200
|
(async () => {
|
|
6050
6201
|
console.log(`CF Memory MCP v${PACKAGE_VERSION} - Diagnostics`);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cf-memory-mcp",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.52.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": {
|