cf-memory-mcp 3.64.0 → 3.65.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.
@@ -4710,6 +4710,62 @@ async function runResumeCli() {
4710
4710
  } else {
4711
4711
  process.stdout.write(JSON.stringify(payload.resume_handoff, null, 2) + '\n');
4712
4712
  }
4713
+ // Branch state: if the handoff's repo matches cwd and there
4714
+ // are uncommitted changes (especially in files_touched),
4715
+ // surface them so the agent knows work was interrupted mid-
4716
+ // edit. Quiet when the tree is clean to avoid noise. Skip
4717
+ // entirely when env var disables it.
4718
+ if (process.env.CF_MEMORY_BRANCH_STATE !== 'off') {
4719
+ try {
4720
+ const meta = server.getRepoMetadata();
4721
+ const handoffRepo = payload.resume_handoff?.handoff?.repo_path
4722
+ || payload.resume_handoff?.repo_path;
4723
+ if (meta.repo_path && handoffRepo && meta.repo_path === handoffRepo) {
4724
+ const { execSync } = require('child_process');
4725
+ const dirty = execSync('git status --porcelain', { cwd: meta.repo_path, encoding: 'utf8', timeout: 2000 })
4726
+ .split('\n').filter(Boolean);
4727
+ if (dirty.length > 0) {
4728
+ const filesTouched = Array.isArray(payload.resume_handoff?.handoff?.files_touched)
4729
+ ? payload.resume_handoff.handoff.files_touched
4730
+ .map(ft => typeof ft === 'string' ? ft : ft?.path)
4731
+ .filter(Boolean)
4732
+ : [];
4733
+ const inHandoff = new Set(filesTouched);
4734
+ const matched = dirty.filter(line => {
4735
+ const p = line.slice(3);
4736
+ return inHandoff.has(p);
4737
+ });
4738
+ const otherCount = dirty.length - matched.length;
4739
+ process.stdout.write('\n### Branch state (since handoff was written)\n');
4740
+ if (matched.length > 0) {
4741
+ process.stdout.write(`Mid-edit on ${matched.length} of your files_touched:\n`);
4742
+ for (const line of matched.slice(0, 8)) {
4743
+ process.stdout.write(` ${line}\n`);
4744
+ }
4745
+ if (matched.length > 8) {
4746
+ process.stdout.write(` ... and ${matched.length - 8} more\n`);
4747
+ }
4748
+ }
4749
+ if (otherCount > 0) {
4750
+ process.stdout.write(`Plus ${otherCount} other uncommitted file${otherCount === 1 ? '' : 's'} in the tree (run \`git status\` for full list).\n`);
4751
+ }
4752
+ // Also check if HEAD has advanced past the handoff time.
4753
+ try {
4754
+ const handoffTime = payload.resume_handoff?.ended_at
4755
+ || payload.resume_handoff?.started_at;
4756
+ if (handoffTime) {
4757
+ const since = `--since="${handoffTime}"`;
4758
+ const newCommits = execSync(`git log ${since} --oneline 2>/dev/null | wc -l`, { cwd: meta.repo_path, encoding: 'utf8', timeout: 2000, shell: '/bin/sh' }).trim();
4759
+ const n = parseInt(newCommits, 10);
4760
+ if (Number.isFinite(n) && n > 0) {
4761
+ process.stdout.write(`${n} commit${n === 1 ? '' : 's'} on this branch since handoff (\`git log --since="${handoffTime}"\` to see them).\n`);
4762
+ }
4763
+ }
4764
+ } catch (_) { /* commit-count is best-effort */ }
4765
+ }
4766
+ }
4767
+ } catch (_) { /* git unavailable or non-repo — silent */ }
4768
+ }
4713
4769
  // --include-chain N: walk parent_session_id back N times and
4714
4770
  // append each parent's resume_prompt inline so the agent sees
4715
4771
  // the full thread arc in one output.
@@ -4737,12 +4793,32 @@ async function runResumeCli() {
4737
4793
  }
4738
4794
  }
4739
4795
  if (Array.isArray(payload.recent_handoffs) && payload.recent_handoffs.length > 1) {
4740
- process.stdout.write('\n---\nOther recent handoffs:\n');
4741
- for (const h of payload.recent_handoffs.slice(0, 4)) {
4742
- const shortId = (h.session_id || '').slice(0, 8);
4743
- process.stdout.write(` ${shortId} [${h.status || '?'}] ${h.goal || ''}\n`);
4796
+ // Dedupe: skip the primary handoff (already shown) and
4797
+ // collapse near-duplicate auto-checkpoints entries that
4798
+ // share normalized (goal, branch, status). Keep the most
4799
+ // recent occurrence of each unique key. This removes
4800
+ // visible noise from 30+ in_progress sessions all named
4801
+ // "Re-verify enrichment+freshness" on the same branch.
4802
+ const primaryId = payload.resume_handoff?.session_id;
4803
+ const norm = (s) => (s || '').toLowerCase().replace(/\s+/g, ' ').trim();
4804
+ const seen = new Set();
4805
+ const unique = [];
4806
+ for (const h of payload.recent_handoffs) {
4807
+ if (h.session_id === primaryId) continue;
4808
+ const key = `${norm(h.goal)}|${norm(h.branch)}|${h.status || ''}`;
4809
+ if (seen.has(key)) continue;
4810
+ seen.add(key);
4811
+ unique.push(h);
4812
+ if (unique.length >= 4) break;
4813
+ }
4814
+ if (unique.length > 0) {
4815
+ process.stdout.write('\n---\nOther recent handoffs:\n');
4816
+ for (const h of unique) {
4817
+ const shortId = (h.session_id || '').slice(0, 8);
4818
+ process.stdout.write(` ${shortId} [${h.status || '?'}] ${h.goal || ''}\n`);
4819
+ }
4820
+ process.stdout.write(`(Pass any short id as: npx cf-memory-mcp resume <id>)\n`);
4744
4821
  }
4745
- process.stdout.write(`(Pass any short id as: npx cf-memory-mcp resume <id>)\n`);
4746
4822
  }
4747
4823
  process.exit(0);
4748
4824
  } else {
@@ -5480,6 +5556,7 @@ function runEnvCli() {
5480
5556
  ['CF_MEMORY_SHUTDOWN_HANDOFF', 'default: on; "off" disables the SIGINT/SIGTERM handoff flush'],
5481
5557
  ['CF_MEMORY_PREWARM_RESUME', 'default: on; "off" disables the background resume prewarm at bridge startup'],
5482
5558
  ['CF_MEMORY_RESUME_DIFF', 'default: on; "off" disables git-diff injection into resume responses'],
5559
+ ['CF_MEMORY_BRANCH_STATE', 'default: on; "off" disables the "Branch state (since handoff was written)" block in `cfm resume`'],
5483
5560
  ['CF_MEMORY_DISK_CACHE', 'default: on; "off" disables ~/.cf-memory/handoff-*.json disk caching'],
5484
5561
  ['CF_MEMORY_NO_RETRY', 'default: off; "1" disables the single retry-on-transient-failure'],
5485
5562
  ['CF_MEMORY_LOG', 'default: off; "1" writes DEBUG/ERROR lines to ~/.cf-memory/bridge.log'],
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cf-memory-mcp",
3
- "version": "3.64.0",
3
+ "version": "3.65.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": {