cf-memory-mcp 3.28.0 → 3.30.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 +178 -11
- package/package.json +1 -1
package/bin/cf-memory-mcp.js
CHANGED
|
@@ -3001,7 +3001,19 @@ class CFMemoryMCP {
|
|
|
3001
3001
|
cwd: process.env.CF_MEMORY_WATCH_PATH || process.cwd(),
|
|
3002
3002
|
response_text: text,
|
|
3003
3003
|
};
|
|
3004
|
-
|
|
3004
|
+
// Atomic write: serialize to a sibling .tmp then rename so a
|
|
3005
|
+
// crash mid-write never leaves a corrupt JSON file. rename(2)
|
|
3006
|
+
// is atomic on POSIX. On Windows it may fail if the target
|
|
3007
|
+
// exists, so fall back to a direct write there.
|
|
3008
|
+
const tmp = `${p}.tmp.${process.pid}`;
|
|
3009
|
+
try {
|
|
3010
|
+
fs.writeFileSync(tmp, JSON.stringify(entry, null, 2));
|
|
3011
|
+
fs.renameSync(tmp, p);
|
|
3012
|
+
} catch (renameErr) {
|
|
3013
|
+
// Fallback: direct write. Best-effort cleanup of .tmp.
|
|
3014
|
+
try { fs.unlinkSync(tmp); } catch (_) { /* ignore */ }
|
|
3015
|
+
fs.writeFileSync(p, JSON.stringify(entry, null, 2));
|
|
3016
|
+
}
|
|
3005
3017
|
_mcpTrace('DISK_CACHE', `saved resume packet to ${p}`);
|
|
3006
3018
|
} catch (err) {
|
|
3007
3019
|
this.logDebug(`saveResumeToDisk failed: ${err && err.message}`);
|
|
@@ -3778,7 +3790,12 @@ if (process.argv.includes('--version') || process.argv.includes('-v')) {
|
|
|
3778
3790
|
process.exit(0);
|
|
3779
3791
|
}
|
|
3780
3792
|
|
|
3781
|
-
|
|
3793
|
+
// Global --help only when no subcommand is present. With a subcommand, fall
|
|
3794
|
+
// through to the per-command help dispatch below.
|
|
3795
|
+
const SUBCOMMANDS = new Set(['resume', 'list', 'checkpoint', 'status', 'clean', 'export', 'import', 'doctor', 'completion']);
|
|
3796
|
+
const hasSubcommand = process.argv[2] && SUBCOMMANDS.has(process.argv[2]);
|
|
3797
|
+
|
|
3798
|
+
if (!hasSubcommand && (process.argv.includes('--help') || process.argv.includes('-h'))) {
|
|
3782
3799
|
console.log(`
|
|
3783
3800
|
CF Memory MCP v${PACKAGE_VERSION}
|
|
3784
3801
|
|
|
@@ -3795,6 +3812,8 @@ Usage:
|
|
|
3795
3812
|
npx cf-memory-mcp export <id> Print a session's handoff as JSON bundle (backup)
|
|
3796
3813
|
npx cf-memory-mcp import <file> Restore a handoff bundle (cross-machine sync); use "-" for stdin
|
|
3797
3814
|
npx cf-memory-mcp doctor Diagnose common setup issues
|
|
3815
|
+
npx cf-memory-mcp completion bash Output shell completion script (bash|zsh|fish)
|
|
3816
|
+
npx cf-memory-mcp <cmd> --help Show flags for a specific command
|
|
3798
3817
|
npx cf-memory-mcp --version Show version
|
|
3799
3818
|
|
|
3800
3819
|
Short alias: \`cfm\` works as a drop-in for \`cf-memory-mcp\` (e.g., \`cfm resume\`).
|
|
@@ -4003,20 +4022,147 @@ async function runListCli() {
|
|
|
4003
4022
|
}
|
|
4004
4023
|
}
|
|
4005
4024
|
|
|
4025
|
+
function runCompletionCli() {
|
|
4026
|
+
const shell = process.argv[3] || 'bash';
|
|
4027
|
+
const commands = ['resume', 'list', 'checkpoint', 'status', 'clean', 'export', 'import', 'doctor', 'completion'];
|
|
4028
|
+
const flags = ['--json', '-j', '--limit', '-n', '--md', '--all', '--force', '-f', '--version', '-v', '--help', '-h', '--diagnose'];
|
|
4029
|
+
if (shell === 'bash') {
|
|
4030
|
+
process.stdout.write(`# cf-memory-mcp bash completion
|
|
4031
|
+
# Install: cf-memory-mcp completion bash > /usr/local/etc/bash_completion.d/cf-memory-mcp
|
|
4032
|
+
# Or for the current shell: source <(cf-memory-mcp completion bash)
|
|
4033
|
+
_cf_memory_mcp_complete() {
|
|
4034
|
+
local cur prev words
|
|
4035
|
+
COMPREPLY=()
|
|
4036
|
+
cur="\${COMP_WORDS[COMP_CWORD]}"
|
|
4037
|
+
prev="\${COMP_WORDS[COMP_CWORD-1]}"
|
|
4038
|
+
if [ "\$COMP_CWORD" -eq 1 ]; then
|
|
4039
|
+
COMPREPLY=( $(compgen -W "${commands.join(' ')}" -- "\$cur") )
|
|
4040
|
+
return 0
|
|
4041
|
+
fi
|
|
4042
|
+
case "\$cur" in
|
|
4043
|
+
--*)
|
|
4044
|
+
COMPREPLY=( $(compgen -W "${flags.filter(f => f.startsWith('--')).join(' ')}" -- "\$cur") )
|
|
4045
|
+
;;
|
|
4046
|
+
esac
|
|
4047
|
+
}
|
|
4048
|
+
complete -F _cf_memory_mcp_complete cf-memory-mcp
|
|
4049
|
+
complete -F _cf_memory_mcp_complete cfm
|
|
4050
|
+
`);
|
|
4051
|
+
} else if (shell === 'zsh') {
|
|
4052
|
+
process.stdout.write(`# cf-memory-mcp zsh completion
|
|
4053
|
+
# Install: cf-memory-mcp completion zsh > "\${fpath[1]}/_cf-memory-mcp" && compinit
|
|
4054
|
+
# Or for the current shell: source <(cf-memory-mcp completion zsh)
|
|
4055
|
+
#compdef cf-memory-mcp cfm
|
|
4056
|
+
_cf_memory_mcp() {
|
|
4057
|
+
local -a commands
|
|
4058
|
+
commands=(
|
|
4059
|
+
${commands.map(c => `'${c}:${c} command'`).join('\n ')}
|
|
4060
|
+
)
|
|
4061
|
+
if (( CURRENT == 2 )); then
|
|
4062
|
+
_describe 'command' commands
|
|
4063
|
+
else
|
|
4064
|
+
case "\$words[2]" in
|
|
4065
|
+
resume|export) _files ;;
|
|
4066
|
+
import) _files -g '*.json' ;;
|
|
4067
|
+
esac
|
|
4068
|
+
fi
|
|
4069
|
+
}
|
|
4070
|
+
_cf_memory_mcp "\$@"
|
|
4071
|
+
`);
|
|
4072
|
+
} else if (shell === 'fish') {
|
|
4073
|
+
process.stdout.write(`# cf-memory-mcp fish completion
|
|
4074
|
+
# Install: cf-memory-mcp completion fish > ~/.config/fish/completions/cf-memory-mcp.fish
|
|
4075
|
+
${commands.map(c => `complete -c cf-memory-mcp -n '__fish_use_subcommand' -a '${c}' -d '${c} command'`).join('\n')}
|
|
4076
|
+
${commands.map(c => `complete -c cfm -n '__fish_use_subcommand' -a '${c}' -d '${c} command'`).join('\n')}
|
|
4077
|
+
complete -c cf-memory-mcp -l json -s j -d 'Emit JSON for scripts'
|
|
4078
|
+
complete -c cf-memory-mcp -l limit -s n -d 'Limit number of results'
|
|
4079
|
+
complete -c cf-memory-mcp -l md -d 'Write markdown to file'
|
|
4080
|
+
complete -c cf-memory-mcp -l force -s f -d 'Bypass duplicate detection'
|
|
4081
|
+
complete -c cf-memory-mcp -l help -s h -d 'Show help'
|
|
4082
|
+
complete -c cf-memory-mcp -l version -s v -d 'Show version'
|
|
4083
|
+
`);
|
|
4084
|
+
} else {
|
|
4085
|
+
process.stderr.write(`Unknown shell "${shell}". Supported: bash, zsh, fish.\n`);
|
|
4086
|
+
process.exit(1);
|
|
4087
|
+
}
|
|
4088
|
+
process.exit(0);
|
|
4089
|
+
}
|
|
4090
|
+
|
|
4091
|
+
// Per-command help texts. Used when "cf-memory-mcp <cmd> --help" is invoked.
|
|
4092
|
+
const PER_COMMAND_HELP = {
|
|
4093
|
+
resume: `cf-memory-mcp resume [<session-id-prefix>] [--md path] [--json]
|
|
4094
|
+
Print the prior resume handoff (markdown by default).
|
|
4095
|
+
<session-id-prefix> Optional: pick a specific session (>=8 char prefix or full UUID).
|
|
4096
|
+
--md <path> Write the markdown to a file instead of stdout.
|
|
4097
|
+
--json, -j Emit the full bootstrap payload as JSON for scripts.
|
|
4098
|
+
Exit codes: 0 = handoff found, 3 = no handoff found.`,
|
|
4099
|
+
list: `cf-memory-mcp list [--limit N] [--json]
|
|
4100
|
+
List recent handoffs for the current cwd, status-ranked.
|
|
4101
|
+
--limit N, -n N Max number of entries (default 5, max 50).
|
|
4102
|
+
--json, -j Emit a JSON array for scripts.`,
|
|
4103
|
+
checkpoint: `cf-memory-mcp checkpoint ["<goal>"] [--force] [--json]
|
|
4104
|
+
Snapshot current state to a fresh handoff with keep_open:true. The
|
|
4105
|
+
session stays active for future checkpoints. The final end_session (or
|
|
4106
|
+
bridge shutdown) finalizes.
|
|
4107
|
+
<goal> Explicit goal string. Defaults to synthesized.
|
|
4108
|
+
--force, -f Bypass duplicate-session detection.
|
|
4109
|
+
--json, -j Emit a JSON status object.
|
|
4110
|
+
Exit codes: 0 = saved, 2 = not stored, 4 = duplicate detected.`,
|
|
4111
|
+
status: `cf-memory-mcp status [--json]
|
|
4112
|
+
Show bridge + server state for the current cwd: version, repo/branch,
|
|
4113
|
+
disk-cache age, server resume availability, and recent-handoff count.
|
|
4114
|
+
--json, -j Emit a JSON status object.`,
|
|
4115
|
+
clean: `cf-memory-mcp clean [--all] [--json]
|
|
4116
|
+
Delete the local disk cache for the current cwd.
|
|
4117
|
+
--all Delete ALL cf-memory disk caches.
|
|
4118
|
+
--json, -j Emit a JSON list of removed paths.`,
|
|
4119
|
+
export: `cf-memory-mcp export <session-id-or-prefix> [--md path]
|
|
4120
|
+
Print a handoff as a JSON bundle to stdout (or write to file). For
|
|
4121
|
+
backup or cross-machine sync.
|
|
4122
|
+
<session-id> Full UUID or short prefix (>=8 chars).
|
|
4123
|
+
--md <path> Write the JSON to a file.`,
|
|
4124
|
+
import: `cf-memory-mcp import [<file>|-] [--json]
|
|
4125
|
+
Restore a handoff bundle into a NEW session (keep_open). Cross-machine
|
|
4126
|
+
sync. Use "-" to read from stdin.
|
|
4127
|
+
<file> Bundle file path; "-" for stdin.
|
|
4128
|
+
--json, -j Emit a JSON status object.`,
|
|
4129
|
+
doctor: `cf-memory-mcp doctor [--json]
|
|
4130
|
+
Diagnose common setup issues. Checks API key, git repo/branch, disk-
|
|
4131
|
+
cache writability, worker reachability, project indexing, resume
|
|
4132
|
+
availability. Exit nonzero if any check fails.
|
|
4133
|
+
--json, -j Emit a JSON list of checks.`,
|
|
4134
|
+
completion: `cf-memory-mcp completion [bash|zsh|fish]
|
|
4135
|
+
Output shell completion script. Pipe to your shell's completion dir.`,
|
|
4136
|
+
};
|
|
4137
|
+
|
|
4138
|
+
function printPerCommandHelp(cmd) {
|
|
4139
|
+
const help = PER_COMMAND_HELP[cmd];
|
|
4140
|
+
if (help) {
|
|
4141
|
+
process.stdout.write(help + '\n');
|
|
4142
|
+
process.exit(0);
|
|
4143
|
+
}
|
|
4144
|
+
}
|
|
4145
|
+
|
|
4006
4146
|
async function runDoctorCli() {
|
|
4007
4147
|
const { flags } = parseCliArgs(process.argv.slice(3));
|
|
4008
4148
|
const server = new CFMemoryMCP();
|
|
4009
4149
|
server.logDebug = () => {};
|
|
4010
4150
|
const checks = [];
|
|
4011
|
-
const add = (label, ok, detail) => checks.push({ label, ok, detail });
|
|
4151
|
+
const add = (label, ok, detail, fix) => checks.push({ label, ok, detail, ...(fix && !ok ? { fix } : {}) });
|
|
4012
4152
|
|
|
4013
4153
|
// 1. API key set?
|
|
4014
|
-
add('CF_MEMORY_API_KEY set', !!API_KEY,
|
|
4154
|
+
add('CF_MEMORY_API_KEY set', !!API_KEY,
|
|
4155
|
+
API_KEY ? '(redacted)' : 'unset — most commands will fail',
|
|
4156
|
+
'export CF_MEMORY_API_KEY=<your-key> # get one at https://memcp.ai');
|
|
4015
4157
|
|
|
4016
4158
|
// 2. Cwd is in a git repo?
|
|
4017
4159
|
const meta = server.getRepoMetadata();
|
|
4018
|
-
add('git repo detected', !!meta.repo_path,
|
|
4019
|
-
|
|
4160
|
+
add('git repo detected', !!meta.repo_path,
|
|
4161
|
+
meta.repo_path || 'no .git in cwd; resume metadata will be empty',
|
|
4162
|
+
'cd into a repo, or run: git init');
|
|
4163
|
+
add('git branch detected', !!meta.branch,
|
|
4164
|
+
meta.branch || 'detached HEAD or no commits',
|
|
4165
|
+
meta.repo_path ? 'git checkout -b main # or commit something first' : null);
|
|
4020
4166
|
|
|
4021
4167
|
// 3. Disk cache writable?
|
|
4022
4168
|
const cachePath = server.getDiskCachePath();
|
|
@@ -4030,7 +4176,8 @@ async function runDoctorCli() {
|
|
|
4030
4176
|
fs.unlinkSync(probe);
|
|
4031
4177
|
diskWritable = true;
|
|
4032
4178
|
} catch (err) {
|
|
4033
|
-
add('disk cache writable', false, `failed: ${err.message}
|
|
4179
|
+
add('disk cache writable', false, `failed: ${err.message}`,
|
|
4180
|
+
`chmod -R u+w ${path.dirname(cachePath)} # or set CF_MEMORY_DISK_CACHE=off to disable`);
|
|
4034
4181
|
}
|
|
4035
4182
|
}
|
|
4036
4183
|
if (diskWritable) add('disk cache writable', true, path.dirname(cachePath));
|
|
@@ -4048,14 +4195,18 @@ async function runDoctorCli() {
|
|
|
4048
4195
|
workerReachable = !res?.error;
|
|
4049
4196
|
workerMs = Date.now() - t0;
|
|
4050
4197
|
} catch (_) { /* unreachable */ }
|
|
4051
|
-
add('worker reachable', workerReachable,
|
|
4198
|
+
add('worker reachable', workerReachable,
|
|
4199
|
+
workerReachable ? `${workerMs}ms` : `failed`,
|
|
4200
|
+
`curl -I ${BASE_URL}/health # check the worker URL and your network`);
|
|
4052
4201
|
|
|
4053
4202
|
// 5. Project indexed?
|
|
4054
4203
|
if (workerReachable) {
|
|
4055
4204
|
const fake = { params: { name: 'retrieve_context', arguments: {} } };
|
|
4056
4205
|
await server.maybeFillProjectId(fake);
|
|
4057
4206
|
const pid = fake.params.arguments.project_id;
|
|
4058
|
-
add('project indexed', !!pid,
|
|
4207
|
+
add('project indexed', !!pid,
|
|
4208
|
+
pid || 'no project matched cwd — run index_project to enable retrieve_context',
|
|
4209
|
+
'In your MCP client: call index_project({project_path:"' + (meta.repo_path || process.cwd()) + '"})');
|
|
4059
4210
|
|
|
4060
4211
|
// 6. Resume handoff available?
|
|
4061
4212
|
if (meta.repo_path) {
|
|
@@ -4073,7 +4224,8 @@ async function runDoctorCli() {
|
|
|
4073
4224
|
add('resume handoff available', hasHandoff,
|
|
4074
4225
|
hasHandoff
|
|
4075
4226
|
? `${(probePayload.resume_handoff.session_id||'').slice(0,8)} (${probePayload.resume_handoff.handoff_age_minutes}m old)`
|
|
4076
|
-
: 'none for this context'
|
|
4227
|
+
: 'none for this context',
|
|
4228
|
+
'cf-memory-mcp checkpoint "what you are working on" # or end_session({handoff:...}) in your MCP client');
|
|
4077
4229
|
} catch (_) { /* skip */ }
|
|
4078
4230
|
}
|
|
4079
4231
|
}
|
|
@@ -4089,9 +4241,12 @@ async function runDoctorCli() {
|
|
|
4089
4241
|
const mark = c.ok ? '✓' : '✗';
|
|
4090
4242
|
if (!c.ok) anyFailed = true;
|
|
4091
4243
|
process.stdout.write(` ${mark} ${c.label.padEnd(28)} ${c.detail || ''}\n`);
|
|
4244
|
+
if (!c.ok && c.fix) {
|
|
4245
|
+
process.stdout.write(` → ${c.fix}\n`);
|
|
4246
|
+
}
|
|
4092
4247
|
}
|
|
4093
4248
|
process.stdout.write('\n');
|
|
4094
|
-
process.stdout.write(anyFailed ? 'Some checks failed. See
|
|
4249
|
+
process.stdout.write(anyFailed ? 'Some checks failed. See suggestions above.\n' : 'All checks passed.\n');
|
|
4095
4250
|
process.exit(anyFailed ? 1 : 0);
|
|
4096
4251
|
}
|
|
4097
4252
|
|
|
@@ -4498,6 +4653,13 @@ async function runCheckpointCli() {
|
|
|
4498
4653
|
}
|
|
4499
4654
|
}
|
|
4500
4655
|
|
|
4656
|
+
// Per-command --help: intercept BEFORE any subcommand dispatch so the
|
|
4657
|
+
// dispatched function doesn't see --help as a positional arg.
|
|
4658
|
+
if (process.argv[2] && process.argv.slice(3).some(a => a === '--help' || a === '-h')) {
|
|
4659
|
+
printPerCommandHelp(process.argv[2]);
|
|
4660
|
+
// If not a known command, fall through to general help.
|
|
4661
|
+
}
|
|
4662
|
+
|
|
4501
4663
|
if (process.argv[2] === 'resume') {
|
|
4502
4664
|
runResumeCli();
|
|
4503
4665
|
return;
|
|
@@ -4538,6 +4700,11 @@ if (process.argv[2] === 'doctor') {
|
|
|
4538
4700
|
return;
|
|
4539
4701
|
}
|
|
4540
4702
|
|
|
4703
|
+
if (process.argv[2] === 'completion') {
|
|
4704
|
+
runCompletionCli();
|
|
4705
|
+
return;
|
|
4706
|
+
}
|
|
4707
|
+
|
|
4541
4708
|
if (process.argv.includes('--diagnose')) {
|
|
4542
4709
|
(async () => {
|
|
4543
4710
|
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.30.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": {
|