coding-friend-cli 1.36.3 → 1.37.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 (28) hide show
  1. package/README.md +1 -1
  2. package/dist/{chunk-57EGAXYI.js → chunk-3WIZA5OZ.js} +4 -0
  3. package/dist/{chunk-EAJJVXAE.js → chunk-676YCW6T.js} +18 -2
  4. package/dist/{chunk-MAGBNYZK.js → chunk-6S7352JG.js} +60 -47
  5. package/dist/{chunk-VV2U7NDN.js → chunk-DP6DFQ4G.js} +83 -35
  6. package/dist/{chunk-CDTQL4FI.js → chunk-PI4P34KF.js} +41 -2
  7. package/dist/{chunk-TPGAW67X.js → chunk-W6D3J5DX.js} +39 -27
  8. package/dist/{chunk-7SMP5CZ4.js → chunk-XEKQW4RN.js} +1 -1
  9. package/dist/{clean-MXIMMXDH.js → clean-WEYQZB34.js} +3 -2
  10. package/dist/{config-B2P7TXL4.js → config-ZMR2AOBB.js} +10 -10
  11. package/dist/{dev-GKD52XE6.js → dev-IGHMMEP4.js} +2 -2
  12. package/dist/{host-PEKK7OOC.js → host-SSRI4JZV.js} +2 -2
  13. package/dist/index.js +34 -32
  14. package/dist/{init-M5JJP5YO.js → init-6MFQWPOP.js} +30 -15
  15. package/dist/{install-VFMO45SP.js → install-37NN4CQH.js} +5 -3
  16. package/dist/{learn-QQ7XEHDY.js → learn-OPH3CI67.js} +2 -2
  17. package/dist/{mcp-372LGOZJ.js → mcp-DWKVMNC7.js} +21 -21
  18. package/dist/{mcp-serve-3FEPFVVQ.js → mcp-serve-5GODJ4DJ.js} +10 -1
  19. package/dist/{memory-XINRMEWB.js → memory-TRDLADUM.js} +5 -5
  20. package/dist/{session-2R7WWNML.js → session-S4WKA4P5.js} +3 -2
  21. package/dist/{status-Z4V7IYOB.js → status-YZEOUX46.js} +13 -10
  22. package/dist/{statusline-WDTN3OI4.js → statusline-KKFJPTI7.js} +2 -2
  23. package/dist/{uninstall-ASLHT3MH.js → uninstall-AVA3J554.js} +14 -0
  24. package/dist/{update-MY2DRJLV.js → update-Q3ACCK4Z.js} +5 -3
  25. package/lib/cf-memory/src/__tests__/sqlite-lazy-db.test.ts +213 -0
  26. package/lib/cf-memory/src/__tests__/tier.test.ts +22 -1
  27. package/lib/cf-memory/src/backends/sqlite/index.ts +142 -85
  28. package/package.json +1 -1
package/README.md CHANGED
@@ -17,7 +17,7 @@ Everything else (every skill, every agent, every hook except the three OPTIONAL
17
17
  - **`node` not found** — install Node.js 20+ from <https://nodejs.org> (the CLI declares `engines.node >= 20`; older versions will refuse to install).
18
18
  - **`EACCES` on global install** — set a user-writable global prefix: `npm config set prefix ~/.npm-global` and add `~/.npm-global/bin` to your `PATH`.
19
19
  - **`cf` not on PATH after install** — verify with `npm root -g` and ensure that directory's `bin/` is in your `PATH`.
20
- - **MCP server not appearing in Claude Code** — after install, run `cf mcp` to register the memory MCP, then restart Claude Code.
20
+ - **MCP server not appearing in Claude Code** — after install, run `cf mcp` to register both Learn and Memory MCP at user scope, then restart Claude Code.
21
21
 
22
22
  ## Requirements
23
23
 
@@ -13,6 +13,10 @@ var STATUSLINE_COMPONENTS = [
13
13
  id: "rate_limit",
14
14
  label: "Rate limit \u2014 current & weekly usage (requires curl & jq)"
15
15
  },
16
+ {
17
+ id: "session",
18
+ label: "Claude Code session ID (\u{1F194} a1b2c3d4-\u2026)"
19
+ },
16
20
  {
17
21
  id: "task_agent",
18
22
  label: "Task progress & active agent (Tasks: 2/5 | Agent: cf-reviewer)"
@@ -1,10 +1,15 @@
1
1
  import {
2
2
  ensureStatusline,
3
3
  getInstalledVersion
4
- } from "./chunk-7SMP5CZ4.js";
4
+ } from "./chunk-XEKQW4RN.js";
5
5
  import {
6
6
  ensureShellCompletion
7
7
  } from "./chunk-667NMPN4.js";
8
+ import {
9
+ isMemoryMcpRegistered,
10
+ registerMemoryMcp,
11
+ removeMemoryMcpEntry
12
+ } from "./chunk-DP6DFQ4G.js";
8
13
  import {
9
14
  resolveScope
10
15
  } from "./chunk-IKLMHJH6.js";
@@ -249,9 +254,20 @@ async function updateCommand(opts) {
249
254
  }
250
255
  }
251
256
  if (doCli) {
252
- const { refreshMemoryAfterUpdate } = await import("./memory-XINRMEWB.js");
257
+ const { refreshMemoryAfterUpdate } = await import("./memory-TRDLADUM.js");
253
258
  await refreshMemoryAfterUpdate();
254
259
  }
260
+ if (!isMemoryMcpRegistered()) {
261
+ registerMemoryMcp();
262
+ }
263
+ const migrationResult = removeMemoryMcpEntry();
264
+ if (migrationResult.removed) {
265
+ if (migrationResult.fileDeleted) {
266
+ log.success("Removed legacy project-scope coding-friend-memory from .mcp.json (file deleted \u2014 was empty).");
267
+ } else {
268
+ log.success("Removed legacy project-scope coding-friend-memory from .mcp.json");
269
+ }
270
+ }
255
271
  if (doStatusline) {
256
272
  const updatedVersion = ensureStatusline();
257
273
  if (updatedVersion) {
@@ -1,6 +1,6 @@
1
1
  import {
2
- writeMemoryMcpEntry
3
- } from "./chunk-VV2U7NDN.js";
2
+ isMemoryMcpRegistered
3
+ } from "./chunk-DP6DFQ4G.js";
4
4
  import {
5
5
  readJson
6
6
  } from "./chunk-5UVDWG5L.js";
@@ -12,7 +12,7 @@ import {
12
12
  import { existsSync } from "fs";
13
13
  import { join } from "path";
14
14
  import chalk from "chalk";
15
- function warnStaleMcpJson(memoryDir) {
15
+ function warnStaleMcpJson() {
16
16
  const localMcpPath = join(process.cwd(), ".mcp.json");
17
17
  if (!existsSync(localMcpPath)) return;
18
18
  const mcpJson = readJson(localMcpPath);
@@ -23,39 +23,35 @@ function warnStaleMcpJson(memoryDir) {
23
23
  return;
24
24
  }
25
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
- }
26
+ if (state.kind === "stale") {
27
+ console.log(
28
+ chalk.yellow(`\u26A0 Stale MCP config detected in .mcp.json`)
29
+ );
30
+ console.log(chalk.dim(` Path no longer exists: ${state.path}`));
31
+ console.log(
32
+ chalk.dim(
33
+ ` This project-scope coding-friend-memory entry shadows the global (user-scope) memory server.`
34
+ )
35
+ );
36
+ console.log(
37
+ chalk.dim(` Run "cf update" to remove it automatically.`)
38
+ );
39
+ console.log();
40
+ } else if (state.kind === "legacy-valid") {
41
+ console.log(
42
+ chalk.cyan(
43
+ `\u2139 .mcp.json has a project-scope coding-friend-memory entry.`
44
+ )
45
+ );
46
+ console.log(
47
+ chalk.dim(
48
+ ` This shadows the global (user-scope) memory server (Claude Code precedence: local > project > user).`
49
+ )
50
+ );
51
+ console.log(
52
+ chalk.dim(` Run "cf update" to remove it automatically.`)
53
+ );
54
+ console.log();
59
55
  }
60
56
  }
61
57
  function detectMemoryMcpState(mcpJson, pathExists) {
@@ -102,24 +98,41 @@ function printHealthSection(result) {
102
98
  }
103
99
  async function checkMemoryMcpHealth(deps) {
104
100
  const checks = [];
101
+ const checkRegistered = deps.isRegistered ?? isMemoryMcpRegistered;
102
+ const registered = checkRegistered();
103
+ checks.push({
104
+ label: "Registered (user scope)",
105
+ ok: registered,
106
+ ...registered ? {} : {
107
+ detail: "coding-friend-memory not registered",
108
+ fix: 'Run "cf mcp" to register'
109
+ }
110
+ });
105
111
  const mcpJson = deps.readMcpJson();
106
112
  const state = detectMemoryMcpState(mcpJson, deps.pathExists);
107
- if (state.kind === "npx") {
108
- checks.push({ label: "Config (.mcp.json)", ok: true });
109
- } else if (state.kind === "none") {
113
+ if (state.kind === "stale") {
114
+ checks.push({
115
+ label: "Project .mcp.json",
116
+ ok: false,
117
+ warn: true,
118
+ detail: `Stale entry shadows user-scope server (path missing: ${state.path})`,
119
+ fix: 'Run "cf update" to remove it automatically'
120
+ });
121
+ } else if (state.kind === "legacy-valid") {
110
122
  checks.push({
111
- label: "Config (.mcp.json)",
123
+ label: "Project .mcp.json",
112
124
  ok: false,
113
- detail: "coding-friend-memory not configured",
114
- fix: 'Run "cf memory mcp" to add the MCP entry'
125
+ warn: true,
126
+ detail: `Legacy absolute-path entry shadows user-scope server`,
127
+ fix: 'Run "cf update" to migrate to the user-scope registration'
115
128
  });
116
- } else {
117
- const detail = state.kind === "stale" ? `Stale path: ${state.path}` : `Absolute path (legacy): ${state.path}`;
129
+ } else if (state.kind === "npx") {
118
130
  checks.push({
119
- label: "Config (.mcp.json)",
131
+ label: "Project .mcp.json",
120
132
  ok: false,
121
- detail,
122
- fix: 'Run "cf memory mcp" to update to the npx format'
133
+ warn: true,
134
+ detail: "Project-scope npx entry shadows user-scope server",
135
+ fix: 'Run "cf update" to remove it automatically'
123
136
  });
124
137
  }
125
138
  const distExists = deps.pathExists(deps.memoryDistPath);
@@ -1,6 +1,3 @@
1
- import {
2
- resolveMemoryDir
3
- } from "./chunk-CDTQL4FI.js";
4
1
  import {
5
2
  getLibPath
6
3
  } from "./chunk-RZRT7NGT.js";
@@ -10,6 +7,9 @@ import {
10
7
  formatScopeLabel,
11
8
  injectBackChoice
12
9
  } from "./chunk-IKLMHJH6.js";
10
+ import {
11
+ runWithStderr
12
+ } from "./chunk-EVGXUDX4.js";
13
13
  import {
14
14
  mergeJson,
15
15
  readJson,
@@ -24,10 +24,44 @@ import {
24
24
  log
25
25
  } from "./chunk-NREZK463.js";
26
26
 
27
+ // src/lib/memory-mcp-register.ts
28
+ var MCP_NAME = "coding-friend-memory";
29
+ function registerMemoryMcp() {
30
+ const result = runWithStderr("claude", [
31
+ "mcp",
32
+ "add",
33
+ "--scope",
34
+ "user",
35
+ MCP_NAME,
36
+ "--",
37
+ "npx",
38
+ "-y",
39
+ "coding-friend-cli",
40
+ "mcp-serve"
41
+ ]);
42
+ if (result.exitCode !== 0) {
43
+ const stderr = result.stderr ?? "";
44
+ if (stderr.includes("ENOENT") || stderr.includes("command not found")) {
45
+ log.warn(
46
+ `claude CLI not found \u2014 add MCP manually:
47
+ claude mcp add --scope user ${MCP_NAME} -- npx -y coding-friend-cli mcp-serve`
48
+ );
49
+ } else {
50
+ log.warn(`Could not register MCP: ${stderr || "unknown error"}`);
51
+ }
52
+ return false;
53
+ }
54
+ return true;
55
+ }
56
+ function isMemoryMcpRegistered() {
57
+ const result = runWithStderr("claude", ["mcp", "get", MCP_NAME]);
58
+ return result.exitCode === 0;
59
+ }
60
+
27
61
  // src/lib/memory-prompts.ts
28
62
  import { confirm, input, select } from "@inquirer/prompts";
63
+ import { rmSync } from "fs";
29
64
  import chalk from "chalk";
30
- import { homedir } from "os";
31
65
  import { join } from "path";
32
66
  function getMemoryFieldScope(field, globalCfg, localCfg) {
33
67
  const localSection = localCfg?.memory;
@@ -216,54 +250,66 @@ async function editMemoryEmbedding(globalCfg, localCfg) {
216
250
  if (ollamaUrl) embedding.ollamaUrl = ollamaUrl;
217
251
  writeMemoryField(scope, "embedding", embedding);
218
252
  }
219
- function writeMemoryMcpEntry(memoryDir) {
253
+ function removeMemoryMcpEntry() {
220
254
  const mcpPath = join(process.cwd(), ".mcp.json");
221
- const existing = readJson(mcpPath) ?? {};
222
- const servers = existing.mcpServers ?? {};
223
- writeJson(mcpPath, {
224
- ...existing,
225
- mcpServers: {
226
- ...servers,
227
- "coding-friend-memory": {
228
- command: "npx",
229
- args: ["-y", "coding-friend-cli", "mcp-serve", memoryDir]
230
- }
231
- }
232
- });
233
- log.success("Added coding-friend-memory to .mcp.json");
255
+ const existing = readJson(mcpPath);
256
+ if (existing == null) {
257
+ return { removed: false, fileDeleted: false };
258
+ }
259
+ const servers = existing.mcpServers;
260
+ if (servers == null || typeof servers !== "object" || Array.isArray(servers) || !("coding-friend-memory" in servers)) {
261
+ return { removed: false, fileDeleted: false };
262
+ }
263
+ delete servers["coding-friend-memory"];
264
+ if (Object.keys(servers).length === 0) {
265
+ delete existing.mcpServers;
266
+ }
267
+ if (Object.keys(existing).length === 0) {
268
+ rmSync(mcpPath, { force: true });
269
+ return { removed: true, fileDeleted: true };
270
+ }
271
+ writeJson(mcpPath, existing);
272
+ return { removed: true, fileDeleted: false };
234
273
  }
235
- function getMemoryMcpStatus() {
274
+ function getMemoryMcpStatus(checkIsRegistered = isMemoryMcpRegistered) {
236
275
  const localMcpPath = join(process.cwd(), ".mcp.json");
237
276
  const localMcp = readJson(localMcpPath);
238
277
  const localServers = localMcp?.mcpServers;
278
+ const userScope = checkIsRegistered();
279
+ if (userScope) {
280
+ const hasShadow = localServers != null && "coding-friend-memory" in localServers;
281
+ return {
282
+ configured: true,
283
+ scope: hasShadow ? "local" : "user",
284
+ userScope: true
285
+ };
286
+ }
239
287
  if (localServers != null && "coding-friend-memory" in localServers) {
240
- return { configured: true, scope: "local" };
288
+ return { configured: true, scope: "local", userScope: false };
241
289
  }
242
290
  const globalMcpPath = join(claudeConfigDir(), ".mcp.json");
243
291
  const globalMcp = readJson(globalMcpPath);
244
292
  const globalServers = globalMcp?.mcpServers;
245
293
  if (globalServers != null && "coding-friend-memory" in globalServers) {
246
- return { configured: true, scope: "global" };
294
+ return { configured: true, scope: "global", userScope: false };
247
295
  }
248
- return { configured: false, scope: null };
296
+ return { configured: false, scope: null, userScope: false };
249
297
  }
250
298
  async function editMemoryMcp() {
251
- const status = getMemoryMcpStatus();
252
- if (status.configured) {
253
- const globalMcpDisplay = join(claudeConfigDir(), ".mcp.json").replace(
254
- homedir(),
255
- "~"
256
- );
257
- const label = status.scope === "local" ? chalk.green("configured") + chalk.dim(" (local .mcp.json)") : chalk.green("configured") + chalk.dim(` (global ${globalMcpDisplay})`) + " " + chalk.yellow("\u26A0 only works for one project");
258
- log.info(`MCP: ${label}`);
299
+ if (isMemoryMcpRegistered()) {
300
+ log.info(`MCP: ${chalk.green("registered")} (user scope)`);
259
301
  const reconfigure = await confirm({
260
- message: "Reconfigure Memory MCP in local .mcp.json?",
302
+ message: "Re-register Memory MCP (user scope)?",
261
303
  default: false
262
304
  });
263
305
  if (!reconfigure) return;
264
306
  }
265
- const memoryDir = resolveMemoryDir();
266
- writeMemoryMcpEntry(memoryDir);
307
+ const ok = registerMemoryMcp();
308
+ if (ok) {
309
+ log.success(
310
+ "Registered coding-friend-memory (user scope). Restart Claude Code to activate."
311
+ );
312
+ }
267
313
  }
268
314
  async function memoryConfigMenu(opts) {
269
315
  while (true) {
@@ -303,7 +349,7 @@ async function memoryConfigMenu(opts) {
303
349
  );
304
350
  const embeddingLabel = embeddingVal?.provider ? embeddingVal.model ? `${embeddingVal.model} (${embeddingVal.provider})` : embeddingVal.provider : "";
305
351
  const mcpStatus = getMemoryMcpStatus();
306
- const mcpLabel = mcpStatus.configured ? mcpStatus.scope === "local" ? chalk.green("configured") + chalk.dim(" (.mcp.json)") : chalk.green("configured") + chalk.dim(" (global)") + " " + chalk.yellow("\u26A0") : chalk.dim("not configured");
352
+ const mcpLabel = mcpStatus.userScope ? mcpStatus.scope === "local" ? chalk.green("registered") + chalk.dim(" (user scope)") + " " + chalk.yellow("\u26A0 shadow") : chalk.green("registered") + chalk.dim(" (user scope)") : mcpStatus.configured ? mcpStatus.scope === "local" ? chalk.yellow("configured") + chalk.dim(" (.mcp.json)") : chalk.yellow("configured") + chalk.dim(" (global)") + " " + chalk.yellow("\u26A0") : chalk.dim("not registered");
307
353
  const choice = await select({
308
354
  message: "Memory settings:",
309
355
  choices: injectBackChoice(
@@ -354,7 +400,9 @@ async function memoryConfigMenu(opts) {
354
400
  }
355
401
 
356
402
  export {
357
- writeMemoryMcpEntry,
403
+ registerMemoryMcp,
404
+ isMemoryMcpRegistered,
405
+ removeMemoryMcpEntry,
358
406
  getMemoryMcpStatus,
359
407
  memoryConfigMenu
360
408
  };
@@ -1,6 +1,9 @@
1
1
  import {
2
2
  DEFAULT_CONFIG
3
- } from "./chunk-57EGAXYI.js";
3
+ } from "./chunk-3WIZA5OZ.js";
4
+ import {
5
+ run
6
+ } from "./chunk-EVGXUDX4.js";
4
7
  import {
5
8
  readJson
6
9
  } from "./chunk-5UVDWG5L.js";
@@ -14,7 +17,31 @@ import {
14
17
  } from "./chunk-NREZK463.js";
15
18
 
16
19
  // src/lib/config.ts
20
+ import { resolve as resolve2, isAbsolute } from "path";
17
21
  import { z } from "zod";
22
+
23
+ // src/lib/project-root.ts
24
+ import { resolve } from "path";
25
+ function resolveMainRepoRoot(baseDir) {
26
+ const gitCommonDir = run("git", ["-C", baseDir, "rev-parse", "--git-common-dir"]);
27
+ if (!gitCommonDir) {
28
+ return resolve(baseDir);
29
+ }
30
+ if (gitCommonDir === ".git") {
31
+ return resolve(baseDir);
32
+ }
33
+ if (gitCommonDir.endsWith("/.git")) {
34
+ const candidate = gitCommonDir.slice(0, -5);
35
+ return resolve(baseDir, candidate);
36
+ }
37
+ const topLevel = run("git", ["-C", baseDir, "rev-parse", "--show-toplevel"]);
38
+ if (topLevel) {
39
+ return resolve(baseDir, topLevel);
40
+ }
41
+ return resolve(baseDir);
42
+ }
43
+
44
+ // src/lib/config.ts
18
45
  function deepMerge(base, override) {
19
46
  const result = { ...base };
20
47
  for (const key of Object.keys(override)) {
@@ -51,6 +78,7 @@ var StatuslineComponentSchema = z.enum([
51
78
  "account",
52
79
  "context",
53
80
  "rate_limit",
81
+ "session",
54
82
  "task_agent"
55
83
  ]);
56
84
  var StatuslineConfigSchema = z.object({
@@ -212,10 +240,21 @@ function resolveMemoryDir(explicitPath) {
212
240
  }
213
241
  return resolvePath("docs/memory");
214
242
  }
243
+ function resolveProjectMemoryDir(baseDir) {
244
+ const root = resolveMainRepoRoot(baseDir);
245
+ const configPath = resolve2(root, ".coding-friend", "config.json");
246
+ const config = readJson(configPath);
247
+ const docsDir = config?.docsDir ?? "docs";
248
+ if (isAbsolute(docsDir)) {
249
+ return resolve2(docsDir, "memory");
250
+ }
251
+ return resolve2(root, docsDir, "memory");
252
+ }
215
253
 
216
254
  export {
217
255
  sanitizeRawConfig,
218
256
  loadConfig,
219
257
  resolveLearnDir,
220
- resolveMemoryDir
258
+ resolveMemoryDir,
259
+ resolveProjectMemoryDir
221
260
  };
@@ -2,16 +2,17 @@ import {
2
2
  checkMemoryMcpHealth,
3
3
  printHealthSection,
4
4
  warnStaleMcpJson
5
- } from "./chunk-MAGBNYZK.js";
6
- import {
7
- getMemoryMcpStatus,
8
- memoryConfigMenu,
9
- writeMemoryMcpEntry
10
- } from "./chunk-VV2U7NDN.js";
5
+ } from "./chunk-6S7352JG.js";
11
6
  import {
12
7
  loadConfig,
13
8
  resolveMemoryDir
14
- } from "./chunk-CDTQL4FI.js";
9
+ } from "./chunk-PI4P34KF.js";
10
+ import {
11
+ getMemoryMcpStatus,
12
+ isMemoryMcpRegistered,
13
+ memoryConfigMenu,
14
+ registerMemoryMcp
15
+ } from "./chunk-DP6DFQ4G.js";
15
16
  import {
16
17
  getLibPath
17
18
  } from "./chunk-RZRT7NGT.js";
@@ -107,7 +108,11 @@ async function refreshMemoryAfterUpdate() {
107
108
  }
108
109
  }
109
110
  function printMemoryMcpConfig(memoryDir) {
110
- console.log(chalk.dim("Add this to your MCP client config:"));
111
+ console.log(
112
+ chalk.dim(
113
+ "For non-Claude-Code MCP clients only (Claude Code is already set up via the user-scope registration above) \u2014 add this to that client's config:"
114
+ )
115
+ );
111
116
  console.log();
112
117
  console.log(`{
113
118
  "mcpServers": {
@@ -127,8 +132,8 @@ function printMemoryMcpConfig(memoryDir) {
127
132
  "Available tools & resources: https://cf.dinhanhthi.com/docs/cli/cf-mcp/"
128
133
  );
129
134
  console.log();
130
- log.warn(
131
- "Note: The memory path is project-specific. Use local .mcp.json (per project), not global ~/.claude/.mcp.json."
135
+ log.dim(
136
+ "Note: this explicit path is needed only for clients that can't set CLAUDE_PROJECT_DIR. Claude Code resolves the project automatically and needs no manual config."
132
137
  );
133
138
  console.log();
134
139
  }
@@ -189,17 +194,25 @@ async function memoryStatusCommand() {
189
194
  log.info(`Embedding: ${chalk.cyan(model)} ${chalk.dim(`(${provider})`)}`);
190
195
  }
191
196
  const mcpStatus = getMemoryMcpStatus();
192
- if (mcpStatus.configured && mcpStatus.scope === "local") {
197
+ if (mcpStatus.userScope && mcpStatus.scope === "local") {
198
+ log.info(
199
+ `MCP: ${chalk.green("registered")} ${chalk.dim("(user scope)")} ${chalk.yellow('\u26A0 project .mcp.json shadows user-scope server \u2014 run "cf update" to clean up')}`
200
+ );
201
+ } else if (mcpStatus.userScope) {
193
202
  log.info(
194
- `MCP: ${chalk.green("configured")} ${chalk.dim("(local .mcp.json)")}`
203
+ `MCP: ${chalk.green("registered")} ${chalk.dim("(user scope)")}`
204
+ );
205
+ } else if (mcpStatus.configured && mcpStatus.scope === "local") {
206
+ log.info(
207
+ `MCP: ${chalk.yellow("configured")} ${chalk.dim('(local .mcp.json \u2014 run "cf mcp" to migrate to user scope)')}`
195
208
  );
196
209
  } else if (mcpStatus.configured && mcpStatus.scope === "global") {
197
210
  log.info(
198
- `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")}`
211
+ `MCP: ${chalk.yellow("configured")} ${chalk.dim('(global ~/.claude/.mcp.json \u2014 run "cf mcp" to migrate to user scope)')} ${chalk.yellow("\u26A0 global config uses a fixed path \u2014 only works for one project")}`
199
212
  );
200
213
  } else {
201
214
  log.info(
202
- `MCP: ${chalk.dim("not configured in this project")} ${chalk.dim('(run "cf memory init" or add manually via "cf memory mcp")')}`
215
+ `MCP: ${chalk.dim("not registered")} ${chalk.dim('(run "cf mcp" to register at user scope)')}`
203
216
  );
204
217
  }
205
218
  const autoCapture = config.memory?.autoCapture ?? false;
@@ -500,13 +513,13 @@ async function memoryInitWizard(memoryDir, mcpDir) {
500
513
  const config = loadConfig();
501
514
  const resolvedTier = config.memory?.tier ?? "auto";
502
515
  if (resolvedTier === "markdown") {
503
- await setupMemoryMcp(memoryDir, mcpDir);
516
+ await setupMemoryMcp();
504
517
  console.log('\u{1F389} Memory initialized! Run "cf memory status" to verify.');
505
518
  printInitDone();
506
519
  return;
507
520
  }
508
521
  if (resolvedTier === "lite") {
509
- await setupMemoryMcp(memoryDir, mcpDir);
522
+ await setupMemoryMcp();
510
523
  console.log('\u{1F389} Memory initialized! Run "cf memory status" to verify.');
511
524
  printInitDone();
512
525
  return;
@@ -514,7 +527,7 @@ async function memoryInitWizard(memoryDir, mcpDir) {
514
527
  const depsOk = await ensureSqliteDepsIfNeeded(mcpDir);
515
528
  if (!depsOk) return;
516
529
  if (!existsSync(memoryDir)) {
517
- await setupMemoryMcp(memoryDir, mcpDir);
530
+ await setupMemoryMcp();
518
531
  console.log('\u{1F389} Memory initialized! Run "cf memory status" to verify.');
519
532
  printInitDone();
520
533
  return;
@@ -522,7 +535,7 @@ async function memoryInitWizard(memoryDir, mcpDir) {
522
535
  const docCount = countMdFiles(memoryDir);
523
536
  if (docCount === 0) {
524
537
  log.info("No existing memories to import.");
525
- await setupMemoryMcp(memoryDir, mcpDir);
538
+ await setupMemoryMcp();
526
539
  console.log('\u{1F389} Memory initialized! Run "cf memory status" to verify.');
527
540
  printInitDone();
528
541
  return;
@@ -544,7 +557,7 @@ async function memoryInitWizard(memoryDir, mcpDir) {
544
557
  } finally {
545
558
  await backend.close();
546
559
  }
547
- await setupMemoryMcp(memoryDir, mcpDir);
560
+ await setupMemoryMcp();
548
561
  console.log();
549
562
  console.log('\u{1F389} Memory initialized! Run "cf memory status" to verify.');
550
563
  log.info(
@@ -561,15 +574,14 @@ function printInitDone() {
561
574
  ` Documentation: ${chalk.cyan("https://cf.dinhanhthi.com/docs/cli/cf-memory/")}`
562
575
  );
563
576
  }
564
- async function setupMemoryMcp(memoryDir, _mcpDir) {
565
- const mcpStatus = getMemoryMcpStatus();
566
- if (mcpStatus.configured && mcpStatus.scope === "local") {
567
- log.info(`MCP: ${chalk.green("already configured")} in .mcp.json`);
577
+ async function setupMemoryMcp() {
578
+ if (isMemoryMcpRegistered()) {
579
+ log.info("coding-friend-memory: already registered (user scope)");
568
580
  return;
569
581
  }
570
- writeMemoryMcpEntry(memoryDir);
571
- log.dim(
572
- " Connects Claude Code to memory so skills can store and retrieve knowledge."
582
+ registerMemoryMcp();
583
+ log.success(
584
+ "Registered coding-friend-memory (user scope). Restart Claude Code to activate."
573
585
  );
574
586
  }
575
587
  async function memoryInitCommand() {
@@ -853,7 +865,7 @@ async function memoryMcpCommand() {
853
865
  const memoryDir = getMemoryDir();
854
866
  const mcpDir = getLibPath("cf-memory");
855
867
  ensureMemoryBuilt(mcpDir);
856
- warnStaleMcpJson(memoryDir);
868
+ warnStaleMcpJson();
857
869
  printMemoryMcpConfig(memoryDir);
858
870
  const localMcpPath = join(process.cwd(), ".mcp.json");
859
871
  const memoryDistPath = join(mcpDir, "dist", "index.js");
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  ALL_COMPONENT_IDS,
3
3
  STATUSLINE_COMPONENTS
4
- } from "./chunk-57EGAXYI.js";
4
+ } from "./chunk-3WIZA5OZ.js";
5
5
  import {
6
6
  mergeJson,
7
7
  readJson,
@@ -1,8 +1,9 @@
1
1
  import {
2
2
  loadConfig,
3
3
  resolveMemoryDir
4
- } from "./chunk-CDTQL4FI.js";
5
- import "./chunk-57EGAXYI.js";
4
+ } from "./chunk-PI4P34KF.js";
5
+ import "./chunk-3WIZA5OZ.js";
6
+ import "./chunk-EVGXUDX4.js";
6
7
  import "./chunk-5UVDWG5L.js";
7
8
  import {
8
9
  resolvePath