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,8 +1,6 @@
1
1
  import {
2
2
  memoryConfigMenu
3
- } from "./chunk-NZKIYVIK.js";
4
- import "./chunk-LK6HFD35.js";
5
- import "./chunk-RZRT7NGT.js";
3
+ } from "./chunk-3UIH5U3Y.js";
6
4
  import {
7
5
  findStatuslineHookPath,
8
6
  getCurrentAccountEmail,
@@ -413,6 +411,68 @@ async function editAutoApprove(globalCfg, localCfg) {
413
411
  log.dim("Docs: https://cf.dinhanhthi.com/docs/reference/auto-approve/");
414
412
  }
415
413
  }
414
+ function buildCodexConfig(input2) {
415
+ return {
416
+ enabled: input2.enabled,
417
+ modes: input2.modes,
418
+ effort: input2.effort
419
+ };
420
+ }
421
+ async function editCodex(globalCfg, localCfg) {
422
+ const current = localCfg?.codex ?? globalCfg?.codex;
423
+ const defaults = DEFAULT_CONFIG.codex;
424
+ if (current !== void 0) {
425
+ log.dim(
426
+ `Current: enabled=${current.enabled}, modes=${(current.modes ?? []).join(",") || "(none)"}, effort=${current.effort}`
427
+ );
428
+ }
429
+ const enabled = await confirm({
430
+ message: "Enable Codex as 6th specialist in /cf-review? (requires `codex` CLI in PATH)",
431
+ default: current?.enabled ?? defaults.enabled
432
+ });
433
+ const modes = await checkbox({
434
+ message: "Which review modes should Codex run in?",
435
+ choices: [
436
+ { name: "QUICK (\u22643 files, \u226450 lines)", value: "QUICK" },
437
+ { name: "STANDARD (4\u201310 files or 51\u2013300 lines)", value: "STANDARD" },
438
+ {
439
+ name: "DEEP (>10 files, >300 lines, or sensitive paths)",
440
+ value: "DEEP"
441
+ }
442
+ ],
443
+ default: current?.modes ?? defaults.modes
444
+ });
445
+ if (modes.length === 0) {
446
+ log.dim(
447
+ "Warning: no modes selected \u2014 Codex will never run. Select at least one mode."
448
+ );
449
+ }
450
+ const effort = await select({
451
+ message: "Codex reasoning effort level:",
452
+ choices: [
453
+ { name: "minimal \u2014 fastest, least thorough", value: "minimal" },
454
+ { name: "low", value: "low" },
455
+ { name: "medium (default)", value: "medium" },
456
+ { name: "high", value: "high" },
457
+ { name: "xhigh \u2014 slowest, most thorough", value: "xhigh" }
458
+ ],
459
+ default: current?.effort ?? defaults.effort
460
+ });
461
+ const scope = await askScope();
462
+ if (scope === "back") return;
463
+ const codexCfg = buildCodexConfig({ enabled, modes, effort });
464
+ writeToScope(scope, { codex: codexCfg });
465
+ if (enabled) {
466
+ log.dim(
467
+ "Tip: make sure `codex` CLI is installed \u2014 `npm install -g @openai/codex`"
468
+ );
469
+ log.dim(
470
+ "Docs: https://cf.dinhanhthi.com/docs/configuration/config-json/#codex"
471
+ );
472
+ } else {
473
+ log.dim("Codex disabled.");
474
+ }
475
+ }
416
476
  async function editStatusline() {
417
477
  const hookResult = findStatuslineHookPath();
418
478
  if (!hookResult) {
@@ -626,6 +686,8 @@ async function configCommand() {
626
686
  globalCfg,
627
687
  localCfg
628
688
  );
689
+ const codexScope = getScopeLabel("codex", globalCfg, localCfg);
690
+ const codexEnabled = getMergedValue("codex", globalCfg, localCfg)?.enabled;
629
691
  const statuslineStatus = isStatuslineConfigured() ? chalk.green("configured") : chalk.yellow("not configured");
630
692
  const completionStatus = hasShellCompletion() ? chalk.green("installed") : chalk.yellow("not installed");
631
693
  const projectRules = getExistingRules(claudeLocalSettingsPath());
@@ -666,6 +728,11 @@ async function configCommand() {
666
728
  value: "autoApprove",
667
729
  description: " Auto-approve safe tool calls, block destructive ones, prompt for ambiguous"
668
730
  },
731
+ {
732
+ name: `Codex cross-engine review ${formatScopeLabel(codexScope)}${codexEnabled !== void 0 ? ` (${codexEnabled ? "enabled" : "disabled"})` : ""}`,
733
+ value: "codex",
734
+ description: " Enable OpenAI Codex as 6th specialist in /cf-review (opt-in)"
735
+ },
669
736
  {
670
737
  name: `Statusline (${statuslineStatus})`,
671
738
  value: "statusline",
@@ -709,6 +776,9 @@ async function configCommand() {
709
776
  case "autoApprove":
710
777
  await editAutoApprove(globalCfg, localCfg);
711
778
  break;
779
+ case "codex":
780
+ await editCodex(globalCfg, localCfg);
781
+ break;
712
782
  case "statusline":
713
783
  await editStatusline();
714
784
  break;
@@ -725,6 +795,9 @@ async function configCommand() {
725
795
  console.log();
726
796
  }
727
797
  }
798
+
728
799
  export {
800
+ buildCodexConfig,
801
+ editCodex,
729
802
  configCommand
730
803
  };
@@ -160,6 +160,22 @@ function stripPath(obj, path) {
160
160
  }
161
161
  delete current[String(path[path.length - 1])];
162
162
  }
163
+ function sanitizeRawConfig(raw) {
164
+ const result = ConfigSchema.safeParse(raw);
165
+ if (result.success) {
166
+ return result.data;
167
+ }
168
+ const unknownKeys = [];
169
+ for (const issue of result.error.issues) {
170
+ if (issue.code === "unrecognized_keys") {
171
+ const keys = issue.keys ?? [];
172
+ unknownKeys.push(...keys);
173
+ }
174
+ }
175
+ const keyList = unknownKeys.length > 0 ? `: ${unknownKeys.join(", ")}` : "";
176
+ log.warn(`sanitizeRawConfig: unrecognized config keys${keyList} \u2014 returning raw config`);
177
+ return raw;
178
+ }
163
179
  function loadConfig() {
164
180
  const global = readJson(globalConfigPath());
165
181
  const local = readJson(localConfigPath());
@@ -196,6 +212,7 @@ function resolveMemoryDir(explicitPath) {
196
212
  }
197
213
 
198
214
  export {
215
+ sanitizeRawConfig,
199
216
  loadConfig,
200
217
  resolveDocsDir,
201
218
  resolveMemoryDir
@@ -0,0 +1,118 @@
1
+ import {
2
+ detectMemoryMcpState
3
+ } from "./chunk-AYIREX73.js";
4
+
5
+ // src/lib/mcp-health.ts
6
+ async function checkMemoryMcpHealth(deps) {
7
+ const checks = [];
8
+ const mcpJson = deps.readMcpJson();
9
+ const state = detectMemoryMcpState(mcpJson, deps.pathExists);
10
+ if (state.kind === "npx") {
11
+ checks.push({ label: "Config (.mcp.json)", ok: true });
12
+ } else if (state.kind === "none") {
13
+ checks.push({
14
+ label: "Config (.mcp.json)",
15
+ ok: false,
16
+ detail: "coding-friend-memory not configured",
17
+ fix: 'Run "cf memory mcp" to add the MCP entry'
18
+ });
19
+ } else {
20
+ const detail = state.kind === "stale" ? `Stale path: ${state.path}` : `Absolute path (legacy): ${state.path}`;
21
+ checks.push({
22
+ label: "Config (.mcp.json)",
23
+ ok: false,
24
+ detail,
25
+ fix: 'Run "cf memory mcp" to update to the npx format'
26
+ });
27
+ }
28
+ const distExists = deps.pathExists(deps.memoryDistPath);
29
+ checks.push({
30
+ label: "Package built",
31
+ ok: distExists,
32
+ ...distExists ? {} : {
33
+ detail: "cf-memory not built",
34
+ fix: 'Run "cf memory init" to build'
35
+ }
36
+ });
37
+ let daemonRunning = false;
38
+ let daemonCheckError;
39
+ try {
40
+ daemonRunning = await deps.isDaemonRunning();
41
+ } catch (err) {
42
+ daemonCheckError = err instanceof Error ? err.message : String(err);
43
+ }
44
+ checks.push({
45
+ label: "Daemon status",
46
+ ok: daemonRunning,
47
+ ...daemonRunning ? {} : {
48
+ warn: true,
49
+ detail: daemonCheckError ? `check failed: ${daemonCheckError}` : "stopped (starts automatically on MCP connect)"
50
+ }
51
+ });
52
+ const ok = checks.every((c) => c.ok || c.warn === true);
53
+ return { checks, ok };
54
+ }
55
+ async function checkLearnMcpHealth(deps) {
56
+ const checks = [];
57
+ const mcpJson = deps.readMcpJson();
58
+ const servers = mcpJson?.mcpServers;
59
+ const hasEntry = servers != null && typeof servers === "object" && !Array.isArray(servers) && "coding-friend-learn" in servers;
60
+ checks.push({
61
+ label: "Config (.mcp.json)",
62
+ ok: hasEntry,
63
+ ...hasEntry ? {} : {
64
+ detail: "coding-friend-learn not configured",
65
+ fix: 'Run "cf mcp" to print the config snippet and add it'
66
+ }
67
+ });
68
+ const distExists = deps.pathExists(deps.learnMcpDistPath);
69
+ checks.push({
70
+ label: "Package built",
71
+ ok: distExists,
72
+ ...distExists ? {} : {
73
+ detail: "learn-mcp not built",
74
+ fix: 'Run "cf mcp" to install and build'
75
+ }
76
+ });
77
+ const mdFiles = deps.listMdFiles(deps.docsDir);
78
+ const hasDocs = mdFiles.length > 0;
79
+ checks.push({
80
+ label: "Docs directory",
81
+ ok: hasDocs,
82
+ ...hasDocs ? {} : {
83
+ detail: `No .md files found in ${deps.docsDir}`,
84
+ fix: 'Run "/cf-learn" to generate docs'
85
+ }
86
+ });
87
+ const ok = checks.every((c) => c.ok || c.warn === true);
88
+ return { checks, ok };
89
+ }
90
+
91
+ // src/lib/fs-utils.ts
92
+ import { readdirSync } from "fs";
93
+ import { join } from "path";
94
+ function listMdFilesRecursive(dir, maxDepth = 15) {
95
+ try {
96
+ const results = [];
97
+ for (const entry of readdirSync(dir, { withFileTypes: true })) {
98
+ if (entry.isDirectory()) {
99
+ if (maxDepth > 0) {
100
+ results.push(
101
+ ...listMdFilesRecursive(join(dir, entry.name), maxDepth - 1)
102
+ );
103
+ }
104
+ } else if (entry.name.endsWith(".md") && entry.name !== "README.md") {
105
+ results.push(entry.name);
106
+ }
107
+ }
108
+ return results;
109
+ } catch {
110
+ return [];
111
+ }
112
+ }
113
+
114
+ export {
115
+ checkMemoryMcpHealth,
116
+ checkLearnMcpHealth,
117
+ listMdFilesRecursive
118
+ };
@@ -0,0 +1,22 @@
1
+ import {
2
+ buildCodexConfig,
3
+ configCommand,
4
+ editCodex
5
+ } from "./chunk-CXG6I6ZF.js";
6
+ import "./chunk-3UIH5U3Y.js";
7
+ import "./chunk-K6EDQGDN.js";
8
+ import "./chunk-RZRT7NGT.js";
9
+ import "./chunk-XOHLBLN2.js";
10
+ import "./chunk-OYPOADCX.js";
11
+ import "./chunk-DMJCO6LJ.js";
12
+ import "./chunk-Z5Q533UG.js";
13
+ import "./chunk-TWKNGPBO.js";
14
+ import "./chunk-EVGXUDX4.js";
15
+ import "./chunk-NREZK463.js";
16
+ import "./chunk-O27FVQFU.js";
17
+ import "./chunk-5UVDWG5L.js";
18
+ export {
19
+ buildCodexConfig,
20
+ configCommand,
21
+ editCodex
22
+ };
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  resolveDocsDir
3
- } from "./chunk-LK6HFD35.js";
3
+ } from "./chunk-K6EDQGDN.js";
4
4
  import {
5
5
  getLibPath
6
6
  } from "./chunk-RZRT7NGT.js";
package/dist/index.js CHANGED
@@ -30,19 +30,19 @@ program.command("enable").description("Re-enable the Coding Friend plugin").opti
30
30
  await enableCommand(opts);
31
31
  });
32
32
  program.command("init").description("Initialize coding-friend in current project").action(async () => {
33
- const { initCommand } = await import("./init-QZG3RV5S.js");
33
+ const { initCommand } = await import("./init-WDFYY5MM.js");
34
34
  await initCommand();
35
35
  });
36
36
  program.command("config").description("Manage Coding Friend configuration").action(async () => {
37
- const { configCommand } = await import("./config-TAYQIBPL.js");
37
+ const { configCommand } = await import("./config-MCEQOLE5.js");
38
38
  await configCommand();
39
39
  });
40
40
  program.command("host").description("Build and serve learning docs as a static website").argument("[path]", "path to docs folder").option("-p, --port <port>", "port number", "3333").action(async (path, opts) => {
41
- const { hostCommand } = await import("./host-Y4N4PI4N.js");
41
+ const { hostCommand } = await import("./host-Y3WIXDSS.js");
42
42
  await hostCommand(path, opts);
43
43
  });
44
44
  program.command("mcp").description("Setup MCP server for learning docs").argument("[path]", "path to docs folder").action(async (path) => {
45
- const { mcpCommand } = await import("./mcp-BD2K7OL6.js");
45
+ const { mcpCommand } = await import("./mcp-J4SD646S.js");
46
46
  await mcpCommand(path);
47
47
  });
48
48
  program.command("mcp-serve").description("Start the cf-memory MCP server (used internally by npx)").argument("<memoryDir>", "path to memory directory").action(async (memoryDir) => {
@@ -69,7 +69,7 @@ program.command("update").description("Update coding-friend plugin, CLI, and sta
69
69
  await updateCommand(opts);
70
70
  });
71
71
  program.command("status").description("Show comprehensive Coding Friend status").action(async () => {
72
- const { statusCommand } = await import("./status-V2A7RSSM.js");
72
+ const { statusCommand } = await import("./status-BX2SVBBW.js");
73
73
  await statusCommand();
74
74
  });
75
75
  var session = program.command("session").description("Save and load Claude Code sessions across machines");
@@ -84,11 +84,11 @@ session.command("save").description("Save current Claude Code session to sync fo
84
84
  "-s, --session-id <id>",
85
85
  "session UUID to save (default: auto-detect newest)"
86
86
  ).option("-l, --label <label>", "label for this session").action(async (opts) => {
87
- const { sessionSaveCommand } = await import("./session-RHXTVNRU.js");
87
+ const { sessionSaveCommand } = await import("./session-6WQGKIRG.js");
88
88
  await sessionSaveCommand(opts);
89
89
  });
90
90
  session.command("load").description("Load a saved session from sync folder").action(async () => {
91
- const { sessionLoadCommand } = await import("./session-RHXTVNRU.js");
91
+ const { sessionLoadCommand } = await import("./session-6WQGKIRG.js");
92
92
  await sessionLoadCommand();
93
93
  });
94
94
  var memory = program.command("memory").description("AI memory system \u2014 store and search project knowledge");
@@ -108,43 +108,43 @@ Memory subcommands:
108
108
  memory mcp Show MCP server setup instructions`
109
109
  );
110
110
  memory.command("status").description("Show memory system status").action(async () => {
111
- const { memoryStatusCommand } = await import("./memory-FZMACIGE.js");
111
+ const { memoryStatusCommand } = await import("./memory-RLZR7XQY.js");
112
112
  await memoryStatusCommand();
113
113
  });
114
114
  memory.command("search").description("Search memories by query").argument("<query>", "search query").action(async (query) => {
115
- const { memorySearchCommand } = await import("./memory-FZMACIGE.js");
115
+ const { memorySearchCommand } = await import("./memory-RLZR7XQY.js");
116
116
  await memorySearchCommand(query);
117
117
  });
118
118
  memory.command("list").description(
119
119
  "List memories in current project, or all projects with --projects"
120
120
  ).option("--projects", "List all project databases with size and metadata").action(async (opts) => {
121
- const { memoryListCommand } = await import("./memory-FZMACIGE.js");
121
+ const { memoryListCommand } = await import("./memory-RLZR7XQY.js");
122
122
  await memoryListCommand(opts);
123
123
  });
124
124
  memory.command("init").description(
125
125
  "Initialize memory system \u2014 interactive wizard (first time) or config menu"
126
126
  ).action(async () => {
127
- const { memoryInitCommand } = await import("./memory-FZMACIGE.js");
127
+ const { memoryInitCommand } = await import("./memory-RLZR7XQY.js");
128
128
  await memoryInitCommand();
129
129
  });
130
130
  memory.command("config").description("Configure memory system settings").action(async () => {
131
- const { memoryConfigCommand } = await import("./memory-FZMACIGE.js");
131
+ const { memoryConfigCommand } = await import("./memory-RLZR7XQY.js");
132
132
  await memoryConfigCommand();
133
133
  });
134
134
  memory.command("start-daemon").description("Start the memory daemon (Tier 2 \u2014 MiniSearch)").action(async () => {
135
- const { memoryStartDaemonCommand } = await import("./memory-FZMACIGE.js");
135
+ const { memoryStartDaemonCommand } = await import("./memory-RLZR7XQY.js");
136
136
  await memoryStartDaemonCommand();
137
137
  });
138
138
  memory.command("stop-daemon").description("Stop the memory daemon").action(async () => {
139
- const { memoryStopDaemonCommand } = await import("./memory-FZMACIGE.js");
139
+ const { memoryStopDaemonCommand } = await import("./memory-RLZR7XQY.js");
140
140
  await memoryStopDaemonCommand();
141
141
  });
142
142
  memory.command("rebuild").description("Rebuild the daemon search index").action(async () => {
143
- const { memoryRebuildCommand } = await import("./memory-FZMACIGE.js");
143
+ const { memoryRebuildCommand } = await import("./memory-RLZR7XQY.js");
144
144
  await memoryRebuildCommand();
145
145
  });
146
146
  memory.command("mcp").description("Show MCP server setup instructions").action(async () => {
147
- const { memoryMcpCommand } = await import("./memory-FZMACIGE.js");
147
+ const { memoryMcpCommand } = await import("./memory-RLZR7XQY.js");
148
148
  await memoryMcpCommand();
149
149
  });
150
150
  memory.command("rm").description("Remove a project database").option("--project-id <id>", "Project ID to remove").option("--all", "Remove all project databases").option(
@@ -152,7 +152,7 @@ memory.command("rm").description("Remove a project database").option("--project-
152
152
  "Remove orphaned projects (source dir missing or 0 memories)"
153
153
  ).action(
154
154
  async (opts) => {
155
- const { memoryRmCommand } = await import("./memory-FZMACIGE.js");
155
+ const { memoryRmCommand } = await import("./memory-RLZR7XQY.js");
156
156
  await memoryRmCommand(opts);
157
157
  }
158
158
  );
@@ -1,12 +1,16 @@
1
+ import {
2
+ editCodex
3
+ } from "./chunk-CXG6I6ZF.js";
1
4
  import {
2
5
  ensureMemoryBuilt,
3
6
  isMemoryInitialized,
4
7
  memoryInitWizard
5
- } from "./chunk-G2P43ZK2.js";
8
+ } from "./chunk-5X3JZKYW.js";
9
+ import "./chunk-AYIREX73.js";
6
10
  import {
7
11
  memoryConfigMenu
8
- } from "./chunk-NZKIYVIK.js";
9
- import "./chunk-LK6HFD35.js";
12
+ } from "./chunk-3UIH5U3Y.js";
13
+ import "./chunk-K6EDQGDN.js";
10
14
  import {
11
15
  getLibPath
12
16
  } from "./chunk-RZRT7NGT.js";
@@ -741,6 +745,8 @@ async function initMenu(gitAvailable) {
741
745
  globalCfg,
742
746
  localCfg
743
747
  );
748
+ const codexScope = getScopeLabel("codex", globalCfg, localCfg);
749
+ const codexEnabled = getMergedValue("codex", globalCfg, localCfg)?.enabled;
744
750
  const projectRules = getExistingRules(claudeLocalSettingsPath());
745
751
  const userRules = getExistingRules(claudeSettingsPath());
746
752
  const allRules = getAllRules();
@@ -792,6 +798,11 @@ async function initMenu(gitAvailable) {
792
798
  value: "autoApprove",
793
799
  description: " Auto-approve safe tool calls, block destructive ones, prompt for ambiguous"
794
800
  },
801
+ {
802
+ name: `Codex cross-engine review ${formatScopeLabel(codexScope)}${codexEnabled !== void 0 ? ` (${codexEnabled ? "enabled" : "disabled"})` : ""}`,
803
+ value: "codex",
804
+ description: " Enable OpenAI Codex as 6th specialist in /cf-review (opt-in)"
805
+ },
795
806
  {
796
807
  name: `Permissions (${permissionStatus} rules)`,
797
808
  value: "permissions",
@@ -860,6 +871,9 @@ async function initMenu(gitAvailable) {
860
871
  }
861
872
  break;
862
873
  }
874
+ case "codex":
875
+ await editCodex(globalCfg, localCfg);
876
+ break;
863
877
  case "permissions": {
864
878
  const learnCfg = localCfg?.learn ?? globalCfg?.learn;
865
879
  const learnOutputDir = learnCfg?.outputDir || `${docsDir}/learn`;
@@ -1000,7 +1014,7 @@ async function initCommand() {
1000
1014
  console.log();
1001
1015
  log.congrats("Setup complete!");
1002
1016
  log.dim(
1003
- "Available commands: /cf-ask, /cf-plan, /cf-fix, /cf-commit, /cf-review, /cf-review-out, /cf-review-in, /cf-ship, /cf-optimize, /cf-scan, /cf-remember, /cf-learn, /cf-teach, /cf-research, /cf-session, /cf-warm, /cf-help"
1017
+ "Available commands: /cf-ask, /cf-plan, /cf-fix, /cf-commit, /cf-review, /cf-review-codex, /cf-review-out, /cf-review-in, /cf-ship, /cf-optimize, /cf-scan, /cf-remember, /cf-learn, /cf-teach, /cf-research, /cf-session, /cf-warm, /cf-help"
1004
1018
  );
1005
1019
  }
1006
1020
  export {
@@ -1,14 +1,21 @@
1
1
  import {
2
- detectMemoryMcpState,
2
+ checkLearnMcpHealth,
3
+ checkMemoryMcpHealth,
4
+ listMdFilesRecursive
5
+ } from "./chunk-TVAY4OQL.js";
6
+ import {
3
7
  ensureMemoryBuilt,
4
- printMemoryMcpConfig,
8
+ printMemoryMcpConfig
9
+ } from "./chunk-5X3JZKYW.js";
10
+ import {
11
+ detectMemoryMcpState,
5
12
  warnStaleMcpJson
6
- } from "./chunk-G2P43ZK2.js";
7
- import "./chunk-NZKIYVIK.js";
13
+ } from "./chunk-AYIREX73.js";
14
+ import "./chunk-3UIH5U3Y.js";
8
15
  import {
9
16
  resolveDocsDir,
10
17
  resolveMemoryDir
11
- } from "./chunk-LK6HFD35.js";
18
+ } from "./chunk-K6EDQGDN.js";
12
19
  import {
13
20
  getLibPath
14
21
  } from "./chunk-RZRT7NGT.js";
@@ -22,7 +29,9 @@ import {
22
29
  log,
23
30
  printBanner
24
31
  } from "./chunk-NREZK463.js";
25
- import "./chunk-5UVDWG5L.js";
32
+ import {
33
+ readJson
34
+ } from "./chunk-5UVDWG5L.js";
26
35
 
27
36
  // src/commands/mcp.ts
28
37
  import { existsSync, readdirSync } from "fs";
@@ -39,10 +48,28 @@ function countMdFiles(dir) {
39
48
  }
40
49
  return count;
41
50
  }
51
+ function printHealthSection(result) {
52
+ console.log(chalk.dim("\u2500\u2500\u2500 Health Check \u2500\u2500\u2500"));
53
+ for (const check of result.checks) {
54
+ if (check.ok) {
55
+ console.log(chalk.green(` \u2713 ${check.label}`));
56
+ } else if (check.warn) {
57
+ const detail = check.detail ? `: ${check.detail}` : "";
58
+ console.log(chalk.yellow(` \u26A0 ${check.label}${detail}`));
59
+ } else {
60
+ const detail = check.detail ? `: ${check.detail}` : "";
61
+ console.log(chalk.red(` \u2717 ${check.label}${detail}`));
62
+ if (check.fix) {
63
+ console.log(chalk.dim(` \u2192 ${check.fix}`));
64
+ }
65
+ }
66
+ }
67
+ console.log();
68
+ }
42
69
  async function mcpCommand(path) {
43
70
  warnStaleMcpJson(resolveMemoryDir());
44
71
  const docsDir = resolveDocsDir(path);
45
- const mcpDir = getLibPath("learn-mcp");
72
+ const learnMcpDir = getLibPath("learn-mcp");
46
73
  if (!existsSync(docsDir)) {
47
74
  log.error(`Docs folder not found: ${docsDir}`);
48
75
  log.dim("Run /cf-learn first to generate some docs.");
@@ -57,18 +84,18 @@ async function mcpCommand(path) {
57
84
  printBanner("\u{1F4DA} Learn MCP");
58
85
  log.info(`Docs folder: ${chalk.cyan(docsDir)}`);
59
86
  log.info(`Found: ${chalk.green(docCount)} docs`);
60
- if (!existsSync(join(mcpDir, "node_modules"))) {
87
+ if (!existsSync(join(learnMcpDir, "node_modules"))) {
61
88
  log.step("Installing MCP server dependencies (one-time setup)...");
62
- const result = run("npm", ["install", "--silent"], { cwd: mcpDir });
89
+ const result = run("npm", ["install", "--silent"], { cwd: learnMcpDir });
63
90
  if (result === null) {
64
91
  log.error("Failed to install dependencies");
65
92
  process.exit(1);
66
93
  }
67
94
  log.success("Done.");
68
95
  }
69
- if (!existsSync(join(mcpDir, "dist"))) {
96
+ if (!existsSync(join(learnMcpDir, "dist"))) {
70
97
  log.step("Building MCP server...");
71
- const result = run("npm", ["run", "build", "--silent"], { cwd: mcpDir });
98
+ const result = run("npm", ["run", "build", "--silent"], { cwd: learnMcpDir });
72
99
  if (result === null) {
73
100
  log.error("Failed to build MCP server");
74
101
  process.exit(1);
@@ -96,9 +123,19 @@ async function mcpCommand(path) {
96
123
  "Available tools & resources: https://cf.dinhanhthi.com/docs/cli/cf-mcp/"
97
124
  );
98
125
  console.log();
99
- printMemoryMcp();
126
+ const localMcpPath = join(process.cwd(), ".mcp.json");
127
+ const learnMcpDistPath = join(learnMcpDir, "dist", "index.js");
128
+ const learnHealth = await checkLearnMcpHealth({
129
+ readMcpJson: () => readJson(localMcpPath),
130
+ pathExists: existsSync,
131
+ listMdFiles: listMdFilesRecursive,
132
+ docsDir,
133
+ learnMcpDistPath
134
+ });
135
+ printHealthSection(learnHealth);
136
+ await printMemoryMcp();
100
137
  }
101
- function printMemoryMcp() {
138
+ async function printMemoryMcp() {
102
139
  const memoryDir = resolveMemoryDir();
103
140
  let mcpDir;
104
141
  try {
@@ -115,8 +152,24 @@ function printMemoryMcp() {
115
152
  log.info(`Memory dir: ${chalk.cyan(memoryDir)}`);
116
153
  console.log();
117
154
  printMemoryMcpConfig(memoryDir);
155
+ const localMcpPath = join(process.cwd(), ".mcp.json");
156
+ const memoryDistPath = join(mcpDir, "dist", "index.js");
157
+ let isDaemonRunning = async () => false;
158
+ try {
159
+ const proc = await import(join(mcpDir, "dist/daemon/process.js"));
160
+ isDaemonRunning = proc.isDaemonRunning;
161
+ } catch {
162
+ }
163
+ const memoryHealth = await checkMemoryMcpHealth({
164
+ readMcpJson: () => readJson(localMcpPath),
165
+ pathExists: existsSync,
166
+ isDaemonRunning,
167
+ memoryDistPath
168
+ });
169
+ printHealthSection(memoryHealth);
118
170
  }
119
171
  export {
120
172
  detectMemoryMcpState,
121
- mcpCommand
173
+ mcpCommand,
174
+ printHealthSection
122
175
  };
@@ -13,9 +13,10 @@ import {
13
13
  memoryStatusCommand,
14
14
  memoryStopDaemonCommand,
15
15
  printMemoryMcpConfig
16
- } from "./chunk-G2P43ZK2.js";
17
- import "./chunk-NZKIYVIK.js";
18
- import "./chunk-LK6HFD35.js";
16
+ } from "./chunk-5X3JZKYW.js";
17
+ import "./chunk-AYIREX73.js";
18
+ import "./chunk-3UIH5U3Y.js";
19
+ import "./chunk-K6EDQGDN.js";
19
20
  import "./chunk-RZRT7NGT.js";
20
21
  import "./chunk-OYPOADCX.js";
21
22
  import "./chunk-Z5Q533UG.js";
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  loadConfig
3
- } from "./chunk-LK6HFD35.js";
3
+ } from "./chunk-K6EDQGDN.js";
4
4
  import "./chunk-OYPOADCX.js";
5
5
  import {
6
6
  claudeSessionDir,