coding-friend-cli 1.33.0 → 1.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.
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  resolveMemoryDir
3
- } from "./chunk-LK6HFD35.js";
3
+ } from "./chunk-K6EDQGDN.js";
4
4
  import {
5
5
  getLibPath
6
6
  } from "./chunk-RZRT7NGT.js";
@@ -1,3 +1,6 @@
1
+ import {
2
+ warnStaleMcpJson
3
+ } from "./chunk-AYIREX73.js";
1
4
  import {
2
5
  editMemoryAutoCapture,
3
6
  editMemoryAutoStart,
@@ -6,11 +9,11 @@ import {
6
9
  getMemoryMcpStatus,
7
10
  memoryConfigMenu,
8
11
  writeMemoryMcpEntry
9
- } from "./chunk-NZKIYVIK.js";
12
+ } from "./chunk-3UIH5U3Y.js";
10
13
  import {
11
14
  loadConfig,
12
15
  resolveMemoryDir
13
- } from "./chunk-LK6HFD35.js";
16
+ } from "./chunk-K6EDQGDN.js";
14
17
  import {
15
18
  getLibPath
16
19
  } from "./chunk-RZRT7NGT.js";
@@ -34,90 +37,17 @@ import {
34
37
  } from "./chunk-5UVDWG5L.js";
35
38
 
36
39
  // src/commands/memory.ts
37
- import { existsSync as existsSync2, readdirSync, statSync, rmSync } from "fs";
38
- import { join as join2, resolve, sep } from "path";
40
+ import { existsSync, readdirSync, statSync, rmSync } from "fs";
41
+ import { join, resolve, sep } from "path";
39
42
  import { homedir } from "os";
40
43
  import { confirm } from "@inquirer/prompts";
41
-
42
- // src/lib/mcp-state.ts
43
- import { existsSync } from "fs";
44
- import { join } from "path";
45
44
  import chalk from "chalk";
46
- function warnStaleMcpJson(memoryDir) {
47
- const localMcpPath = join(process.cwd(), ".mcp.json");
48
- if (!existsSync(localMcpPath)) return;
49
- const mcpJson = readJson(localMcpPath);
50
- if (mcpJson === null) {
51
- log.warn(
52
- ".mcp.json exists but could not be parsed \u2014 check for syntax errors."
53
- );
54
- return;
55
- }
56
- const state = detectMemoryMcpState(mcpJson, existsSync);
57
- if (state.kind === "stale" || state.kind === "legacy-valid") {
58
- if (memoryDir) {
59
- writeMemoryMcpEntry(memoryDir);
60
- if (state.kind === "stale") {
61
- log.success(
62
- "Auto-updated stale .mcp.json to version-stable npx format."
63
- );
64
- } else {
65
- log.success("Updated .mcp.json to version-stable npx format.");
66
- }
67
- console.log();
68
- } else if (state.kind === "stale") {
69
- console.log(
70
- chalk.yellow(`\u26A0 Stale MCP config detected in .mcp.json`)
71
- );
72
- console.log(chalk.dim(` Path no longer exists: ${state.path}`));
73
- console.log(
74
- chalk.dim(` Run "cf memory mcp" to update to the new format.`)
75
- );
76
- console.log();
77
- } else {
78
- console.log(
79
- chalk.cyan(
80
- `\u2139 .mcp.json uses an absolute path for coding-friend-memory.`
81
- )
82
- );
83
- console.log(
84
- chalk.dim(
85
- ` Consider running "cf memory mcp" to switch to the version-stable format.`
86
- )
87
- );
88
- console.log();
89
- }
90
- }
91
- }
92
- function detectMemoryMcpState(mcpJson, pathExists) {
93
- if (!mcpJson) return { kind: "none" };
94
- const servers = mcpJson.mcpServers;
95
- if (!servers) return { kind: "none" };
96
- const entry = servers["coding-friend-memory"];
97
- if (!entry) return { kind: "none" };
98
- const command = entry.command;
99
- if (!command) return { kind: "none" };
100
- if (command === "npx") return { kind: "npx" };
101
- if (command === "node") {
102
- const args = entry.args;
103
- const serverPath = args?.[0];
104
- if (!serverPath) return { kind: "none" };
105
- if (!pathExists(serverPath)) {
106
- return { kind: "stale", path: serverPath };
107
- }
108
- return { kind: "legacy-valid", path: serverPath };
109
- }
110
- return { kind: "none" };
111
- }
112
-
113
- // src/commands/memory.ts
114
- import chalk2 from "chalk";
115
45
  function countMdFiles(dir) {
116
- if (!existsSync2(dir)) return 0;
46
+ if (!existsSync(dir)) return 0;
117
47
  let count = 0;
118
48
  for (const entry of readdirSync(dir, { withFileTypes: true })) {
119
49
  if (entry.isDirectory()) {
120
- count += countMdFiles(join2(dir, entry.name));
50
+ count += countMdFiles(join(dir, entry.name));
121
51
  } else if (entry.name.endsWith(".md") && entry.name !== "README.md") {
122
52
  count++;
123
53
  }
@@ -137,7 +67,7 @@ function truncateError(text) {
137
67
  return [...head, ` ... (${skipped} lines omitted) ...`, ...tail].join("\n");
138
68
  }
139
69
  function ensureMemoryBuilt(mcpDir) {
140
- if (!existsSync2(join2(mcpDir, "node_modules"))) {
70
+ if (!existsSync(join(mcpDir, "node_modules"))) {
141
71
  log.step("Installing memory server dependencies (one-time setup)...");
142
72
  const result = runWithStderr("npm", ["install"], { cwd: mcpDir });
143
73
  if (result.exitCode !== 0) {
@@ -147,7 +77,7 @@ function ensureMemoryBuilt(mcpDir) {
147
77
  }
148
78
  log.success("Done.");
149
79
  }
150
- if (!existsSync2(join2(mcpDir, "dist"))) {
80
+ if (!existsSync(join(mcpDir, "dist"))) {
151
81
  log.step("Building memory server...");
152
82
  const result = runWithStderr("npm", ["run", "build"], { cwd: mcpDir });
153
83
  if (result.exitCode !== 0) {
@@ -159,7 +89,7 @@ function ensureMemoryBuilt(mcpDir) {
159
89
  }
160
90
  }
161
91
  function printMemoryMcpConfig(memoryDir) {
162
- console.log(chalk2.dim("Add this to your MCP client config:"));
92
+ console.log(chalk.dim("Add this to your MCP client config:"));
163
93
  console.log();
164
94
  console.log(`{
165
95
  "mcpServers": {
@@ -194,43 +124,43 @@ async function memoryStatusCommand() {
194
124
  const docCount = countMdFiles(memoryDir);
195
125
  const mcpDir = getLibPath("cf-memory");
196
126
  ensureMemoryBuilt(mcpDir);
197
- const { isDaemonRunning, getDaemonInfo } = await import(join2(mcpDir, "dist/daemon/process.js"));
198
- const { areSqliteDepsAvailable } = await import(join2(mcpDir, "dist/lib/lazy-install.js"));
127
+ const { isDaemonRunning, getDaemonInfo } = await import(join(mcpDir, "dist/daemon/process.js"));
128
+ const { areSqliteDepsAvailable } = await import(join(mcpDir, "dist/lib/lazy-install.js"));
199
129
  const sqliteAvailable = areSqliteDepsAvailable();
200
130
  const running = await isDaemonRunning();
201
131
  const daemonInfo = getDaemonInfo();
202
132
  let tierLabel;
203
133
  if (sqliteAvailable) {
204
- tierLabel = chalk2.cyan("Tier 1 (SQLite + Hybrid)");
134
+ tierLabel = chalk.cyan("Tier 1 (SQLite + Hybrid)");
205
135
  } else if (running) {
206
- tierLabel = chalk2.cyan("Tier 2 (MiniSearch + Daemon)");
136
+ tierLabel = chalk.cyan("Tier 2 (MiniSearch + Daemon)");
207
137
  } else {
208
- tierLabel = chalk2.cyan("Tier 3 (Markdown)");
138
+ tierLabel = chalk.cyan("Tier 3 (Markdown)");
209
139
  }
210
140
  printBanner("\u{1F9E0} Coding Friend Memory");
211
141
  console.log();
212
142
  log.info(`Tier: ${tierLabel}`);
213
- log.info(`Memory dir: ${chalk2.cyan(memoryDir)}`);
214
- log.info(`Memories in this dir: ${chalk2.green(String(docCount))}`);
143
+ log.info(`Memory dir: ${chalk.cyan(memoryDir)}`);
144
+ log.info(`Memories in this dir: ${chalk.green(String(docCount))}`);
215
145
  if (running && daemonInfo) {
216
146
  const uptime = (Date.now() - daemonInfo.startedAt) / 1e3;
217
147
  log.info(
218
- `Daemon: ${chalk2.green("running")} (PID ${daemonInfo.pid}, uptime ${formatUptime(uptime)}) ${chalk2.dim('Turn it off by "cf memory stop-daemon"')}`
148
+ `Daemon: ${chalk.green("running")} (PID ${daemonInfo.pid}, uptime ${formatUptime(uptime)}) ${chalk.dim('Turn it off by "cf memory stop-daemon"')}`
219
149
  );
220
150
  } else if (sqliteAvailable) {
221
151
  log.info(
222
- `Daemon: ${chalk2.dim("stopped")} ${chalk2.dim("(not needed \u2014 Tier 1 uses SQLite directly)")}`
152
+ `Daemon: ${chalk.dim("stopped")} ${chalk.dim("(not needed \u2014 Tier 1 uses SQLite directly)")}`
223
153
  );
224
154
  } else {
225
155
  log.info(
226
- `Daemon: ${chalk2.dim("stopped")} ${chalk2.dim('(run "cf memory start-daemon" for Tier 2 search)')}`
156
+ `Daemon: ${chalk.dim("stopped")} ${chalk.dim('(run "cf memory start-daemon" for Tier 2 search)')}`
227
157
  );
228
158
  }
229
159
  if (sqliteAvailable) {
230
- log.info(`SQLite deps: ${chalk2.green("installed")}`);
160
+ log.info(`SQLite deps: ${chalk.green("installed")}`);
231
161
  } else {
232
162
  log.info(
233
- `SQLite deps: ${chalk2.dim("not installed")} (run "cf memory init" to enable Tier 1)`
163
+ `SQLite deps: ${chalk.dim("not installed")} (run "cf memory init" to enable Tier 1)`
234
164
  );
235
165
  }
236
166
  const config = loadConfig();
@@ -238,33 +168,33 @@ async function memoryStatusCommand() {
238
168
  if (embeddingConfig?.provider || embeddingConfig?.model) {
239
169
  const provider = embeddingConfig.provider ?? "transformers";
240
170
  const model = embeddingConfig.model ?? (provider === "ollama" ? "all-minilm:l6-v2" : "Xenova/all-MiniLM-L6-v2");
241
- log.info(`Embedding: ${chalk2.cyan(model)} ${chalk2.dim(`(${provider})`)}`);
171
+ log.info(`Embedding: ${chalk.cyan(model)} ${chalk.dim(`(${provider})`)}`);
242
172
  }
243
173
  const mcpStatus = getMemoryMcpStatus();
244
174
  if (mcpStatus.configured && mcpStatus.scope === "local") {
245
175
  log.info(
246
- `MCP: ${chalk2.green("configured")} ${chalk2.dim("(local .mcp.json)")}`
176
+ `MCP: ${chalk.green("configured")} ${chalk.dim("(local .mcp.json)")}`
247
177
  );
248
178
  } else if (mcpStatus.configured && mcpStatus.scope === "global") {
249
179
  log.info(
250
- `MCP: ${chalk2.green("configured")} ${chalk2.dim("(global ~/.claude/.mcp.json)")} ${chalk2.yellow("\u26A0 global config uses a fixed path \u2014 only works for one project")}`
180
+ `MCP: ${chalk.green("configured")} ${chalk.dim("(global ~/.claude/.mcp.json)")} ${chalk.yellow("\u26A0 global config uses a fixed path \u2014 only works for one project")}`
251
181
  );
252
182
  } else {
253
183
  log.info(
254
- `MCP: ${chalk2.dim("not configured in this project")} ${chalk2.dim('(run "cf memory init" or add manually via "cf memory mcp")')}`
184
+ `MCP: ${chalk.dim("not configured in this project")} ${chalk.dim('(run "cf memory init" or add manually via "cf memory mcp")')}`
255
185
  );
256
186
  }
257
187
  const autoCapture = config.memory?.autoCapture ?? false;
258
188
  log.info(
259
- `Auto-capture: ${autoCapture ? chalk2.green("on") : chalk2.dim("off")}`
189
+ `Auto-capture: ${autoCapture ? chalk.green("on") : chalk.dim("off")}`
260
190
  );
261
- if (existsSync2(memoryDir)) {
191
+ if (existsSync(memoryDir)) {
262
192
  const categories = readdirSync(memoryDir, { withFileTypes: true }).filter((d) => d.isDirectory() && !d.name.startsWith(".")).map((d) => {
263
- const catCount = countMdFiles(join2(memoryDir, d.name));
193
+ const catCount = countMdFiles(join(memoryDir, d.name));
264
194
  return `${d.name} (${catCount})`;
265
195
  }).filter((s) => !s.endsWith("(0)"));
266
196
  if (categories.length > 0) {
267
- log.info(`Categories: ${chalk2.dim(categories.join(", "))}`);
197
+ log.info(`Categories: ${chalk.dim(categories.join(", "))}`);
268
198
  }
269
199
  }
270
200
  console.log();
@@ -273,7 +203,7 @@ async function memoryStatusCommand() {
273
203
  }
274
204
  async function memorySearchCommand(query) {
275
205
  const memoryDir = getMemoryDir();
276
- if (!existsSync2(memoryDir)) {
206
+ if (!existsSync(memoryDir)) {
277
207
  log.error(`Memory dir not found: ${memoryDir}`);
278
208
  log.dim("Run `cf init` to create project folders.");
279
209
  process.exit(1);
@@ -285,7 +215,7 @@ async function memorySearchCommand(query) {
285
215
  [
286
216
  "-e",
287
217
  `
288
- import { MarkdownBackend } from ${JSON.stringify(join2(mcpDir, "dist/backends/markdown.js"))};
218
+ import { MarkdownBackend } from ${JSON.stringify(join(mcpDir, "dist/backends/markdown.js"))};
289
219
  const backend = new MarkdownBackend(${JSON.stringify(memoryDir)});
290
220
  const results = await backend.search({ query: process.env.CF_SEARCH_QUERY, limit: 10 });
291
221
  for (const r of results) {
@@ -308,7 +238,7 @@ async function memoryListCommand(opts) {
308
238
  return memoryListProjectsCommand();
309
239
  }
310
240
  const memoryDir = getMemoryDir();
311
- if (!existsSync2(memoryDir)) {
241
+ if (!existsSync(memoryDir)) {
312
242
  log.info(`No memory directory found at: ${memoryDir}`);
313
243
  log.dim(
314
244
  "This folder has no memories yet. Use --projects to list all project databases."
@@ -325,7 +255,7 @@ async function memoryListCommand(opts) {
325
255
  [
326
256
  "-e",
327
257
  `
328
- import { MarkdownBackend } from ${JSON.stringify(join2(mcpDir, "dist/backends/markdown.js"))};
258
+ import { MarkdownBackend } from ${JSON.stringify(join(mcpDir, "dist/backends/markdown.js"))};
329
259
  const backend = new MarkdownBackend(${JSON.stringify(memoryDir)});
330
260
  const metas = await backend.list({});
331
261
  if (metas.length === 0) { console.log("No memories found."); process.exit(0); }
@@ -361,7 +291,7 @@ async function memoryStartDaemonCommand() {
361
291
  const memoryDir = getMemoryDir();
362
292
  const mcpDir = getLibPath("cf-memory");
363
293
  ensureMemoryBuilt(mcpDir);
364
- const { isDaemonRunning, getDaemonInfo, spawnDaemon } = await import(join2(mcpDir, "dist/daemon/process.js"));
294
+ const { isDaemonRunning, getDaemonInfo, spawnDaemon } = await import(join(mcpDir, "dist/daemon/process.js"));
365
295
  if (await isDaemonRunning()) {
366
296
  const info = getDaemonInfo();
367
297
  log.info(`Daemon already running (PID ${info?.pid})`);
@@ -373,7 +303,7 @@ async function memoryStartDaemonCommand() {
373
303
  const result = await spawnDaemon(memoryDir, embedding, {});
374
304
  if (result) {
375
305
  log.success(`Daemon started (PID ${result.pid})`);
376
- log.info(`Watching ${chalk2.cyan(memoryDir)} for changes`);
306
+ log.info(`Watching ${chalk.cyan(memoryDir)} for changes`);
377
307
  } else {
378
308
  log.error("Daemon did not start within 3 seconds");
379
309
  process.exit(1);
@@ -382,7 +312,7 @@ async function memoryStartDaemonCommand() {
382
312
  async function memoryStopDaemonCommand() {
383
313
  const mcpDir = getLibPath("cf-memory");
384
314
  ensureMemoryBuilt(mcpDir);
385
- const { stopDaemon, isDaemonRunning } = await import(join2(mcpDir, "dist/daemon/process.js"));
315
+ const { stopDaemon, isDaemonRunning } = await import(join(mcpDir, "dist/daemon/process.js"));
386
316
  if (!await isDaemonRunning()) {
387
317
  log.info("Daemon is not running.");
388
318
  return;
@@ -399,10 +329,10 @@ async function memoryRebuildCommand() {
399
329
  const memoryDir = getMemoryDir();
400
330
  const mcpDir = getLibPath("cf-memory");
401
331
  ensureMemoryBuilt(mcpDir);
402
- const { areSqliteDepsAvailable } = await import(join2(mcpDir, "dist/lib/lazy-install.js"));
332
+ const { areSqliteDepsAvailable } = await import(join(mcpDir, "dist/lib/lazy-install.js"));
403
333
  if (areSqliteDepsAvailable()) {
404
334
  log.step("Rebuilding SQLite index + embeddings...");
405
- const { SqliteBackend } = await import(join2(mcpDir, "dist/backends/sqlite/index.js"));
335
+ const { SqliteBackend } = await import(join(mcpDir, "dist/backends/sqlite/index.js"));
406
336
  const config = loadConfig();
407
337
  const embedding = config.memory?.embedding;
408
338
  const opts = embedding ? { embedding, skipVec: false } : { skipVec: false };
@@ -412,7 +342,7 @@ async function memoryRebuildCommand() {
412
342
  const stats = await backend.stats();
413
343
  log.success(`Rebuilt: ${stats.total} memories indexed.`);
414
344
  if (backend.isVecEnabled()) {
415
- log.info(`Vector search: ${chalk2.green("enabled")}`);
345
+ log.info(`Vector search: ${chalk.green("enabled")}`);
416
346
  }
417
347
  if (!backend.isRebuildNeeded()) {
418
348
  log.info("Embedding dimensions: up to date");
@@ -422,14 +352,14 @@ async function memoryRebuildCommand() {
422
352
  }
423
353
  return;
424
354
  }
425
- const { isDaemonRunning, getDaemonPaths } = await import(join2(mcpDir, "dist/daemon/process.js"));
355
+ const { isDaemonRunning, getDaemonPaths } = await import(join(mcpDir, "dist/daemon/process.js"));
426
356
  if (!await isDaemonRunning()) {
427
357
  log.info("No SQLite deps and daemon not running. Nothing to rebuild.");
428
358
  log.dim("Install Tier 1 deps: cf memory init");
429
359
  log.dim("Or start the daemon: cf memory start-daemon");
430
360
  return;
431
361
  }
432
- const { DaemonClient } = await import(join2(mcpDir, "dist/lib/daemon-client.js"));
362
+ const { DaemonClient } = await import(join(mcpDir, "dist/lib/daemon-client.js"));
433
363
  const paths = getDaemonPaths();
434
364
  const client = new DaemonClient(paths.socketPath);
435
365
  log.step("Rebuilding search index via daemon...");
@@ -446,7 +376,7 @@ function getDbPath(memoryDir) {
446
376
  const stripped = resolved.replace(/\/docs\/memory$/, "").replace(/\/memory$/, "");
447
377
  const id = stripped.replace(/\//g, "-");
448
378
  const home = homedir();
449
- const dbPath = join2(
379
+ const dbPath = join(
450
380
  home,
451
381
  ".coding-friend",
452
382
  "memory",
@@ -454,7 +384,7 @@ function getDbPath(memoryDir) {
454
384
  id,
455
385
  "db.sqlite"
456
386
  );
457
- return existsSync2(dbPath) ? dbPath : null;
387
+ return existsSync(dbPath) ? dbPath : null;
458
388
  } catch {
459
389
  return null;
460
390
  }
@@ -463,7 +393,7 @@ async function ensureSqliteDepsIfNeeded(mcpDir) {
463
393
  const config = loadConfig();
464
394
  const tier = config.memory?.tier ?? "auto";
465
395
  if (tier === "markdown" || tier === "lite") return true;
466
- const { ensureDeps, areSqliteDepsAvailable } = await import(join2(mcpDir, "dist/lib/lazy-install.js"));
396
+ const { ensureDeps, areSqliteDepsAvailable } = await import(join(mcpDir, "dist/lib/lazy-install.js"));
467
397
  if (areSqliteDepsAvailable()) return true;
468
398
  log.step("Installing SQLite dependencies...");
469
399
  const installed = await ensureDeps({
@@ -534,7 +464,7 @@ async function memoryInitWizard(memoryDir, mcpDir) {
534
464
  }
535
465
  const depsOk = await ensureSqliteDepsIfNeeded(mcpDir);
536
466
  if (!depsOk) return;
537
- if (!existsSync2(memoryDir)) {
467
+ if (!existsSync(memoryDir)) {
538
468
  log.info(
539
469
  "No memory directory found. Memories will be indexed as they're created."
540
470
  );
@@ -550,7 +480,7 @@ async function memoryInitWizard(memoryDir, mcpDir) {
550
480
  return;
551
481
  }
552
482
  log.step(`Importing ${docCount} existing memories into SQLite...`);
553
- const { SqliteBackend } = await import(join2(mcpDir, "dist/backends/sqlite/index.js"));
483
+ const { SqliteBackend } = await import(join(mcpDir, "dist/backends/sqlite/index.js"));
554
484
  const embedding = config.memory?.embedding;
555
485
  const backend = new SqliteBackend(memoryDir, {
556
486
  skipVec: false,
@@ -561,7 +491,7 @@ async function memoryInitWizard(memoryDir, mcpDir) {
561
491
  const stats = await backend.stats();
562
492
  log.success(`Imported ${stats.total} memories. DB: ${backend.getDbPath()}`);
563
493
  log.info(
564
- `Vector search: ${backend.isVecEnabled() ? chalk2.green("enabled") : chalk2.dim("disabled (sqlite-vec not available)")}`
494
+ `Vector search: ${backend.isVecEnabled() ? chalk.green("enabled") : chalk.dim("disabled (sqlite-vec not available)")}`
565
495
  );
566
496
  } finally {
567
497
  await backend.close();
@@ -570,13 +500,13 @@ async function memoryInitWizard(memoryDir, mcpDir) {
570
500
  console.log();
571
501
  log.success('Memory initialized! Run "cf memory status" to verify.');
572
502
  log.info(
573
- `Tip: Run ${chalk2.cyan("/cf-scan")} in Claude Code to populate memory with project knowledge.`
503
+ `Tip: Run ${chalk.cyan("/cf-scan")} in Claude Code to populate memory with project knowledge.`
574
504
  );
575
505
  }
576
506
  async function setupMemoryMcp(memoryDir, _mcpDir) {
577
507
  const mcpStatus = getMemoryMcpStatus();
578
508
  if (mcpStatus.configured && mcpStatus.scope === "local") {
579
- log.info(`MCP: ${chalk2.green("already configured")} in .mcp.json`);
509
+ log.info(`MCP: ${chalk.green("already configured")} in .mcp.json`);
580
510
  return;
581
511
  }
582
512
  console.log();
@@ -625,7 +555,7 @@ async function memoryConfigCommand() {
625
555
  }
626
556
  function getProjectsBaseDir() {
627
557
  const home = homedir();
628
- return join2(home, ".coding-friend", "memory", "projects");
558
+ return join(home, ".coding-friend", "memory", "projects");
629
559
  }
630
560
  function formatSize(bytes) {
631
561
  if (bytes < 1024) return `${bytes} B`;
@@ -643,9 +573,9 @@ function formatDate(raw) {
643
573
  }
644
574
  function dirSize(dir) {
645
575
  let total = 0;
646
- if (!existsSync2(dir)) return 0;
576
+ if (!existsSync(dir)) return 0;
647
577
  for (const entry of readdirSync(dir, { withFileTypes: true })) {
648
- const p = join2(dir, entry.name);
578
+ const p = join(dir, entry.name);
649
579
  if (entry.isFile()) {
650
580
  total += statSync(p).size;
651
581
  } else if (entry.isDirectory()) {
@@ -655,7 +585,7 @@ function dirSize(dir) {
655
585
  return total;
656
586
  }
657
587
  function getProjectInfo(projectDir, projectId, mcpDir, knownSourceDir) {
658
- const dbPath = join2(projectDir, "db.sqlite");
588
+ const dbPath = join(projectDir, "db.sqlite");
659
589
  const size = dirSize(projectDir);
660
590
  const info = {
661
591
  id: projectId,
@@ -664,7 +594,7 @@ function getProjectInfo(projectDir, projectId, mcpDir, knownSourceDir) {
664
594
  size,
665
595
  lastUpdated: null
666
596
  };
667
- if (!existsSync2(dbPath)) return info;
597
+ if (!existsSync(dbPath)) return info;
668
598
  try {
669
599
  const backfillDir = knownSourceDir ? JSON.stringify(knownSourceDir) : "null";
670
600
  const result = run(
@@ -705,7 +635,7 @@ async function memoryListProjectsCommand() {
705
635
  const baseDir = getProjectsBaseDir();
706
636
  const mcpDir = getLibPath("cf-memory");
707
637
  ensureMemoryBuilt(mcpDir);
708
- if (!existsSync2(baseDir)) {
638
+ if (!existsSync(baseDir)) {
709
639
  log.info("No memory projects found.");
710
640
  log.dim('Run "cf memory init" in a project to create one.');
711
641
  return;
@@ -716,35 +646,35 @@ async function memoryListProjectsCommand() {
716
646
  return;
717
647
  }
718
648
  const currentMemoryDir = resolve(getMemoryDir());
719
- const { projectId } = await import(join2(mcpDir, "dist/backends/sqlite/index.js"));
649
+ const { projectId } = await import(join(mcpDir, "dist/backends/sqlite/index.js"));
720
650
  const currentProjectId = projectId(currentMemoryDir);
721
651
  log.step(`Scanning ${dirs.length} project(s)...
722
652
  `);
723
653
  const projects = [];
724
654
  for (const id of dirs) {
725
655
  const knownDir = id === currentProjectId ? currentMemoryDir : void 0;
726
- projects.push(getProjectInfo(join2(baseDir, id), id, mcpDir, knownDir));
656
+ projects.push(getProjectInfo(join(baseDir, id), id, mcpDir, knownDir));
727
657
  }
728
658
  projects.sort((a, b) => b.size - a.size);
729
659
  const totalSize = projects.reduce((sum, p) => sum + p.size, 0);
730
660
  const idxW = String(projects.length).length;
731
661
  const header = `${"#".padStart(idxW)} ${"SIZE".padStart(10)} ${"MEMS".padStart(4)} ${"PROJECT ID".padEnd(12)} ${"UPDATED".padEnd(16)} PATH`;
732
- console.log(chalk2.bold(header));
733
- console.log(chalk2.dim("-".repeat(header.length + 10)));
662
+ console.log(chalk.bold(header));
663
+ console.log(chalk.dim("-".repeat(header.length + 10)));
734
664
  projects.forEach((p, i) => {
735
- const idx = chalk2.dim(String(i + 1).padStart(idxW));
736
- const sizeStr = chalk2.yellow(formatSize(p.size).padStart(10));
737
- const memCount = chalk2.green(String(p.memories).padStart(4));
738
- const idStr = chalk2.cyan(p.id.padEnd(12));
739
- const dateStr = p.lastUpdated ? formatDate(p.lastUpdated).padEnd(16) : chalk2.dim("n/a".padEnd(16));
740
- const pathStr = p.sourceDir ? chalk2.dim(p.sourceDir) : chalk2.dim("(unknown)");
665
+ const idx = chalk.dim(String(i + 1).padStart(idxW));
666
+ const sizeStr = chalk.yellow(formatSize(p.size).padStart(10));
667
+ const memCount = chalk.green(String(p.memories).padStart(4));
668
+ const idStr = chalk.cyan(p.id.padEnd(12));
669
+ const dateStr = p.lastUpdated ? formatDate(p.lastUpdated).padEnd(16) : chalk.dim("n/a".padEnd(16));
670
+ const pathStr = p.sourceDir ? chalk.dim(p.sourceDir) : chalk.dim("(unknown)");
741
671
  console.log(
742
672
  `${idx} ${sizeStr} ${memCount} ${idStr} ${dateStr} ${pathStr}`
743
673
  );
744
674
  });
745
675
  console.log();
746
676
  console.log(
747
- chalk2.bold(
677
+ chalk.bold(
748
678
  `Total: ${projects.length} project(s), ${formatSize(totalSize)}`
749
679
  )
750
680
  );
@@ -752,7 +682,7 @@ async function memoryListProjectsCommand() {
752
682
  }
753
683
  async function memoryRmCommand(opts) {
754
684
  const baseDir = getProjectsBaseDir();
755
- if (!existsSync2(baseDir)) {
685
+ if (!existsSync(baseDir)) {
756
686
  log.info("No memory projects found. Nothing to remove.");
757
687
  return;
758
688
  }
@@ -762,9 +692,9 @@ async function memoryRmCommand(opts) {
762
692
  const dirs = readdirSync(baseDir, { withFileTypes: true }).filter((d) => d.isDirectory() && !d.name.startsWith(".")).map((d) => d.name);
763
693
  const orphaned = [];
764
694
  for (const id of dirs) {
765
- const projectDir = join2(baseDir, id);
695
+ const projectDir = join(baseDir, id);
766
696
  const info = getProjectInfo(projectDir, id, mcpDir);
767
- if (info.sourceDir && !existsSync2(info.sourceDir)) {
697
+ if (info.sourceDir && !existsSync(info.sourceDir)) {
768
698
  orphaned.push({
769
699
  id,
770
700
  reason: `source dir missing: ${info.sourceDir}`,
@@ -788,7 +718,7 @@ async function memoryRmCommand(opts) {
788
718
  );
789
719
  console.log();
790
720
  for (const o of orphaned) {
791
- console.log(` ${chalk2.cyan(o.id)} ${chalk2.dim(o.reason)}`);
721
+ console.log(` ${chalk.cyan(o.id)} ${chalk.dim(o.reason)}`);
792
722
  }
793
723
  console.log();
794
724
  const ok = await confirm({
@@ -800,7 +730,7 @@ async function memoryRmCommand(opts) {
800
730
  return;
801
731
  }
802
732
  for (const o of orphaned) {
803
- rmSync(join2(baseDir, o.id), { recursive: true, force: true });
733
+ rmSync(join(baseDir, o.id), { recursive: true, force: true });
804
734
  }
805
735
  log.success(
806
736
  `Deleted ${orphaned.length} orphaned project(s) (${formatSize(totalSize)}).`
@@ -816,7 +746,7 @@ async function memoryRmCommand(opts) {
816
746
  return;
817
747
  }
818
748
  const totalSize = dirs.reduce(
819
- (sum, d) => sum + dirSize(join2(baseDir, d.name)),
749
+ (sum, d) => sum + dirSize(join(baseDir, d.name)),
820
750
  0
821
751
  );
822
752
  log.warn(
@@ -833,19 +763,19 @@ async function memoryRmCommand(opts) {
833
763
  return;
834
764
  }
835
765
  for (const d of dirs) {
836
- rmSync(join2(baseDir, d.name), { recursive: true, force: true });
766
+ rmSync(join(baseDir, d.name), { recursive: true, force: true });
837
767
  }
838
768
  log.success(`Deleted ${dirs.length} project database(s).`);
839
769
  return;
840
770
  }
841
771
  if (opts.projectId) {
842
- const projectDir = join2(baseDir, opts.projectId);
772
+ const projectDir = join(baseDir, opts.projectId);
843
773
  const resolved = resolve(projectDir);
844
774
  if (!resolved.startsWith(resolve(baseDir) + sep)) {
845
775
  log.error("Invalid project ID.");
846
776
  process.exit(1);
847
777
  }
848
- if (!existsSync2(projectDir)) {
778
+ if (!existsSync(projectDir)) {
849
779
  log.error(`Project "${opts.projectId}" not found.`);
850
780
  log.dim('Run "cf memory list --projects" to see available projects.');
851
781
  process.exit(1);
@@ -881,8 +811,6 @@ async function memoryMcpCommand() {
881
811
  }
882
812
 
883
813
  export {
884
- warnStaleMcpJson,
885
- detectMemoryMcpState,
886
814
  ensureMemoryBuilt,
887
815
  printMemoryMcpConfig,
888
816
  memoryStatusCommand,
@@ -0,0 +1,85 @@
1
+ import {
2
+ writeMemoryMcpEntry
3
+ } from "./chunk-3UIH5U3Y.js";
4
+ import {
5
+ log
6
+ } from "./chunk-NREZK463.js";
7
+ import {
8
+ readJson
9
+ } from "./chunk-5UVDWG5L.js";
10
+
11
+ // src/lib/mcp-state.ts
12
+ import { existsSync } from "fs";
13
+ import { join } from "path";
14
+ import chalk from "chalk";
15
+ function warnStaleMcpJson(memoryDir) {
16
+ const localMcpPath = join(process.cwd(), ".mcp.json");
17
+ if (!existsSync(localMcpPath)) return;
18
+ const mcpJson = readJson(localMcpPath);
19
+ if (mcpJson === null) {
20
+ log.warn(
21
+ ".mcp.json exists but could not be parsed \u2014 check for syntax errors."
22
+ );
23
+ return;
24
+ }
25
+ const state = detectMemoryMcpState(mcpJson, existsSync);
26
+ if (state.kind === "stale" || state.kind === "legacy-valid") {
27
+ if (memoryDir) {
28
+ writeMemoryMcpEntry(memoryDir);
29
+ if (state.kind === "stale") {
30
+ log.success(
31
+ "Auto-updated stale .mcp.json to version-stable npx format."
32
+ );
33
+ } else {
34
+ log.success("Updated .mcp.json to version-stable npx format.");
35
+ }
36
+ console.log();
37
+ } else if (state.kind === "stale") {
38
+ console.log(
39
+ chalk.yellow(`\u26A0 Stale MCP config detected in .mcp.json`)
40
+ );
41
+ console.log(chalk.dim(` Path no longer exists: ${state.path}`));
42
+ console.log(
43
+ chalk.dim(` Run "cf memory mcp" to update to the new format.`)
44
+ );
45
+ console.log();
46
+ } else {
47
+ console.log(
48
+ chalk.cyan(
49
+ `\u2139 .mcp.json uses an absolute path for coding-friend-memory.`
50
+ )
51
+ );
52
+ console.log(
53
+ chalk.dim(
54
+ ` Consider running "cf memory mcp" to switch to the version-stable format.`
55
+ )
56
+ );
57
+ console.log();
58
+ }
59
+ }
60
+ }
61
+ function detectMemoryMcpState(mcpJson, pathExists) {
62
+ if (!mcpJson) return { kind: "none" };
63
+ const servers = mcpJson.mcpServers;
64
+ if (!servers) return { kind: "none" };
65
+ const entry = servers["coding-friend-memory"];
66
+ if (!entry) return { kind: "none" };
67
+ const command = entry.command;
68
+ if (!command) return { kind: "none" };
69
+ if (command === "npx") return { kind: "npx" };
70
+ if (command === "node") {
71
+ const args = entry.args;
72
+ const serverPath = args?.[0];
73
+ if (!serverPath) return { kind: "none" };
74
+ if (!pathExists(serverPath)) {
75
+ return { kind: "stale", path: serverPath };
76
+ }
77
+ return { kind: "legacy-valid", path: serverPath };
78
+ }
79
+ return { kind: "none" };
80
+ }
81
+
82
+ export {
83
+ warnStaleMcpJson,
84
+ detectMemoryMcpState
85
+ };