coding-friend-cli 1.36.4 → 1.38.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 (71) hide show
  1. package/README.md +8 -3
  2. package/dist/{chunk-GZT3YPAP.js → chunk-32UAW3NX.js} +1 -1
  3. package/dist/chunk-5QCGEPQO.js +238 -0
  4. package/dist/{chunk-YIXAQ4R2.js → chunk-6NXZKAOV.js} +1 -1
  5. package/dist/{chunk-XHP3EBR3.js → chunk-776ZY62N.js} +112 -4
  6. package/dist/{chunk-N4WZWRCI.js → chunk-BXSCYGWE.js} +85 -37
  7. package/dist/{chunk-AK6RMCAC.js → chunk-CGEMETKQ.js} +44 -30
  8. package/dist/{chunk-XEKQW4RN.js → chunk-ETTZRJ55.js} +1 -1
  9. package/dist/{chunk-IKLMHJH6.js → chunk-GIH6E7GJ.js} +62 -1
  10. package/dist/{chunk-56YJWGAN.js → chunk-ONG4KRYP.js} +60 -47
  11. package/dist/{chunk-X2H7JJMR.js → chunk-PLSJW7QJ.js} +29 -1
  12. package/dist/{chunk-O5ZKLFSL.js → chunk-RCE4OWNX.js} +1 -1
  13. package/dist/{chunk-K4WSC62G.js → chunk-RFNKPAJW.js} +42 -2
  14. package/dist/{clean-Q4VU662V.js → clean-YWEMYDE5.js} +3 -2
  15. package/dist/{config-CTTHWJH5.js → config-NG7SICM4.js} +15 -27
  16. package/dist/{dev-IGHMMEP4.js → dev-PTBNMKZT.js} +3 -3
  17. package/dist/{disable-HL5OEURT.js → disable-MO7AXYYF.js} +20 -3
  18. package/dist/{enable-CZIOCKKJ.js → enable-4GXLQEHD.js} +20 -3
  19. package/dist/{guide-CO2V4ZLZ.js → guide-HCR43LJN.js} +1 -1
  20. package/dist/{host-NCJMUSME.js → host-EV6QIVTI.js} +3 -3
  21. package/dist/index.js +62 -48
  22. package/dist/{init-UABKVDVT.js → init-KYE5CJMW.js} +135 -17
  23. package/dist/{install-R2HDM3N4.js → install-XAFQY5YF.js} +80 -5
  24. package/dist/{learn-K64AUUFB.js → learn-MC4VDHZH.js} +2 -2
  25. package/dist/{mcp-OMQFSJP4.js → mcp-FV6YX3ZT.js} +24 -24
  26. package/dist/{mcp-serve-3FEPFVVQ.js → mcp-serve-PXREYQRZ.js} +10 -1
  27. package/dist/{mcp-serve-learn-OF3ZJUJC.js → mcp-serve-learn-UVXIDOEZ.js} +1 -1
  28. package/dist/{memory-Y6GNALG3.js → memory-DF7OUOTV.js} +7 -7
  29. package/dist/{permission-I3OFJUYA.js → permission-VISPGMUK.js} +38 -4
  30. package/dist/{permissions-J46VP6US.js → permissions-2SBACQHV.js} +2 -2
  31. package/dist/{session-Q2DLQPYP.js → session-QK6OZO3X.js} +3 -2
  32. package/dist/{status-ML4XEDYL.js → status-FVFTJ6GS.js} +18 -14
  33. package/dist/{statusline-KKFJPTI7.js → statusline-AOZHEQSV.js} +2 -2
  34. package/dist/{uninstall-ASLHT3MH.js → uninstall-MF6FRUBO.js} +64 -3
  35. package/dist/{update-DIOH45TE.js → update-QNUCSDSA.js} +7 -4
  36. package/dist/{update-check-25SCLHZO.js → update-check-7LKJLJPM.js} +1 -1
  37. package/lib/cf-memory/src/backends/sqlite/index.ts +142 -85
  38. package/lib/cf-memory/src/lib/claude-md.ts +82 -70
  39. package/lib/cf-memory/src/lib/status-frame.ts +2 -2
  40. package/lib/cf-memory/src/tools/delete.ts +1 -1
  41. package/lib/cf-memory/src/tools/store.ts +6 -6
  42. package/lib/cf-memory/src/tools/update.ts +5 -11
  43. package/package.json +32 -4
  44. package/lib/cf-memory/README.md +0 -317
  45. package/lib/cf-memory/src/__tests__/claude-md.test.ts +0 -343
  46. package/lib/cf-memory/src/__tests__/daemon-client.test.ts +0 -146
  47. package/lib/cf-memory/src/__tests__/daemon-e2e.test.ts +0 -223
  48. package/lib/cf-memory/src/__tests__/daemon.test.ts +0 -407
  49. package/lib/cf-memory/src/__tests__/dedup.test.ts +0 -103
  50. package/lib/cf-memory/src/__tests__/embeddings.test.ts +0 -292
  51. package/lib/cf-memory/src/__tests__/lazy-install.test.ts +0 -210
  52. package/lib/cf-memory/src/__tests__/markdown-backend.test.ts +0 -450
  53. package/lib/cf-memory/src/__tests__/migration.test.ts +0 -255
  54. package/lib/cf-memory/src/__tests__/migrations.test.ts +0 -288
  55. package/lib/cf-memory/src/__tests__/minisearch-backend.test.ts +0 -262
  56. package/lib/cf-memory/src/__tests__/ollama.test.ts +0 -48
  57. package/lib/cf-memory/src/__tests__/project-id.test.ts +0 -43
  58. package/lib/cf-memory/src/__tests__/schema.test.ts +0 -128
  59. package/lib/cf-memory/src/__tests__/search.test.ts +0 -115
  60. package/lib/cf-memory/src/__tests__/status-frame.test.ts +0 -136
  61. package/lib/cf-memory/src/__tests__/temporal-decay.test.ts +0 -54
  62. package/lib/cf-memory/src/__tests__/tier.test.ts +0 -367
  63. package/lib/cf-memory/src/__tests__/tools.test.ts +0 -83
  64. package/lib/cf-memory/vitest.config.ts +0 -7
  65. package/lib/learn-host/.prettierignore +0 -4
  66. package/lib/learn-host/.prettierrc +0 -8
  67. package/lib/learn-host/README.md +0 -113
  68. package/lib/learn-host/eslint.config.mjs +0 -6
  69. package/lib/learn-mcp/README.md +0 -187
  70. package/lib/learn-mcp/src/__tests__/docs.test.ts +0 -117
  71. package/lib/learn-mcp/vitest.config.ts +0 -8
@@ -2,22 +2,23 @@ import {
2
2
  checkMemoryMcpHealth,
3
3
  printHealthSection,
4
4
  warnStaleMcpJson
5
- } from "./chunk-56YJWGAN.js";
6
- import {
7
- getMemoryMcpStatus,
8
- memoryConfigMenu,
9
- writeMemoryMcpEntry
10
- } from "./chunk-N4WZWRCI.js";
5
+ } from "./chunk-ONG4KRYP.js";
11
6
  import {
12
7
  loadConfig,
13
8
  resolveMemoryDir
14
- } from "./chunk-K4WSC62G.js";
9
+ } from "./chunk-RFNKPAJW.js";
10
+ import {
11
+ getMemoryMcpStatus,
12
+ isMemoryMcpRegistered,
13
+ memoryConfigMenu,
14
+ registerMemoryMcp
15
+ } from "./chunk-BXSCYGWE.js";
15
16
  import {
16
17
  getLibPath
17
18
  } from "./chunk-RZRT7NGT.js";
18
19
  import {
19
20
  showConfigHint
20
- } from "./chunk-IKLMHJH6.js";
21
+ } from "./chunk-GIH6E7GJ.js";
21
22
  import {
22
23
  run,
23
24
  runWithStderr
@@ -29,7 +30,7 @@ import {
29
30
  import {
30
31
  globalConfigPath,
31
32
  localConfigPath
32
- } from "./chunk-X2H7JJMR.js";
33
+ } from "./chunk-PLSJW7QJ.js";
33
34
  import {
34
35
  log,
35
36
  printBanner
@@ -103,11 +104,17 @@ async function refreshMemoryAfterUpdate() {
103
104
  log.warn('Memory server build failed \u2014 run "cf memory status" to retry.');
104
105
  }
105
106
  } catch {
106
- log.warn('Could not refresh memory server \u2014 run "cf memory status" to retry.');
107
+ log.warn(
108
+ 'Could not refresh memory server \u2014 run "cf memory status" to retry.'
109
+ );
107
110
  }
108
111
  }
109
112
  function printMemoryMcpConfig(memoryDir) {
110
- console.log(chalk.dim("Add this to your MCP client config:"));
113
+ console.log(
114
+ chalk.dim(
115
+ "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:"
116
+ )
117
+ );
111
118
  console.log();
112
119
  console.log(`{
113
120
  "mcpServers": {
@@ -127,8 +134,8 @@ function printMemoryMcpConfig(memoryDir) {
127
134
  "Available tools & resources: https://cf.dinhanhthi.com/docs/cli/cf-mcp/"
128
135
  );
129
136
  console.log();
130
- log.warn(
131
- "Note: The memory path is project-specific. Use local .mcp.json (per project), not global ~/.claude/.mcp.json."
137
+ log.dim(
138
+ "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
139
  );
133
140
  console.log();
134
141
  }
@@ -189,17 +196,25 @@ async function memoryStatusCommand() {
189
196
  log.info(`Embedding: ${chalk.cyan(model)} ${chalk.dim(`(${provider})`)}`);
190
197
  }
191
198
  const mcpStatus = getMemoryMcpStatus();
192
- if (mcpStatus.configured && mcpStatus.scope === "local") {
199
+ if (mcpStatus.userScope && mcpStatus.scope === "local") {
200
+ log.info(
201
+ `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')}`
202
+ );
203
+ } else if (mcpStatus.userScope) {
193
204
  log.info(
194
- `MCP: ${chalk.green("configured")} ${chalk.dim("(local .mcp.json)")}`
205
+ `MCP: ${chalk.green("registered")} ${chalk.dim("(user scope)")}`
206
+ );
207
+ } else if (mcpStatus.configured && mcpStatus.scope === "local") {
208
+ log.info(
209
+ `MCP: ${chalk.yellow("configured")} ${chalk.dim('(local .mcp.json \u2014 run "cf mcp" to migrate to user scope)')}`
195
210
  );
196
211
  } else if (mcpStatus.configured && mcpStatus.scope === "global") {
197
212
  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")}`
213
+ `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
214
  );
200
215
  } else {
201
216
  log.info(
202
- `MCP: ${chalk.dim("not configured in this project")} ${chalk.dim('(run "cf memory init" or add manually via "cf memory mcp")')}`
217
+ `MCP: ${chalk.dim("not registered")} ${chalk.dim('(run "cf mcp" to register at user scope)')}`
203
218
  );
204
219
  }
205
220
  const autoCapture = config.memory?.autoCapture ?? false;
@@ -500,13 +515,13 @@ async function memoryInitWizard(memoryDir, mcpDir) {
500
515
  const config = loadConfig();
501
516
  const resolvedTier = config.memory?.tier ?? "auto";
502
517
  if (resolvedTier === "markdown") {
503
- await setupMemoryMcp(memoryDir, mcpDir);
518
+ await setupMemoryMcp();
504
519
  console.log('\u{1F389} Memory initialized! Run "cf memory status" to verify.');
505
520
  printInitDone();
506
521
  return;
507
522
  }
508
523
  if (resolvedTier === "lite") {
509
- await setupMemoryMcp(memoryDir, mcpDir);
524
+ await setupMemoryMcp();
510
525
  console.log('\u{1F389} Memory initialized! Run "cf memory status" to verify.');
511
526
  printInitDone();
512
527
  return;
@@ -514,7 +529,7 @@ async function memoryInitWizard(memoryDir, mcpDir) {
514
529
  const depsOk = await ensureSqliteDepsIfNeeded(mcpDir);
515
530
  if (!depsOk) return;
516
531
  if (!existsSync(memoryDir)) {
517
- await setupMemoryMcp(memoryDir, mcpDir);
532
+ await setupMemoryMcp();
518
533
  console.log('\u{1F389} Memory initialized! Run "cf memory status" to verify.');
519
534
  printInitDone();
520
535
  return;
@@ -522,7 +537,7 @@ async function memoryInitWizard(memoryDir, mcpDir) {
522
537
  const docCount = countMdFiles(memoryDir);
523
538
  if (docCount === 0) {
524
539
  log.info("No existing memories to import.");
525
- await setupMemoryMcp(memoryDir, mcpDir);
540
+ await setupMemoryMcp();
526
541
  console.log('\u{1F389} Memory initialized! Run "cf memory status" to verify.');
527
542
  printInitDone();
528
543
  return;
@@ -544,7 +559,7 @@ async function memoryInitWizard(memoryDir, mcpDir) {
544
559
  } finally {
545
560
  await backend.close();
546
561
  }
547
- await setupMemoryMcp(memoryDir, mcpDir);
562
+ await setupMemoryMcp();
548
563
  console.log();
549
564
  console.log('\u{1F389} Memory initialized! Run "cf memory status" to verify.');
550
565
  log.info(
@@ -561,15 +576,14 @@ function printInitDone() {
561
576
  ` Documentation: ${chalk.cyan("https://cf.dinhanhthi.com/docs/cli/cf-memory/")}`
562
577
  );
563
578
  }
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`);
579
+ async function setupMemoryMcp() {
580
+ if (isMemoryMcpRegistered()) {
581
+ log.info("coding-friend-memory: already registered (user scope)");
568
582
  return;
569
583
  }
570
- writeMemoryMcpEntry(memoryDir);
571
- log.dim(
572
- " Connects Claude Code to memory so skills can store and retrieve knowledge."
584
+ registerMemoryMcp();
585
+ log.success(
586
+ "Registered coding-friend-memory (user scope). Restart Claude Code to activate."
573
587
  );
574
588
  }
575
589
  async function memoryInitCommand() {
@@ -853,7 +867,7 @@ async function memoryMcpCommand() {
853
867
  const memoryDir = getMemoryDir();
854
868
  const mcpDir = getLibPath("cf-memory");
855
869
  ensureMemoryBuilt(mcpDir);
856
- warnStaleMcpJson(memoryDir);
870
+ warnStaleMcpJson();
857
871
  printMemoryMcpConfig(memoryDir);
858
872
  const localMcpPath = join(process.cwd(), ".mcp.json");
859
873
  const memoryDistPath = join(mcpDir, "dist", "index.js");
@@ -12,7 +12,7 @@ import {
12
12
  globalConfigPath,
13
13
  installedPluginsPath,
14
14
  pluginCachePath
15
- } from "./chunk-X2H7JJMR.js";
15
+ } from "./chunk-PLSJW7QJ.js";
16
16
 
17
17
  // src/lib/statusline.ts
18
18
  import { existsSync, readdirSync } from "fs";
@@ -7,11 +7,62 @@ import {
7
7
  import {
8
8
  globalConfigPath,
9
9
  localConfigPath
10
- } from "./chunk-X2H7JJMR.js";
10
+ } from "./chunk-PLSJW7QJ.js";
11
11
  import {
12
12
  log
13
13
  } from "./chunk-NREZK463.js";
14
14
 
15
+ // src/lib/host.ts
16
+ var CODEX_MIN_VERSION = "0.130.0";
17
+ function resolveHost(opts = {}) {
18
+ const agent = opts.agent?.trim().toLowerCase();
19
+ const codexAlias = opts.codex === true;
20
+ if (agent && agent !== "claude" && agent !== "codex") {
21
+ throw new Error(
22
+ `Unsupported agent "${opts.agent}". Use "claude" or "codex".`
23
+ );
24
+ }
25
+ if (codexAlias && agent === "claude") {
26
+ throw new Error("Use either --agent claude or --codex, not both.");
27
+ }
28
+ if (codexAlias) return "codex";
29
+ if (agent === "codex") return "codex";
30
+ return "claude";
31
+ }
32
+ function getCodexMinVersion() {
33
+ return CODEX_MIN_VERSION;
34
+ }
35
+ function checkCodexVersion() {
36
+ const min = getCodexMinVersion();
37
+ const output = run("codex", ["--version"]);
38
+ const actual = output ? extractVersion(output) : void 0;
39
+ return {
40
+ ok: actual ? compareVersions(actual, min) >= 0 : false,
41
+ actual,
42
+ min
43
+ };
44
+ }
45
+ function extractVersion(input) {
46
+ return input.match(/\b(\d+\.\d+\.\d+)\b/)?.[1];
47
+ }
48
+ function compareVersions(a, b) {
49
+ const left = parseVersion(a);
50
+ const right = parseVersion(b);
51
+ for (let i = 0; i < 3; i += 1) {
52
+ if (left[i] > right[i]) return 1;
53
+ if (left[i] < right[i]) return -1;
54
+ }
55
+ return 0;
56
+ }
57
+ function parseVersion(version) {
58
+ const parts = version.split(".").map((part) => Number.parseInt(part, 10));
59
+ return [
60
+ Number.isFinite(parts[0]) ? parts[0] : 0,
61
+ Number.isFinite(parts[1]) ? parts[1] : 0,
62
+ Number.isFinite(parts[2]) ? parts[2] : 0
63
+ ];
64
+ }
65
+
15
66
  // src/lib/prompt-utils.ts
16
67
  import { select, Separator } from "@inquirer/prompts";
17
68
  import { existsSync } from "fs";
@@ -80,6 +131,14 @@ async function resolveScope(opts, message) {
80
131
  }
81
132
  return askPluginScope(message);
82
133
  }
134
+ function resolveHostFlags(opts) {
135
+ try {
136
+ return { host: resolveHost(opts) };
137
+ } catch (err) {
138
+ log.error(err instanceof Error ? err.message : String(err));
139
+ process.exit(1);
140
+ }
141
+ }
83
142
  function showConfigHint() {
84
143
  console.log(chalk.dim("Config files:"));
85
144
  console.log(chalk.dim(" Global: ~/.coding-friend/config.json"));
@@ -173,10 +232,12 @@ function applyDocsDirChange(newValue, oldValue, scope, subfolders = []) {
173
232
  }
174
233
 
175
234
  export {
235
+ checkCodexVersion,
176
236
  BACK,
177
237
  injectBackChoice,
178
238
  askScope,
179
239
  resolveScope,
240
+ resolveHostFlags,
180
241
  showConfigHint,
181
242
  getScopeLabel,
182
243
  formatScopeLabel,
@@ -1,6 +1,6 @@
1
1
  import {
2
- writeMemoryMcpEntry
3
- } from "./chunk-N4WZWRCI.js";
2
+ isMemoryMcpRegistered
3
+ } from "./chunk-BXSCYGWE.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,8 @@
1
1
  // src/lib/paths.ts
2
2
  import { homedir } from "os";
3
3
  import { resolve, join, isAbsolute } from "path";
4
+ var CODEX_MARKETPLACE_NAME = "coding-friend-marketplace";
5
+ var CODEX_PLUGIN_NAME = "coding-friend";
4
6
  function resolvePath(p, base = process.cwd()) {
5
7
  if (isAbsolute(p)) return p;
6
8
  if (p.startsWith("~/")) return join(homedir(), p.slice(2));
@@ -77,6 +79,29 @@ function claudeProjectsDir() {
77
79
  function claudeSessionDir(encodedPath) {
78
80
  return join(claudeProjectsDir(), encodedPath);
79
81
  }
82
+ function codexConfigDir() {
83
+ const env = process.env.CODEX_HOME?.trim();
84
+ if (!env) return join(homedir(), ".codex");
85
+ if (env === "~") return homedir();
86
+ if (env.startsWith("~/")) return join(homedir(), env.slice(2));
87
+ return env;
88
+ }
89
+ function codexConfigTomlPath() {
90
+ return join(codexConfigDir(), "config.toml");
91
+ }
92
+ function codexPluginsCacheDir() {
93
+ return join(codexConfigDir(), "plugins", "cache");
94
+ }
95
+ function codexInstalledPluginsPath() {
96
+ return join(
97
+ codexPluginsCacheDir(),
98
+ CODEX_MARKETPLACE_NAME,
99
+ CODEX_PLUGIN_NAME
100
+ );
101
+ }
102
+ function codexAgentsDir() {
103
+ return join(codexConfigDir(), "agents");
104
+ }
80
105
 
81
106
  export {
82
107
  resolvePath,
@@ -95,5 +120,8 @@ export {
95
120
  globalConfigDir,
96
121
  memoryDepsDir,
97
122
  encodeProjectPath,
98
- claudeSessionDir
123
+ claudeSessionDir,
124
+ codexConfigTomlPath,
125
+ codexInstalledPluginsPath,
126
+ codexAgentsDir
99
127
  };
@@ -4,7 +4,7 @@ import {
4
4
  } from "./chunk-5UVDWG5L.js";
5
5
  import {
6
6
  pluginCachePath
7
- } from "./chunk-X2H7JJMR.js";
7
+ } from "./chunk-PLSJW7QJ.js";
8
8
 
9
9
  // src/lib/permissions.ts
10
10
  var STATIC_RULES = [
@@ -1,6 +1,9 @@
1
1
  import {
2
2
  DEFAULT_CONFIG
3
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";
@@ -8,13 +11,37 @@ import {
8
11
  globalConfigPath,
9
12
  localConfigPath,
10
13
  resolvePath
11
- } from "./chunk-X2H7JJMR.js";
14
+ } from "./chunk-PLSJW7QJ.js";
12
15
  import {
13
16
  log
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)) {
@@ -80,6 +107,7 @@ var ConfigSchema = z.strictObject({
80
107
  memory: MemoryConfigSchema.optional(),
81
108
  review: ReviewConfigSchema.optional(),
82
109
  autoApprove: z.boolean().optional(),
110
+ autoApproveCodex: z.boolean().optional(),
83
111
  autoApproveIgnore: z.array(z.string()).optional(),
84
112
  autoApproveAllowExtra: z.array(z.string()).optional(),
85
113
  disableGUIPlan: z.boolean().optional(),
@@ -94,6 +122,7 @@ var KNOWN_KEYS = [
94
122
  "memory",
95
123
  "review",
96
124
  "autoApprove",
125
+ "autoApproveCodex",
97
126
  "autoApproveIgnore",
98
127
  "autoApproveAllowExtra",
99
128
  "disableGUIPlan",
@@ -213,10 +242,21 @@ function resolveMemoryDir(explicitPath) {
213
242
  }
214
243
  return resolvePath("docs/memory");
215
244
  }
245
+ function resolveProjectMemoryDir(baseDir) {
246
+ const root = resolveMainRepoRoot(baseDir);
247
+ const configPath = resolve2(root, ".coding-friend", "config.json");
248
+ const config = readJson(configPath);
249
+ const docsDir = config?.docsDir ?? "docs";
250
+ if (isAbsolute(docsDir)) {
251
+ return resolve2(docsDir, "memory");
252
+ }
253
+ return resolve2(root, docsDir, "memory");
254
+ }
216
255
 
217
256
  export {
218
257
  sanitizeRawConfig,
219
258
  loadConfig,
220
259
  resolveLearnDir,
221
- resolveMemoryDir
260
+ resolveMemoryDir,
261
+ resolveProjectMemoryDir
222
262
  };
@@ -1,12 +1,13 @@
1
1
  import {
2
2
  loadConfig,
3
3
  resolveMemoryDir
4
- } from "./chunk-K4WSC62G.js";
4
+ } from "./chunk-RFNKPAJW.js";
5
5
  import "./chunk-3WIZA5OZ.js";
6
+ import "./chunk-EVGXUDX4.js";
6
7
  import "./chunk-5UVDWG5L.js";
7
8
  import {
8
9
  resolvePath
9
- } from "./chunk-X2H7JJMR.js";
10
+ } from "./chunk-PLSJW7QJ.js";
10
11
  import {
11
12
  log,
12
13
  printBanner
@@ -1,21 +1,17 @@
1
1
  import {
2
2
  getAllRules,
3
3
  getExistingRules
4
- } from "./chunk-O5ZKLFSL.js";
4
+ } from "./chunk-RCE4OWNX.js";
5
5
  import {
6
6
  listMdFilesRecursive
7
7
  } from "./chunk-2DTXVMPQ.js";
8
8
  import {
9
9
  registerLearnMcp,
10
10
  unregisterLearnMcp
11
- } from "./chunk-YIXAQ4R2.js";
12
- import {
13
- memoryConfigMenu
14
- } from "./chunk-N4WZWRCI.js";
11
+ } from "./chunk-6NXZKAOV.js";
15
12
  import {
16
13
  resolveLearnDir
17
- } from "./chunk-K4WSC62G.js";
18
- import "./chunk-RZRT7NGT.js";
14
+ } from "./chunk-RFNKPAJW.js";
19
15
  import {
20
16
  findStatuslineHookPath,
21
17
  getCurrentAccountEmail,
@@ -26,7 +22,7 @@ import {
26
22
  saveStatuslineConfig,
27
23
  selectStatuslineComponents,
28
24
  writeStatuslineSettings
29
- } from "./chunk-XEKQW4RN.js";
25
+ } from "./chunk-ETTZRJ55.js";
30
26
  import {
31
27
  ALL_COMPONENT_IDS,
32
28
  DEFAULT_CONFIG
@@ -36,6 +32,10 @@ import {
36
32
  hasShellCompletion,
37
33
  removeShellCompletion
38
34
  } from "./chunk-667NMPN4.js";
35
+ import {
36
+ memoryConfigMenu
37
+ } from "./chunk-BXSCYGWE.js";
38
+ import "./chunk-RZRT7NGT.js";
39
39
  import {
40
40
  BACK,
41
41
  applyDocsDirChange,
@@ -45,7 +45,7 @@ import {
45
45
  getScopeLabel,
46
46
  injectBackChoice,
47
47
  showConfigHint
48
- } from "./chunk-IKLMHJH6.js";
48
+ } from "./chunk-GIH6E7GJ.js";
49
49
  import "./chunk-EVGXUDX4.js";
50
50
  import {
51
51
  mergeJson,
@@ -58,7 +58,7 @@ import {
58
58
  globalConfigPath,
59
59
  localConfigPath,
60
60
  resolvePath
61
- } from "./chunk-X2H7JJMR.js";
61
+ } from "./chunk-PLSJW7QJ.js";
62
62
  import {
63
63
  log,
64
64
  printBanner
@@ -448,11 +448,7 @@ async function editDisableGUIPlan(globalCfg, localCfg) {
448
448
  writeToScope(scope, { disableGUIPlan: value });
449
449
  }
450
450
  async function editGuiPlanFormat(globalCfg, localCfg) {
451
- const currentValue = getMergedValue(
452
- "guiPlanFormat",
453
- globalCfg,
454
- localCfg
455
- );
451
+ const currentValue = getMergedValue("guiPlanFormat", globalCfg, localCfg);
456
452
  if (currentValue !== void 0) {
457
453
  log.dim(`Current: ${currentValue}`);
458
454
  }
@@ -476,16 +472,8 @@ async function planDocsSubMenu() {
476
472
  while (true) {
477
473
  const globalCfg = readJson(globalConfigPath());
478
474
  const localCfg = readJson(localConfigPath());
479
- const disableVal = getMergedValue(
480
- "disableGUIPlan",
481
- globalCfg,
482
- localCfg
483
- );
484
- const formatVal = getMergedValue(
485
- "guiPlanFormat",
486
- globalCfg,
487
- localCfg
488
- );
475
+ const disableVal = getMergedValue("disableGUIPlan", globalCfg, localCfg);
476
+ const formatVal = getMergedValue("guiPlanFormat", globalCfg, localCfg);
489
477
  const choice = await select({
490
478
  message: "Plan docs settings:",
491
479
  choices: injectBackChoice(
@@ -553,7 +541,7 @@ async function editAutoApprove(globalCfg, localCfg) {
553
541
  if (scope === "back") return;
554
542
  writeToScope(scope, { autoApprove: value });
555
543
  if (value) {
556
- const { runDangerousRulesAudit } = await import("./permissions-J46VP6US.js");
544
+ const { runDangerousRulesAudit } = await import("./permissions-2SBACQHV.js");
557
545
  await runDangerousRulesAudit(
558
546
  [
559
547
  claudeProjectSettingsPath(),
@@ -749,7 +737,7 @@ async function editPermissions() {
749
737
  )
750
738
  });
751
739
  if (choice === BACK) return;
752
- const { permissionCommand } = await import("./permission-I3OFJUYA.js");
740
+ const { permissionCommand } = await import("./permission-VISPGMUK.js");
753
741
  switch (choice) {
754
742
  case "interactive":
755
743
  await permissionCommand({});