cf-memory-mcp 3.37.0 → 3.39.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.
@@ -66,6 +66,32 @@ const STREAMABLE_HTTP_URL = `${BASE_URL}/mcp`;
66
66
  const LEGACY_SERVER_URL = `${BASE_URL}/mcp/message`;
67
67
  const PROGRESS_SSE_URL = `${BASE_URL}/api/indexing/progress`;
68
68
  const PACKAGE_VERSION = require('../package.json').version;
69
+
70
+ /**
71
+ * Fetch the latest version of cf-memory-mcp from the npm registry.
72
+ * Returns the latest version string, or null on timeout/error.
73
+ * Used by `doctor` to flag outdated installs. Bounded by timeoutMs
74
+ * so an unreachable registry doesn't hang the doctor command.
75
+ */
76
+ function checkLatestNpmVersion(currentVersion, timeoutMs = 1500) {
77
+ return new Promise((resolve) => {
78
+ const req = https.get('https://registry.npmjs.org/cf-memory-mcp/latest', {
79
+ timeout: timeoutMs,
80
+ headers: { 'Accept': 'application/json', 'User-Agent': `cf-memory-mcp/${currentVersion}` },
81
+ }, (res) => {
82
+ let body = '';
83
+ res.on('data', (chunk) => { body += chunk; });
84
+ res.on('end', () => {
85
+ try {
86
+ const parsed = JSON.parse(body);
87
+ resolve(parsed.version || null);
88
+ } catch (_) { resolve(null); }
89
+ });
90
+ });
91
+ req.on('error', () => resolve(null));
92
+ req.on('timeout', () => { req.destroy(); resolve(null); });
93
+ });
94
+ }
69
95
  // Default per-request timeout. Batch uploads use BATCH_TIMEOUT_MS below.
70
96
  const TIMEOUT_MS = 60000;
71
97
  // Batch uploads can take longer because the worker processes each file
@@ -3886,6 +3912,8 @@ function parseCliArgs(rest) {
3886
3912
  flags.blockers_only = true;
3887
3913
  } else if (a === '--chain') {
3888
3914
  flags.chain = true;
3915
+ } else if (a === '--validate') {
3916
+ flags.validate = true;
3889
3917
  } else if (a === '--older-than') {
3890
3918
  // Accept "7d" / "30d" / "12h" / raw number (days).
3891
3919
  const raw = rest[++i] || '';
@@ -4092,6 +4120,56 @@ async function runResumeCli() {
4092
4120
  process.exit(0);
4093
4121
  }
4094
4122
 
4123
+ // --validate: verify the handoff's file references still exist
4124
+ // on disk. Resolves each path relative to the handoff's repo_path
4125
+ // (or cwd). Reports per-path status. Useful before resuming to
4126
+ // catch "the file was moved or deleted since the handoff".
4127
+ if (flags.validate) {
4128
+ if (!found) {
4129
+ process.stderr.write('No resume handoff available to validate.\n');
4130
+ process.exit(3);
4131
+ }
4132
+ const handoff = payload.resume_handoff.handoff;
4133
+ const repoRoot = handoff.repo_path || process.env.CF_MEMORY_WATCH_PATH || process.cwd();
4134
+ const results = [];
4135
+ const checkPath = (relPath, kind) => {
4136
+ try {
4137
+ const full = path.isAbsolute(relPath) ? relPath : path.join(repoRoot, relPath);
4138
+ const exists = fs.existsSync(full);
4139
+ results.push({ kind, path: relPath, exists });
4140
+ } catch (_) {
4141
+ results.push({ kind, path: relPath, exists: false });
4142
+ }
4143
+ };
4144
+ for (const f of (handoff.files_touched || [])) checkPath(f.path, 'touched');
4145
+ for (const a of (handoff.code_anchors || [])) checkPath(a.file_path, 'anchor');
4146
+ const missing = results.filter(r => !r.exists);
4147
+
4148
+ if (flags.json) {
4149
+ process.stdout.write(JSON.stringify({
4150
+ repo_root: repoRoot,
4151
+ total: results.length,
4152
+ missing_count: missing.length,
4153
+ results,
4154
+ }, null, 2) + '\n');
4155
+ process.exit(missing.length === 0 ? 0 : 4);
4156
+ }
4157
+ process.stdout.write(`Validating ${results.length} file reference${results.length === 1 ? '' : 's'} against ${repoRoot}:\n\n`);
4158
+ for (const r of results) {
4159
+ const mark = r.exists ? '✓' : '✗';
4160
+ process.stdout.write(` ${mark} [${r.kind}] ${r.path}\n`);
4161
+ }
4162
+ process.stdout.write(`\n`);
4163
+ if (missing.length === 0) {
4164
+ process.stdout.write(`All ${results.length} references exist.\n`);
4165
+ process.exit(0);
4166
+ } else {
4167
+ process.stdout.write(`${missing.length} of ${results.length} references are missing.\n`);
4168
+ process.stdout.write(`The repo has moved or these files were deleted since the handoff was written.\n`);
4169
+ process.exit(4);
4170
+ }
4171
+ }
4172
+
4095
4173
  // --md <path>: write the rendered markdown to a file instead of
4096
4174
  // (or in addition to) stdout. Useful for piping to a markdown
4097
4175
  // viewer or saving to the project.
@@ -4566,8 +4644,10 @@ const PER_COMMAND_HELP = {
4566
4644
  --anchors-only code_anchors as "file:lines symbol".
4567
4645
  --decisions-only decisions, one per line.
4568
4646
  --blockers-only open blockers, one per line.
4647
+ --chain Walk parent_session_id back; show the thread history.
4648
+ --validate Check that files_touched + code_anchors still exist locally.
4569
4649
  --json, -j Full bootstrap payload as JSON.
4570
- Exit codes: 0 = found, 3 = no handoff / no data.`,
4650
+ Exit codes: 0 = found, 3 = no handoff / no data, 4 = --validate found missing files.`,
4571
4651
  list: `cf-memory-mcp list [--status S] [--since ISO] [--repo PATH] [--limit N] [--json]
4572
4652
  List recent handoffs for the current cwd, status-ranked. Header shows
4573
4653
  a status-count summary.
@@ -4648,6 +4728,20 @@ async function runDoctorCli() {
4648
4728
  const checks = [];
4649
4729
  const add = (label, ok, detail, fix) => checks.push({ label, ok, detail, ...(fix && !ok ? { fix } : {}) });
4650
4730
 
4731
+ // 0. Installed vs latest npm version. Best-effort: skipped on
4732
+ // network failure or when registry is unreachable. Doesn't add to
4733
+ // exit-code failures so doctor still passes if you're just behind.
4734
+ try {
4735
+ const latest = await checkLatestNpmVersion(PACKAGE_VERSION, 1500);
4736
+ if (latest && latest !== PACKAGE_VERSION) {
4737
+ add(`npm version current`, false,
4738
+ `installed v${PACKAGE_VERSION}, latest v${latest}`,
4739
+ `npm install -g cf-memory-mcp@latest # or: npx cf-memory-mcp@${latest}`);
4740
+ } else if (latest) {
4741
+ add(`npm version current`, true, `v${PACKAGE_VERSION} (latest)`);
4742
+ }
4743
+ } catch (_) { /* network failure or registry timeout — skip */ }
4744
+
4651
4745
  // 1. API key set?
4652
4746
  add('CF_MEMORY_API_KEY set', !!API_KEY,
4653
4747
  API_KEY ? '(redacted)' : 'unset — most commands will fail',
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cf-memory-mcp",
3
- "version": "3.37.0",
3
+ "version": "3.39.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": {