cf-memory-mcp 3.43.0 → 3.45.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 +183 -8
- package/package.json +1 -1
package/bin/cf-memory-mcp.js
CHANGED
|
@@ -3967,6 +3967,12 @@ function parseCliArgs(rest) {
|
|
|
3967
3967
|
flags.copy = true;
|
|
3968
3968
|
} else if (a === '--card') {
|
|
3969
3969
|
flags.card = true;
|
|
3970
|
+
} else if (a === '--short') {
|
|
3971
|
+
flags.short = true;
|
|
3972
|
+
} else if (a === '--age') {
|
|
3973
|
+
flags.age = true;
|
|
3974
|
+
} else if (a === '--quality') {
|
|
3975
|
+
flags.quality = true;
|
|
3970
3976
|
} else if (a === '--older-than') {
|
|
3971
3977
|
// Accept "7d" / "30d" / "12h" / raw number (days).
|
|
3972
3978
|
const raw = rest[++i] || '';
|
|
@@ -4129,6 +4135,35 @@ async function runResumeCli() {
|
|
|
4129
4135
|
process.exit(0);
|
|
4130
4136
|
}
|
|
4131
4137
|
|
|
4138
|
+
// --age: print just handoff_age_minutes (single integer).
|
|
4139
|
+
if (flags.age) {
|
|
4140
|
+
const ageMin = payload.resume_handoff?.handoff_age_minutes;
|
|
4141
|
+
if (typeof ageMin !== 'number') process.exit(3);
|
|
4142
|
+
process.stdout.write(String(ageMin) + '\n');
|
|
4143
|
+
process.exit(0);
|
|
4144
|
+
}
|
|
4145
|
+
|
|
4146
|
+
// --quality: print just quality_score (single float).
|
|
4147
|
+
if (flags.quality) {
|
|
4148
|
+
const q = payload.resume_handoff?.quality_score;
|
|
4149
|
+
if (typeof q !== 'number') process.exit(3);
|
|
4150
|
+
process.stdout.write(String(q) + '\n');
|
|
4151
|
+
process.exit(0);
|
|
4152
|
+
}
|
|
4153
|
+
|
|
4154
|
+
// --short: single line for tmux/status bars. "goal · status"
|
|
4155
|
+
// truncated to terminal-friendly length.
|
|
4156
|
+
if (flags.short) {
|
|
4157
|
+
const envelope = payload.resume_handoff;
|
|
4158
|
+
if (!envelope) process.exit(3);
|
|
4159
|
+
const h = envelope.handoff || {};
|
|
4160
|
+
const goal = (h.goal || '(no goal)').slice(0, 60);
|
|
4161
|
+
const status = h.status || '?';
|
|
4162
|
+
const stale = envelope.stale ? ' [STALE]' : '';
|
|
4163
|
+
process.stdout.write(`${goal} · ${status}${stale}\n`);
|
|
4164
|
+
process.exit(0);
|
|
4165
|
+
}
|
|
4166
|
+
|
|
4132
4167
|
// --card: compact 2-3 line summary card for status widgets.
|
|
4133
4168
|
// Format: [status · age · q=score] goal / → next_step
|
|
4134
4169
|
if (flags.card) {
|
|
@@ -4750,10 +4785,41 @@ function runEnvCli() {
|
|
|
4750
4785
|
|
|
4751
4786
|
function runCompletionCli() {
|
|
4752
4787
|
const shell = process.argv[3] || 'bash';
|
|
4788
|
+
const install = process.argv.includes('--install');
|
|
4753
4789
|
const commands = ['resume', 'list', 'history', 'checkpoint', 'link', 'status', 'clean', 'export', 'import', 'delete', 'doctor', 'env', 'completion'];
|
|
4754
4790
|
const flags = ['--json', '-j', '--limit', '-n', '--md', '--all', '--force', '-f', '--version', '-v', '--help', '-h', '--diagnose'];
|
|
4791
|
+
|
|
4792
|
+
// Determine the install target for each shell. Use user-local paths
|
|
4793
|
+
// only (no sudo). Caller can override via stdout-redirect when
|
|
4794
|
+
// --install isn't used.
|
|
4795
|
+
const installTarget = (() => {
|
|
4796
|
+
const home = os.homedir();
|
|
4797
|
+
if (shell === 'bash') {
|
|
4798
|
+
// Prefer ~/.bash_completion.d/ if it exists; fall back to ~/.bash_completion.
|
|
4799
|
+
const dir = path.join(home, '.bash_completion.d');
|
|
4800
|
+
return fs.existsSync(dir) ? path.join(dir, 'cf-memory-mcp') : path.join(home, '.bash_completion');
|
|
4801
|
+
}
|
|
4802
|
+
if (shell === 'zsh') {
|
|
4803
|
+
// Honor $ZSH_CUSTOM/plugins or ~/.zsh/completions/
|
|
4804
|
+
const zdir = process.env.ZSH_CUSTOM
|
|
4805
|
+
? path.join(process.env.ZSH_CUSTOM, 'plugins', 'cf-memory-mcp')
|
|
4806
|
+
: path.join(home, '.zsh', 'completions');
|
|
4807
|
+
return path.join(zdir, '_cf-memory-mcp');
|
|
4808
|
+
}
|
|
4809
|
+
if (shell === 'fish') {
|
|
4810
|
+
return path.join(home, '.config', 'fish', 'completions', 'cf-memory-mcp.fish');
|
|
4811
|
+
}
|
|
4812
|
+
if (shell === 'powershell' || shell === 'pwsh') {
|
|
4813
|
+
return path.join(home, '.config', 'powershell', 'cf-memory-mcp.ps1');
|
|
4814
|
+
}
|
|
4815
|
+
return null;
|
|
4816
|
+
})();
|
|
4817
|
+
// Buffer the output so we can either print or write-to-disk.
|
|
4818
|
+
let output = '';
|
|
4819
|
+
const emit = (chunk) => { output += chunk; };
|
|
4820
|
+
|
|
4755
4821
|
if (shell === 'bash') {
|
|
4756
|
-
|
|
4822
|
+
emit(`# cf-memory-mcp bash completion
|
|
4757
4823
|
# Install: cf-memory-mcp completion bash > /usr/local/etc/bash_completion.d/cf-memory-mcp
|
|
4758
4824
|
# Or for the current shell: source <(cf-memory-mcp completion bash)
|
|
4759
4825
|
_cf_memory_mcp_complete() {
|
|
@@ -4775,7 +4841,7 @@ complete -F _cf_memory_mcp_complete cf-memory-mcp
|
|
|
4775
4841
|
complete -F _cf_memory_mcp_complete cfm
|
|
4776
4842
|
`);
|
|
4777
4843
|
} else if (shell === 'zsh') {
|
|
4778
|
-
|
|
4844
|
+
emit(`# cf-memory-mcp zsh completion
|
|
4779
4845
|
# Install: cf-memory-mcp completion zsh > "\${fpath[1]}/_cf-memory-mcp" && compinit
|
|
4780
4846
|
# Or for the current shell: source <(cf-memory-mcp completion zsh)
|
|
4781
4847
|
#compdef cf-memory-mcp cfm
|
|
@@ -4796,7 +4862,7 @@ _cf_memory_mcp() {
|
|
|
4796
4862
|
_cf_memory_mcp "\$@"
|
|
4797
4863
|
`);
|
|
4798
4864
|
} else if (shell === 'powershell' || shell === 'pwsh') {
|
|
4799
|
-
|
|
4865
|
+
emit(`# cf-memory-mcp PowerShell completion
|
|
4800
4866
|
# Install: cf-memory-mcp completion powershell | Out-String | Invoke-Expression
|
|
4801
4867
|
# To persist: add the above to your $PROFILE
|
|
4802
4868
|
Register-ArgumentCompleter -Native -CommandName cf-memory-mcp,cfm -ScriptBlock {
|
|
@@ -4816,7 +4882,7 @@ Register-ArgumentCompleter -Native -CommandName cf-memory-mcp,cfm -ScriptBlock {
|
|
|
4816
4882
|
}
|
|
4817
4883
|
`);
|
|
4818
4884
|
} else if (shell === 'fish') {
|
|
4819
|
-
|
|
4885
|
+
emit(`# cf-memory-mcp fish completion
|
|
4820
4886
|
# Install: cf-memory-mcp completion fish > ~/.config/fish/completions/cf-memory-mcp.fish
|
|
4821
4887
|
${commands.map(c => `complete -c cf-memory-mcp -n '__fish_use_subcommand' -a '${c}' -d '${c} command'`).join('\n')}
|
|
4822
4888
|
${commands.map(c => `complete -c cfm -n '__fish_use_subcommand' -a '${c}' -d '${c} command'`).join('\n')}
|
|
@@ -4831,6 +4897,31 @@ complete -c cf-memory-mcp -l version -s v -d 'Show version'
|
|
|
4831
4897
|
process.stderr.write(`Unknown shell "${shell}". Supported: bash, zsh, fish, powershell.\n`);
|
|
4832
4898
|
process.exit(1);
|
|
4833
4899
|
}
|
|
4900
|
+
|
|
4901
|
+
// Either install to a user-local path or print to stdout.
|
|
4902
|
+
if (install) {
|
|
4903
|
+
if (!installTarget) {
|
|
4904
|
+
process.stderr.write(`No install target for shell ${shell}.\n`);
|
|
4905
|
+
process.exit(1);
|
|
4906
|
+
}
|
|
4907
|
+
try {
|
|
4908
|
+
const dir = path.dirname(installTarget);
|
|
4909
|
+
if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
|
|
4910
|
+
fs.writeFileSync(installTarget, output);
|
|
4911
|
+
process.stderr.write(`Installed ${shell} completion to ${installTarget}\n`);
|
|
4912
|
+
if (shell === 'bash') {
|
|
4913
|
+
process.stderr.write(`Add to ~/.bashrc to autoload: source ${installTarget}\n`);
|
|
4914
|
+
} else if (shell === 'zsh') {
|
|
4915
|
+
const dirToFpath = path.dirname(installTarget);
|
|
4916
|
+
process.stderr.write(`Add to ~/.zshrc:\n fpath=(${dirToFpath} $fpath)\n autoload -Uz compinit && compinit\n`);
|
|
4917
|
+
}
|
|
4918
|
+
} catch (err) {
|
|
4919
|
+
process.stderr.write(`Failed to install: ${err.message}\n`);
|
|
4920
|
+
process.exit(1);
|
|
4921
|
+
}
|
|
4922
|
+
} else {
|
|
4923
|
+
process.stdout.write(output);
|
|
4924
|
+
}
|
|
4834
4925
|
process.exit(0);
|
|
4835
4926
|
}
|
|
4836
4927
|
|
|
@@ -4852,6 +4943,8 @@ const PER_COMMAND_HELP = {
|
|
|
4852
4943
|
--chain Walk parent_session_id back; show the thread history.
|
|
4853
4944
|
--validate Check that files_touched + code_anchors still exist locally.
|
|
4854
4945
|
--raw Print just the raw handoff JSON (no envelope metadata).
|
|
4946
|
+
--age Print handoff_age_minutes (single integer).
|
|
4947
|
+
--quality Print quality_score (single float, 0-1).
|
|
4855
4948
|
--json, -j Full bootstrap payload as JSON.
|
|
4856
4949
|
Exit codes: 0 = found, 3 = no handoff / no data, 4 = --validate found missing files.`,
|
|
4857
4950
|
list: `cf-memory-mcp list [--status S] [--since ISO] [--repo PATH] [--limit N] [--json]
|
|
@@ -4879,10 +4972,13 @@ const PER_COMMAND_HELP = {
|
|
|
4879
4972
|
--all Delete ALL cf-memory disk caches.
|
|
4880
4973
|
--json, -j Emit a JSON list of removed paths.`,
|
|
4881
4974
|
export: `cf-memory-mcp export <session-id-or-prefix> [--md path]
|
|
4975
|
+
cf-memory-mcp export --all
|
|
4882
4976
|
Print a handoff as a JSON bundle to stdout (or write to file). For
|
|
4883
|
-
backup or cross-machine sync.
|
|
4977
|
+
backup or cross-machine sync. With --all, streams ALL handoffs as
|
|
4978
|
+
NDJSON (one bundle per line) suitable for piping to a file.
|
|
4884
4979
|
<session-id> Full UUID or short prefix (>=8 chars).
|
|
4885
|
-
--md <path> Write the JSON to a file
|
|
4980
|
+
--md <path> Write the JSON to a file (single-handoff mode).
|
|
4981
|
+
--all Stream all handoffs as NDJSON.`,
|
|
4886
4982
|
import: `cf-memory-mcp import [<file>|-] [--json]
|
|
4887
4983
|
Restore a handoff bundle into a NEW session (keep_open). Cross-machine
|
|
4888
4984
|
sync. Use "-" to read from stdin.
|
|
@@ -5060,12 +5156,91 @@ async function runExportCli() {
|
|
|
5060
5156
|
}
|
|
5061
5157
|
const { positional, flags } = parseCliArgs(process.argv.slice(3));
|
|
5062
5158
|
const sessionArg = positional[0];
|
|
5063
|
-
|
|
5064
|
-
|
|
5159
|
+
const exportAll = process.argv.includes('--all');
|
|
5160
|
+
if (!sessionArg && !exportAll) {
|
|
5161
|
+
console.error('Usage:');
|
|
5162
|
+
console.error(' cf-memory-mcp export <session-id-or-prefix> # single handoff bundle');
|
|
5163
|
+
console.error(' cf-memory-mcp export --all # all handoffs as NDJSON');
|
|
5065
5164
|
process.exit(1);
|
|
5066
5165
|
}
|
|
5067
5166
|
const server = new CFMemoryMCP();
|
|
5068
5167
|
server.logDebug = () => {};
|
|
5168
|
+
|
|
5169
|
+
// --all: stream every handoff as NDJSON (one bundle per line).
|
|
5170
|
+
if (exportAll) {
|
|
5171
|
+
// EPIPE handler: when piping to `head` etc., downstream closes
|
|
5172
|
+
// stdout while we're still writing. Exit cleanly instead of
|
|
5173
|
+
// dumping a Node stack trace.
|
|
5174
|
+
process.stdout.on('error', (err) => {
|
|
5175
|
+
if (err.code === 'EPIPE') process.exit(0);
|
|
5176
|
+
throw err;
|
|
5177
|
+
});
|
|
5178
|
+
try {
|
|
5179
|
+
// Page through all the user's handoffs via get_context_bootstrap
|
|
5180
|
+
// with recent_handoff_limit=50, sorted by recency. Use `since`
|
|
5181
|
+
// to walk back in time without dupes — query for entries before
|
|
5182
|
+
// the oldest one in the previous batch.
|
|
5183
|
+
const seen = new Set();
|
|
5184
|
+
let cursor = null; // ISO timestamp; entries with ended_at|started_at >= cursor are returned
|
|
5185
|
+
let totalEmitted = 0;
|
|
5186
|
+
for (let page = 0; page < 100; page++) {
|
|
5187
|
+
const args = { resume: true, recent_handoff_limit: 50 };
|
|
5188
|
+
const meta = server.getRepoMetadata();
|
|
5189
|
+
if (meta.repo_path) args.repo_path = meta.repo_path;
|
|
5190
|
+
if (cursor) args.since = cursor;
|
|
5191
|
+
const res = await server.makeRequest({
|
|
5192
|
+
jsonrpc: '2.0', id: `cli-export-all-${page}-${Date.now()}`,
|
|
5193
|
+
method: 'tools/call',
|
|
5194
|
+
params: { name: 'get_context_bootstrap', arguments: args },
|
|
5195
|
+
});
|
|
5196
|
+
const text = res?.result?.content?.[0]?.text;
|
|
5197
|
+
const payload = JSON.parse(text || '{}');
|
|
5198
|
+
const handoffs = payload.recent_handoffs || [];
|
|
5199
|
+
if (handoffs.length === 0) break;
|
|
5200
|
+
let newCount = 0;
|
|
5201
|
+
let oldestSeen = null;
|
|
5202
|
+
for (const h of handoffs) {
|
|
5203
|
+
if (seen.has(h.session_id)) continue;
|
|
5204
|
+
seen.add(h.session_id);
|
|
5205
|
+
// Fetch the full handoff (not just the summary).
|
|
5206
|
+
const fullRes = await server.makeRequest({
|
|
5207
|
+
jsonrpc: '2.0', id: `cli-export-all-detail-${Date.now()}`,
|
|
5208
|
+
method: 'tools/call',
|
|
5209
|
+
params: { name: 'get_context_bootstrap', arguments: { resume: true, session_id_hint: h.session_id } },
|
|
5210
|
+
});
|
|
5211
|
+
const fullText = fullRes?.result?.content?.[0]?.text;
|
|
5212
|
+
const fullPayload = JSON.parse(fullText || '{}');
|
|
5213
|
+
const envelope = fullPayload.resume_handoff;
|
|
5214
|
+
if (!envelope) continue;
|
|
5215
|
+
const bundle = {
|
|
5216
|
+
kind: 'cf-memory-handoff',
|
|
5217
|
+
version: 1,
|
|
5218
|
+
exported_at: new Date().toISOString(),
|
|
5219
|
+
session_id: envelope.session_id,
|
|
5220
|
+
started_at: envelope.started_at,
|
|
5221
|
+
ended_at: envelope.ended_at,
|
|
5222
|
+
handoff: envelope.handoff,
|
|
5223
|
+
};
|
|
5224
|
+
process.stdout.write(JSON.stringify(bundle) + '\n');
|
|
5225
|
+
totalEmitted++;
|
|
5226
|
+
newCount++;
|
|
5227
|
+
const t = envelope.ended_at || envelope.started_at;
|
|
5228
|
+
if (!oldestSeen || t > oldestSeen) oldestSeen = t;
|
|
5229
|
+
}
|
|
5230
|
+
// No new entries → we've exhausted the timeline.
|
|
5231
|
+
if (newCount === 0) break;
|
|
5232
|
+
// Step the cursor forward (next page returns entries from
|
|
5233
|
+
// after the oldest we saw).
|
|
5234
|
+
cursor = oldestSeen;
|
|
5235
|
+
}
|
|
5236
|
+
process.stderr.write(`Exported ${totalEmitted} handoffs.\n`);
|
|
5237
|
+
process.exit(totalEmitted === 0 ? 3 : 0);
|
|
5238
|
+
} catch (err) {
|
|
5239
|
+
console.error('export --all failed:', err.message);
|
|
5240
|
+
process.exit(1);
|
|
5241
|
+
}
|
|
5242
|
+
return;
|
|
5243
|
+
}
|
|
5069
5244
|
try {
|
|
5070
5245
|
const args = { resume: true, session_id_hint: sessionArg };
|
|
5071
5246
|
const response = await server.makeRequest({
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cf-memory-mcp",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.45.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": {
|