cf-memory-mcp 3.32.0 → 3.34.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.
Files changed (2) hide show
  1. package/bin/cf-memory-mcp.js +109 -21
  2. package/package.json +1 -1
@@ -418,7 +418,8 @@ const TOOLS_LIST = [
418
418
  session_id: { type: 'string', description: 'Full session UUID to delete (not a prefix).' },
419
419
  status: { description: 'Status filter: single string or array of statuses (in_progress, blocked, completed, abandoned).' },
420
420
  older_than_days: { type: 'number', description: 'Delete sessions older than N days.' },
421
- repo_path: { type: 'string', description: 'Restrict to a specific repo_path.' }
421
+ repo_path: { type: 'string', description: 'Restrict to a specific repo_path.' },
422
+ dry_run: { type: 'boolean', description: 'When true, preview what would be deleted without actually deleting.' }
422
423
  }
423
424
  }
424
425
  }
@@ -3869,6 +3870,8 @@ function parseCliArgs(rest) {
3869
3870
  flags.md_path = rest[++i];
3870
3871
  } else if (a.startsWith('--md=')) {
3871
3872
  flags.md_path = a.slice('--md='.length);
3873
+ } else if (a === '--next-only') {
3874
+ flags.next_only = true;
3872
3875
  } else if (a === '--older-than') {
3873
3876
  // Accept "7d" / "30d" / "12h" / raw number (days).
3874
3877
  const raw = rest[++i] || '';
@@ -3894,6 +3897,10 @@ function parseCliArgs(rest) {
3894
3897
  flags.repo_path = rest[++i];
3895
3898
  } else if (a.startsWith('--repo=')) {
3896
3899
  flags.repo_path = a.slice('--repo='.length);
3900
+ } else if (a === '--since') {
3901
+ flags.since = rest[++i];
3902
+ } else if (a.startsWith('--since=')) {
3903
+ flags.since = a.slice('--since='.length);
3897
3904
  } else if (a === '--yes' || a === '-y') {
3898
3905
  flags.yes = true;
3899
3906
  } else positional.push(a);
@@ -3947,6 +3954,21 @@ async function runResumeCli() {
3947
3954
  // offline-fallback cache, not just MCP-stdio usage.
3948
3955
  if (found) server.saveResumeToDisk(response);
3949
3956
 
3957
+ // --next-only: print just the first next_step. Useful for
3958
+ // shell prompts, status bars, one-liner scripting:
3959
+ // $ cf-memory-mcp resume --next-only
3960
+ if (flags.next_only) {
3961
+ const next = (payload.next_actions && payload.next_actions[0])
3962
+ || (payload.resume_handoff?.handoff?.next_steps && payload.resume_handoff.handoff.next_steps[0])
3963
+ || '';
3964
+ if (next) {
3965
+ process.stdout.write(next + '\n');
3966
+ process.exit(0);
3967
+ } else {
3968
+ process.exit(3);
3969
+ }
3970
+ }
3971
+
3950
3972
  // --md <path>: write the rendered markdown to a file instead of
3951
3973
  // (or in addition to) stdout. Useful for piping to a markdown
3952
3974
  // viewer or saving to the project.
@@ -4006,9 +4028,19 @@ async function runListCli() {
4006
4028
  const args = { resume: true };
4007
4029
  if (meta.repo_path) args.repo_path = meta.repo_path;
4008
4030
  if (meta.branch) args.branch = meta.branch;
4009
- const fake = { params: { name: 'retrieve_context', arguments: {} } };
4010
- await server.maybeFillProjectId(fake);
4011
- if (fake.params.arguments.project_id) args.project_id = fake.params.arguments.project_id;
4031
+ // Pass-through server-side filters when set on the CLI.
4032
+ if (flags.status) args.status_filter = flags.status;
4033
+ if (flags.since) args.since = flags.since;
4034
+ if (flags.repo_path) {
4035
+ // --repo overrides cwd → skip cwd's project_id too. Otherwise
4036
+ // tier-1 project_id_exact would short-circuit the lookup and
4037
+ // return cwd's handoff regardless of repo_path.
4038
+ args.repo_path = flags.repo_path;
4039
+ } else {
4040
+ const fake = { params: { name: 'retrieve_context', arguments: {} } };
4041
+ await server.maybeFillProjectId(fake);
4042
+ if (fake.params.arguments.project_id) args.project_id = fake.params.arguments.project_id;
4043
+ }
4012
4044
 
4013
4045
  const response = await server.makeRequest({
4014
4046
  jsonrpc: '2.0',
@@ -4025,8 +4057,10 @@ async function runListCli() {
4025
4057
 
4026
4058
  if (flags.json) {
4027
4059
  process.stdout.write(JSON.stringify({
4028
- repo_path: meta.repo_path,
4029
- branch: meta.branch,
4060
+ repo_path: args.repo_path || meta.repo_path,
4061
+ branch: args.branch || meta.branch,
4062
+ status_filter: flags.status,
4063
+ since: flags.since,
4030
4064
  count: list.length,
4031
4065
  handoffs: list,
4032
4066
  empty_hint: list.length === 0 ? payload.empty_hint : undefined,
@@ -4047,8 +4081,10 @@ async function runListCli() {
4047
4081
  const countSummary = Object.entries(counts)
4048
4082
  .map(([s, n]) => `${n} ${s}`)
4049
4083
  .join(', ');
4050
- // Render a compact table to terminal.
4051
- process.stdout.write(`Recent handoffs for ${meta.repo_path || 'this cwd'} (${countSummary}):\n\n`);
4084
+ // Render a compact table to terminal. Show the actual filter
4085
+ // applied (--repo override, or cwd default).
4086
+ const displayRepo = args.repo_path || 'this cwd';
4087
+ process.stdout.write(`Recent handoffs for ${displayRepo} (${countSummary}):\n\n`);
4052
4088
  const fmtAge = (iso) => {
4053
4089
  if (!iso) return '?';
4054
4090
  const min = Math.round((Date.now() - new Date(iso).getTime()) / 60000);
@@ -4094,22 +4130,69 @@ async function runDeleteCli() {
4094
4130
  server.logDebug = () => {};
4095
4131
  try {
4096
4132
  if (bulkMode) {
4097
- // Bulk delete path. First dry-run: query matching sessions
4098
- // count via list, then confirm with --yes for >5.
4099
4133
  const args = {};
4100
4134
  if (flags.status) args.status = flags.status;
4101
4135
  if (flags.older_than_days !== undefined) args.older_than_days = flags.older_than_days;
4102
4136
  if (flags.repo_path) args.repo_path = flags.repo_path;
4103
4137
 
4104
- // Preview: do an initial delete_session with the same filters
4105
- // to count matches via the response's deleted_session_ids
4106
- // (capped at 50). For larger counts we accept the cap as an
4107
- // approximation; the server handles all of them anyway.
4108
- // Use --yes to skip the preview/confirm prompt.
4138
+ // Dry-run preview when --yes is missing. The server returns
4139
+ // {deleted:false, deleted_count, deleted_session_ids[]} so the
4140
+ // user can see what WOULD be deleted before committing.
4109
4141
  if (!flags.yes) {
4110
- process.stderr.write(`Dry-run: previewing matches without confirm prompt is not supported yet.\n`);
4111
- process.stderr.write(`Run again with --yes to confirm: cf-memory-mcp delete ${process.argv.slice(3).join(' ')} --yes\n`);
4112
- process.exit(2);
4142
+ const previewRes = await server.makeRequest({
4143
+ jsonrpc: '2.0', id: `cli-delete-preview-${Date.now()}`,
4144
+ method: 'tools/call',
4145
+ params: { name: 'delete_session', arguments: { ...args, dry_run: true } },
4146
+ });
4147
+ const previewText = previewRes?.result?.content?.[0]?.text;
4148
+ const preview = JSON.parse(previewText || '{}');
4149
+ if (flags.json) {
4150
+ process.stdout.write(JSON.stringify({ ...preview, would_delete: true }, null, 2) + '\n');
4151
+ process.exit(2);
4152
+ }
4153
+ if (preview.deleted_count === 0) {
4154
+ process.stderr.write(`No sessions match the filters. Nothing to delete.\n`);
4155
+ process.exit(3);
4156
+ }
4157
+ process.stderr.write(`Would delete ${preview.deleted_count} session${preview.deleted_count === 1 ? '' : 's'}:\n`);
4158
+ const previewIds = preview.deleted_session_ids || [];
4159
+ for (const id of previewIds.slice(0, 10)) {
4160
+ process.stderr.write(` ${id.slice(0, 8)}\n`);
4161
+ }
4162
+ if (previewIds.length > 10) {
4163
+ process.stderr.write(` ... and ${preview.deleted_count - 10} more\n`);
4164
+ }
4165
+ // Interactive TTY: prompt for confirmation instead of
4166
+ // requiring --yes. Piped/non-TTY stdin falls through to
4167
+ // the "re-run with --yes" path (avoids hangs in scripts).
4168
+ if (process.stdin.isTTY && process.stderr.isTTY) {
4169
+ process.stderr.write(`\nProceed with deletion? [y/N] `);
4170
+ const answer = await new Promise((resolve) => {
4171
+ process.stdin.setEncoding('utf8');
4172
+ let buf = '';
4173
+ const onData = (chunk) => {
4174
+ buf += chunk;
4175
+ const nl = buf.indexOf('\n');
4176
+ if (nl !== -1) {
4177
+ process.stdin.removeListener('data', onData);
4178
+ process.stdin.pause();
4179
+ resolve(buf.slice(0, nl).trim().toLowerCase());
4180
+ }
4181
+ };
4182
+ process.stdin.on('data', onData);
4183
+ process.stdin.resume();
4184
+ });
4185
+ if (answer === 'y' || answer === 'yes') {
4186
+ // Fall through to the actual delete below.
4187
+ } else {
4188
+ process.stderr.write(`Aborted.\n`);
4189
+ process.exit(2);
4190
+ }
4191
+ } else {
4192
+ process.stderr.write(`\nRe-run with --yes to confirm:\n`);
4193
+ process.stderr.write(` cf-memory-mcp delete ${process.argv.slice(3).join(' ')} --yes\n`);
4194
+ process.exit(2);
4195
+ }
4113
4196
  }
4114
4197
 
4115
4198
  const deleteRes = await server.makeRequest({
@@ -4284,14 +4367,19 @@ complete -c cf-memory-mcp -l version -s v -d 'Show version'
4284
4367
 
4285
4368
  // Per-command help texts. Used when "cf-memory-mcp <cmd> --help" is invoked.
4286
4369
  const PER_COMMAND_HELP = {
4287
- resume: `cf-memory-mcp resume [<session-id-prefix>] [--md path] [--json]
4370
+ resume: `cf-memory-mcp resume [<session-id-prefix>] [--md path] [--next-only] [--json]
4288
4371
  Print the prior resume handoff (markdown by default).
4289
4372
  <session-id-prefix> Optional: pick a specific session (>=8 char prefix or full UUID).
4290
4373
  --md <path> Write the markdown to a file instead of stdout.
4374
+ --next-only Print just the first next_step (for shell prompts / one-liners).
4291
4375
  --json, -j Emit the full bootstrap payload as JSON for scripts.
4292
4376
  Exit codes: 0 = handoff found, 3 = no handoff found.`,
4293
- list: `cf-memory-mcp list [--limit N] [--json]
4294
- List recent handoffs for the current cwd, status-ranked.
4377
+ list: `cf-memory-mcp list [--status S] [--since ISO] [--repo PATH] [--limit N] [--json]
4378
+ List recent handoffs for the current cwd, status-ranked. Header shows
4379
+ a status-count summary.
4380
+ --status S Restrict to a given status (in_progress, completed, ...).
4381
+ --since ISO Lower bound on handoff timestamp (ISO 8601).
4382
+ --repo PATH Override the cwd's repo (peek at another repo).
4295
4383
  --limit N, -n N Max number of entries (default 5, max 50).
4296
4384
  --json, -j Emit a JSON array for scripts.`,
4297
4385
  checkpoint: `cf-memory-mcp checkpoint ["<goal>"] [--force] [--json]
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cf-memory-mcp",
3
- "version": "3.32.0",
3
+ "version": "3.34.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": {