coding-friend-cli 1.34.1 → 1.35.1

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 (35) hide show
  1. package/README.md +1 -0
  2. package/dist/chunk-2DTXVMPQ.js +26 -0
  3. package/dist/{chunk-DHH6SRXV.js → chunk-57EGAXYI.js} +3 -2
  4. package/dist/{chunk-AZ6AK5M4.js → chunk-B2NFXRFP.js} +12 -2
  5. package/dist/{chunk-QCKIBJF6.js → chunk-C5YWESSF.js} +2 -6
  6. package/dist/chunk-E5J24GD5.js +190 -0
  7. package/dist/{chunk-G4QGJEV7.js → chunk-H5TXJZD7.js} +6 -6
  8. package/dist/{chunk-FAANBGCB.js → chunk-L53RNEV3.js} +116 -73
  9. package/dist/{chunk-DMJCO6LJ.js → chunk-RLUNNJWJ.js} +2 -1
  10. package/dist/{chunk-LXZ47XCD.js → chunk-WNTMKIKC.js} +1 -1
  11. package/dist/chunk-WSUYLIYX.js +61 -0
  12. package/dist/clean-67ACB4OS.js +95 -0
  13. package/dist/{config-44VQBNMS.js → config-HTQAALMA.js} +181 -120
  14. package/dist/{dev-QXVERU42.js → dev-PFUF67SH.js} +7 -7
  15. package/dist/{disable-ZWQVU3QZ.js → disable-CDOM7SSI.js} +2 -2
  16. package/dist/{enable-NXP6APNI.js → enable-SF7742UR.js} +2 -2
  17. package/dist/{host-6IBKKCPA.js → host-HKA4OHBM.js} +3 -3
  18. package/dist/index.js +38 -34
  19. package/dist/{init-KY3ZPEWP.js → init-YM7YEEGK.js} +153 -85
  20. package/dist/{install-22BLHQ6D.js → install-VYZSTXN6.js} +8 -8
  21. package/dist/{mcp-OGZAZZRF.js → mcp-7MO2MCLE.js} +69 -66
  22. package/dist/{mcp-serve-learn-UNW6VH5M.js → mcp-serve-learn-OKVC5WV3.js} +19 -3
  23. package/dist/{memory-JM45DEIX.js → memory-XALEMWQF.js} +7 -7
  24. package/dist/{permission-DNW7DE24.js → permission-C5R3LMZQ.js} +2 -2
  25. package/dist/postinstall.js +1 -1
  26. package/dist/{session-RCEE6P35.js → session-M4KRQ4VU.js} +2 -2
  27. package/dist/{status-NMSVOSC4.js → status-2XM5OZDR.js} +19 -14
  28. package/dist/{statusline-GKVAX7GR.js → statusline-VFIA7OS2.js} +3 -3
  29. package/dist/{uninstall-6ZJRS2H4.js → uninstall-QKEC3O64.js} +6 -6
  30. package/dist/{update-P5ZR2JVQ.js → update-IV4Y3T72.js} +6 -6
  31. package/package.json +3 -2
  32. package/dist/chunk-CZDXMFEZ.js +0 -118
  33. package/dist/chunk-FI5HEQ43.js +0 -85
  34. package/dist/{chunk-Z5Q533UG.js → chunk-HPNRQYLM.js} +3 -3
  35. package/dist/{update-check-5SCD2JR2.js → update-check-BPCQ25AR.js} +3 -3
package/README.md CHANGED
@@ -101,6 +101,7 @@ cf memory config # Configure memory system settings
101
101
  cf memory mcp # Show MCP server config for clients
102
102
  cf status # Show comprehensive Coding Friend status
103
103
  # (versions, plugin, memory, config)
104
+ cf clean # Clean files generated by Coding Friend in docs/
104
105
  cf session save # Save current Claude Code session to docs/sessions/
105
106
  cf session load # Load a saved session from docs/sessions/
106
107
  cf help # Show all commands
@@ -0,0 +1,26 @@
1
+ // src/lib/fs-utils.ts
2
+ import { readdirSync } from "fs";
3
+ import { join } from "path";
4
+ function listMdFilesRecursive(dir, maxDepth = 15) {
5
+ try {
6
+ const results = [];
7
+ for (const entry of readdirSync(dir, { withFileTypes: true })) {
8
+ if (entry.isDirectory()) {
9
+ if (maxDepth > 0) {
10
+ results.push(
11
+ ...listMdFilesRecursive(join(dir, entry.name), maxDepth - 1)
12
+ );
13
+ }
14
+ } else if (entry.name.endsWith(".md") && entry.name !== "README.md") {
15
+ results.push(entry.name);
16
+ }
17
+ }
18
+ return results;
19
+ } catch {
20
+ return [];
21
+ }
22
+ }
23
+
24
+ export {
25
+ listMdFilesRecursive
26
+ };
@@ -24,7 +24,7 @@ var DEFAULT_CONFIG = {
24
24
  docsDir: "docs",
25
25
  learn: {
26
26
  language: "en",
27
- outputDir: "docs/learn",
27
+ outputDir: "~/.coding-friend/learn",
28
28
  categories: [
29
29
  {
30
30
  name: "concepts",
@@ -42,7 +42,8 @@ var DEFAULT_CONFIG = {
42
42
  }
43
43
  ],
44
44
  autoCommit: false,
45
- readmeIndex: false
45
+ readmeIndex: false,
46
+ disabled: false
46
47
  }
47
48
  };
48
49
 
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  DEFAULT_CONFIG
3
- } from "./chunk-DHH6SRXV.js";
3
+ } from "./chunk-57EGAXYI.js";
4
4
  import {
5
5
  globalConfigPath,
6
6
  localConfigPath,
@@ -40,7 +40,8 @@ var LearnConfigSchema = z.object({
40
40
  outputDir: z.string().optional(),
41
41
  categories: z.array(LearnCategorySchema).optional(),
42
42
  autoCommit: z.boolean().optional(),
43
- readmeIndex: z.union([z.boolean(), z.literal("per-category")]).optional()
43
+ readmeIndex: z.union([z.boolean(), z.literal("per-category")]).optional(),
44
+ disabled: z.boolean().optional()
44
45
  });
45
46
  var StatuslineComponentSchema = z.enum([
46
47
  "version",
@@ -69,6 +70,7 @@ var MemoryConfigSchema = z.object({
69
70
  var ConfigSchema = z.strictObject({
70
71
  language: z.string().optional(),
71
72
  docsDir: z.string().optional(),
73
+ tdd: z.boolean().optional(),
72
74
  learn: LearnConfigSchema.optional(),
73
75
  statusline: StatuslineConfigSchema.optional(),
74
76
  memory: MemoryConfigSchema.optional(),
@@ -79,6 +81,7 @@ var ConfigSchema = z.strictObject({
79
81
  var KNOWN_KEYS = [
80
82
  "language",
81
83
  "docsDir",
84
+ "tdd",
82
85
  "learn",
83
86
  "statusline",
84
87
  "memory",
@@ -195,6 +198,12 @@ function resolveDocsDir(explicitPath) {
195
198
  }
196
199
  return resolvePath("docs/learn");
197
200
  }
201
+ function resolveLearnDir(globalCfg) {
202
+ if (globalCfg?.learn?.outputDir) {
203
+ return resolvePath(globalCfg.learn.outputDir);
204
+ }
205
+ return resolvePath("~/.coding-friend/learn");
206
+ }
198
207
  function resolveMemoryDir(explicitPath) {
199
208
  if (explicitPath) {
200
209
  return resolvePath(explicitPath);
@@ -210,5 +219,6 @@ export {
210
219
  sanitizeRawConfig,
211
220
  loadConfig,
212
221
  resolveDocsDir,
222
+ resolveLearnDir,
213
223
  resolveMemoryDir
214
224
  };
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  resolveMemoryDir
3
- } from "./chunk-AZ6AK5M4.js";
3
+ } from "./chunk-B2NFXRFP.js";
4
4
  import {
5
5
  getLibPath
6
6
  } from "./chunk-RZRT7NGT.js";
@@ -9,7 +9,7 @@ import {
9
9
  askScope,
10
10
  formatScopeLabel,
11
11
  injectBackChoice
12
- } from "./chunk-Z5Q533UG.js";
12
+ } from "./chunk-HPNRQYLM.js";
13
13
  import {
14
14
  globalConfigPath,
15
15
  localConfigPath
@@ -352,10 +352,6 @@ async function memoryConfigMenu(opts) {
352
352
  }
353
353
 
354
354
  export {
355
- editMemoryTier,
356
- editMemoryAutoCapture,
357
- editMemoryAutoStart,
358
- editMemoryEmbedding,
359
355
  writeMemoryMcpEntry,
360
356
  getMemoryMcpStatus,
361
357
  memoryConfigMenu
@@ -0,0 +1,190 @@
1
+ import {
2
+ writeMemoryMcpEntry
3
+ } from "./chunk-C5YWESSF.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
+ // src/lib/mcp-health.ts
83
+ import chalk2 from "chalk";
84
+ function printHealthSection(result) {
85
+ console.log(chalk2.dim("\u2500\u2500\u2500 Health Check \u2500\u2500\u2500"));
86
+ for (const check of result.checks) {
87
+ if (check.ok) {
88
+ const detail = check.detail ? `: ${check.detail}` : "";
89
+ console.log(chalk2.green(` \u2713 ${check.label}${detail}`));
90
+ } else if (check.warn) {
91
+ const detail = check.detail ? `: ${check.detail}` : "";
92
+ console.log(chalk2.yellow(` \u26A0 ${check.label}${detail}`));
93
+ } else {
94
+ const detail = check.detail ? `: ${check.detail}` : "";
95
+ console.log(chalk2.red(` \u2717 ${check.label}${detail}`));
96
+ if (check.fix) {
97
+ console.log(chalk2.dim(` \u2192 ${check.fix}`));
98
+ }
99
+ }
100
+ }
101
+ console.log();
102
+ }
103
+ async function checkMemoryMcpHealth(deps) {
104
+ const checks = [];
105
+ const mcpJson = deps.readMcpJson();
106
+ 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") {
110
+ checks.push({
111
+ label: "Config (.mcp.json)",
112
+ ok: false,
113
+ detail: "coding-friend-memory not configured",
114
+ fix: 'Run "cf memory mcp" to add the MCP entry'
115
+ });
116
+ } else {
117
+ const detail = state.kind === "stale" ? `Stale path: ${state.path}` : `Absolute path (legacy): ${state.path}`;
118
+ checks.push({
119
+ label: "Config (.mcp.json)",
120
+ ok: false,
121
+ detail,
122
+ fix: 'Run "cf memory mcp" to update to the npx format'
123
+ });
124
+ }
125
+ const distExists = deps.pathExists(deps.memoryDistPath);
126
+ checks.push({
127
+ label: "Package built",
128
+ ok: distExists,
129
+ ...distExists ? {} : {
130
+ detail: "cf-memory not built",
131
+ fix: 'Run "cf memory init" to build'
132
+ }
133
+ });
134
+ let daemonRunning = false;
135
+ let daemonCheckError;
136
+ try {
137
+ daemonRunning = await deps.isDaemonRunning();
138
+ } catch (err) {
139
+ daemonCheckError = err instanceof Error ? err.message : String(err);
140
+ }
141
+ checks.push({
142
+ label: "Daemon",
143
+ ok: daemonRunning,
144
+ detail: daemonRunning ? "running" : daemonCheckError ? `check failed: ${daemonCheckError}` : "stopped (starts automatically on MCP connect)",
145
+ ...!daemonRunning ? { warn: true } : {}
146
+ });
147
+ const ok = checks.every((c) => c.ok || c.warn === true);
148
+ return { checks, ok };
149
+ }
150
+ async function checkLearnMcpHealth(deps) {
151
+ const checks = [];
152
+ const isRegistered = deps.checkRegistered();
153
+ checks.push({
154
+ label: "Registered (claude mcp)",
155
+ ok: isRegistered,
156
+ ...isRegistered ? {} : {
157
+ detail: "coding-friend-learn not registered",
158
+ fix: 'Run "cf mcp" to register'
159
+ }
160
+ });
161
+ const distExists = deps.pathExists(deps.learnMcpDistPath);
162
+ checks.push({
163
+ label: "Package built",
164
+ ok: distExists,
165
+ ...distExists ? {} : {
166
+ detail: "learn-mcp not built",
167
+ fix: 'Run "cf mcp" to install and build'
168
+ }
169
+ });
170
+ const mdFiles = deps.listMdFiles(deps.docsDir);
171
+ const hasDocs = mdFiles.length > 0;
172
+ checks.push({
173
+ label: "Docs directory",
174
+ ok: hasDocs,
175
+ ...hasDocs ? {} : {
176
+ detail: `No .md files found in ${deps.docsDir}`,
177
+ fix: 'Run "/cf-learn" to generate docs'
178
+ }
179
+ });
180
+ const ok = checks.every((c) => c.ok || c.warn === true);
181
+ return { checks, ok };
182
+ }
183
+
184
+ export {
185
+ warnStaleMcpJson,
186
+ detectMemoryMcpState,
187
+ printHealthSection,
188
+ checkMemoryMcpHealth,
189
+ checkLearnMcpHealth
190
+ };
@@ -1,22 +1,22 @@
1
1
  import {
2
2
  ensureStatusline,
3
3
  getInstalledVersion
4
- } from "./chunk-LXZ47XCD.js";
4
+ } from "./chunk-WNTMKIKC.js";
5
5
  import {
6
6
  ensureShellCompletion
7
- } from "./chunk-DMJCO6LJ.js";
7
+ } from "./chunk-RLUNNJWJ.js";
8
8
  import {
9
9
  resolveScope
10
- } from "./chunk-Z5Q533UG.js";
11
- import {
12
- claudeSettingsPath
13
- } from "./chunk-TWKNGPBO.js";
10
+ } from "./chunk-HPNRQYLM.js";
14
11
  import {
15
12
  commandExists,
16
13
  run,
17
14
  runWithStderr,
18
15
  sleepSync
19
16
  } from "./chunk-EVGXUDX4.js";
17
+ import {
18
+ claudeSettingsPath
19
+ } from "./chunk-TWKNGPBO.js";
20
20
  import {
21
21
  log,
22
22
  printBanner
@@ -1,38 +1,37 @@
1
1
  import {
2
+ checkMemoryMcpHealth,
3
+ printHealthSection,
2
4
  warnStaleMcpJson
3
- } from "./chunk-FI5HEQ43.js";
5
+ } from "./chunk-E5J24GD5.js";
4
6
  import {
5
- editMemoryAutoCapture,
6
- editMemoryAutoStart,
7
- editMemoryEmbedding,
8
- editMemoryTier,
9
7
  getMemoryMcpStatus,
10
8
  memoryConfigMenu,
11
9
  writeMemoryMcpEntry
12
- } from "./chunk-QCKIBJF6.js";
10
+ } from "./chunk-C5YWESSF.js";
13
11
  import {
14
12
  loadConfig,
15
13
  resolveMemoryDir
16
- } from "./chunk-AZ6AK5M4.js";
14
+ } from "./chunk-B2NFXRFP.js";
17
15
  import {
18
16
  getLibPath
19
17
  } from "./chunk-RZRT7NGT.js";
20
18
  import {
21
19
  showConfigHint
22
- } from "./chunk-Z5Q533UG.js";
23
- import {
24
- globalConfigPath,
25
- localConfigPath
26
- } from "./chunk-TWKNGPBO.js";
20
+ } from "./chunk-HPNRQYLM.js";
27
21
  import {
28
22
  run,
29
23
  runWithStderr
30
24
  } from "./chunk-EVGXUDX4.js";
25
+ import {
26
+ globalConfigPath,
27
+ localConfigPath
28
+ } from "./chunk-TWKNGPBO.js";
31
29
  import {
32
30
  log,
33
31
  printBanner
34
32
  } from "./chunk-NREZK463.js";
35
33
  import {
34
+ mergeJson,
36
35
  readJson
37
36
  } from "./chunk-5UVDWG5L.js";
38
37
 
@@ -336,19 +335,38 @@ async function memoryRebuildCommand() {
336
335
  const config = loadConfig();
337
336
  const embedding = config.memory?.embedding;
338
337
  const opts = embedding ? { embedding, skipVec: false } : { skipVec: false };
339
- const backend = new SqliteBackend(memoryDir, opts);
340
- try {
341
- await backend.rebuild();
342
- const stats = await backend.stats();
343
- log.success(`Rebuilt: ${stats.total} memories indexed.`);
344
- if (backend.isVecEnabled()) {
345
- log.info(`Vector search: ${chalk.green("enabled")}`);
338
+ const runRebuild = async () => {
339
+ const backend = new SqliteBackend(memoryDir, opts);
340
+ try {
341
+ await backend.rebuild();
342
+ const stats = await backend.stats();
343
+ log.success(`Rebuilt: ${stats.total} memories indexed.`);
344
+ if (backend.isVecEnabled()) {
345
+ log.info(`Vector search: ${chalk.green("enabled")}`);
346
+ }
347
+ if (!backend.isRebuildNeeded()) {
348
+ log.info("Embedding dimensions: up to date");
349
+ }
350
+ } finally {
351
+ await backend.close();
346
352
  }
347
- if (!backend.isRebuildNeeded()) {
348
- log.info("Embedding dimensions: up to date");
353
+ };
354
+ try {
355
+ await runRebuild();
356
+ } catch (err) {
357
+ const isCorrupt = err instanceof Error && (err.message.includes("database disk image is malformed") || err.code === "SQLITE_CORRUPT" || err.code === "SQLITE_IOERR_SHORT_READ");
358
+ if (isCorrupt) {
359
+ const dbPath = getDbPath(memoryDir);
360
+ if (dbPath) {
361
+ log.warn("SQLite database is corrupt. Deleting and recreating...");
362
+ for (const suffix of ["", "-shm", "-wal"]) {
363
+ rmSync(dbPath + suffix, { force: true });
364
+ }
365
+ }
366
+ await runRebuild();
367
+ } else {
368
+ throw err;
349
369
  }
350
- } finally {
351
- await backend.close();
352
370
  }
353
371
  return;
354
372
  }
@@ -422,69 +440,80 @@ function isMemoryInitialized() {
422
440
  return getDbPath(memoryDir) !== null;
423
441
  }
424
442
  async function memoryInitWizard(memoryDir, mcpDir) {
425
- log.step("Step 1/4: Search tier");
426
- await editMemoryTier(
427
- readJson(globalConfigPath()),
428
- readJson(localConfigPath())
429
- );
443
+ const globalCfg = readJson(globalConfigPath());
444
+ const globalMem = globalCfg?.memory ?? {};
445
+ const tier = globalMem.tier ?? "auto";
446
+ const embedding = globalMem.embedding ?? { provider: "transformers" };
447
+ const autoCapture = globalMem.autoCapture ?? true;
448
+ const autoStart = globalMem.autoStart ?? true;
449
+ const existingLocalCfg = readJson(localConfigPath());
450
+ const existingMem = existingLocalCfg?.memory ?? {};
451
+ mergeJson(localConfigPath(), {
452
+ memory: { ...existingMem, tier, embedding, autoCapture, autoStart }
453
+ });
454
+ const inherited = chalk.dim("\u2190 global config");
455
+ const defaulted = chalk.dim("\u2190 default");
456
+ const src = (field) => field !== void 0 ? inherited : defaulted;
430
457
  console.log();
431
- log.step("Step 2/4: Embedding provider");
432
- await editMemoryEmbedding(
433
- readJson(globalConfigPath()),
434
- readJson(localConfigPath())
458
+ log.info(`Search tier: ${chalk.cyan(String(tier))} ${src(globalMem.tier)}`);
459
+ log.dim(
460
+ " Auto-detects best backend: SQLite (Tier 1) \u2192 MiniSearch daemon (Tier 2) \u2192 Grep (Tier 3)."
435
461
  );
436
- console.log();
437
- log.step("Step 3/4: Auto-capture");
438
- await editMemoryAutoCapture(
439
- readJson(globalConfigPath()),
440
- readJson(localConfigPath())
462
+ log.info(
463
+ `Embedding: ${chalk.cyan(String(embedding.provider ?? "transformers"))} ${src(globalMem.embedding)}`
441
464
  );
442
- console.log();
443
- log.step("Step 4/4: Auto-start daemon");
444
- await editMemoryAutoStart(
445
- readJson(globalConfigPath()),
446
- readJson(localConfigPath())
465
+ log.dim(
466
+ " Powers semantic search in Tier 1. Runs locally, no external service needed."
467
+ );
468
+ log.info(
469
+ `Auto-capture: ${autoCapture ? chalk.green("on") : chalk.dim("off")} ${src(globalMem.autoCapture)}`
470
+ );
471
+ log.dim(
472
+ " Saves session summaries to memory when Claude Code compacts context."
473
+ );
474
+ log.info(
475
+ `Auto-start daemon: ${autoStart ? chalk.green("on") : chalk.dim("off")} ${src(globalMem.autoStart)}`
476
+ );
477
+ log.dim(
478
+ " Background process watches docs/memory/ and auto-rebuilds the search index on file changes."
447
479
  );
448
480
  console.log();
449
481
  const config = loadConfig();
450
- const tier = config.memory?.tier ?? "auto";
451
- if (tier === "markdown") {
482
+ const resolvedTier = config.memory?.tier ?? "auto";
483
+ if (resolvedTier === "markdown") {
452
484
  await setupMemoryMcp(memoryDir, mcpDir);
453
- log.success(
454
- 'Memory initialized with Tier 3 (markdown). Run "cf memory status" to verify.'
455
- );
485
+ console.log('\u{1F389} Memory initialized! Run "cf memory status" to verify.');
486
+ printInitDone();
456
487
  return;
457
488
  }
458
- if (tier === "lite") {
489
+ if (resolvedTier === "lite") {
459
490
  await setupMemoryMcp(memoryDir, mcpDir);
460
- log.success(
461
- 'Memory initialized. Run "cf memory start-daemon" to enable Tier 2 search.'
462
- );
491
+ console.log('\u{1F389} Memory initialized! Run "cf memory status" to verify.');
492
+ printInitDone();
463
493
  return;
464
494
  }
465
495
  const depsOk = await ensureSqliteDepsIfNeeded(mcpDir);
466
496
  if (!depsOk) return;
467
497
  if (!existsSync(memoryDir)) {
468
- log.info(
469
- "No memory directory found. Memories will be indexed as they're created."
470
- );
471
498
  await setupMemoryMcp(memoryDir, mcpDir);
472
- log.success('Memory initialized. Run "cf memory status" to verify.');
499
+ console.log('\u{1F389} Memory initialized! Run "cf memory status" to verify.');
500
+ printInitDone();
473
501
  return;
474
502
  }
475
503
  const docCount = countMdFiles(memoryDir);
476
504
  if (docCount === 0) {
477
505
  log.info("No existing memories to import.");
478
506
  await setupMemoryMcp(memoryDir, mcpDir);
479
- log.success('Memory initialized. Run "cf memory status" to verify.');
507
+ console.log('\u{1F389} Memory initialized! Run "cf memory status" to verify.');
508
+ printInitDone();
480
509
  return;
481
510
  }
482
511
  log.step(`Importing ${docCount} existing memories into SQLite...`);
483
512
  const { SqliteBackend } = await import(join(mcpDir, "dist/backends/sqlite/index.js"));
484
- const embedding = config.memory?.embedding;
513
+ const configEmbedding = config.memory?.embedding;
485
514
  const backend = new SqliteBackend(memoryDir, {
486
515
  skipVec: false,
487
- ...embedding && { embedding }
516
+ ...configEmbedding && { embedding: configEmbedding }
488
517
  });
489
518
  try {
490
519
  await backend.rebuild();
@@ -498,10 +527,20 @@ async function memoryInitWizard(memoryDir, mcpDir) {
498
527
  }
499
528
  await setupMemoryMcp(memoryDir, mcpDir);
500
529
  console.log();
501
- log.success('Memory initialized! Run "cf memory status" to verify.');
530
+ console.log('\u{1F389} Memory initialized! Run "cf memory status" to verify.');
502
531
  log.info(
503
532
  `Tip: Run ${chalk.cyan("/cf-scan")} in Claude Code to populate memory with project knowledge.`
504
533
  );
534
+ log.dim(` Change settings anytime: ${chalk.cyan("cf memory config")}`);
535
+ log.dim(
536
+ ` Documentation: ${chalk.cyan("https://cf.dinhanhthi.com/docs/cli/cf-memory/")}`
537
+ );
538
+ }
539
+ function printInitDone() {
540
+ log.dim(` Change settings anytime: ${chalk.cyan("cf memory config")}`);
541
+ log.dim(
542
+ ` Documentation: ${chalk.cyan("https://cf.dinhanhthi.com/docs/cli/cf-memory/")}`
543
+ );
505
544
  }
506
545
  async function setupMemoryMcp(memoryDir, _mcpDir) {
507
546
  const mcpStatus = getMemoryMcpStatus();
@@ -509,20 +548,10 @@ async function setupMemoryMcp(memoryDir, _mcpDir) {
509
548
  log.info(`MCP: ${chalk.green("already configured")} in .mcp.json`);
510
549
  return;
511
550
  }
512
- console.log();
513
- log.step("MCP setup");
551
+ writeMemoryMcpEntry(memoryDir);
514
552
  log.dim(
515
- "The Memory MCP connects Claude Code to the memory system so skills can store and search memories."
553
+ " Connects Claude Code to memory so skills can store and retrieve knowledge."
516
554
  );
517
- const addMcp = await confirm({
518
- message: "Add coding-friend-memory to .mcp.json?",
519
- default: true
520
- });
521
- if (!addMcp) {
522
- log.dim('Skipped. Run "cf memory mcp" anytime to get the config.');
523
- return;
524
- }
525
- writeMemoryMcpEntry(memoryDir);
526
555
  }
527
556
  async function memoryInitCommand() {
528
557
  const memoryDir = getMemoryDir();
@@ -541,9 +570,8 @@ async function memoryInitCommand() {
541
570
  return;
542
571
  }
543
572
  console.log();
544
- printBanner("\u{1F9E0} Memory Setup");
573
+ printBanner("\u{1F9E0} CF Memory Setup");
545
574
  console.log();
546
- showConfigHint();
547
575
  await memoryInitWizard(memoryDir, mcpDir);
548
576
  }
549
577
  async function memoryConfigCommand() {
@@ -808,6 +836,21 @@ async function memoryMcpCommand() {
808
836
  ensureMemoryBuilt(mcpDir);
809
837
  warnStaleMcpJson(memoryDir);
810
838
  printMemoryMcpConfig(memoryDir);
839
+ const localMcpPath = join(process.cwd(), ".mcp.json");
840
+ const memoryDistPath = join(mcpDir, "dist", "index.js");
841
+ let isDaemonRunning = async () => false;
842
+ try {
843
+ const proc = await import(join(mcpDir, "dist/daemon/process.js"));
844
+ isDaemonRunning = proc.isDaemonRunning;
845
+ } catch {
846
+ }
847
+ const memoryHealth = await checkMemoryMcpHealth({
848
+ readMcpJson: () => readJson(localMcpPath),
849
+ pathExists: existsSync,
850
+ isDaemonRunning,
851
+ memoryDistPath
852
+ });
853
+ printHealthSection(memoryHealth);
811
854
  }
812
855
 
813
856
  export {
@@ -21,7 +21,7 @@ ${MARKER_START}
21
21
  _cf_completions() {
22
22
  local cur="\${COMP_WORDS[COMP_CWORD]}"
23
23
  local prev="\${COMP_WORDS[COMP_CWORD-1]}"
24
- local commands="install uninstall disable enable init config host mcp memory permission statusline update status dev session"
24
+ local commands="install uninstall disable enable init config host mcp memory permission statusline update status clean dev session"
25
25
  local scope_flags="--user --global --project --local"
26
26
  local update_flags="--cli --plugin --statusline --user --global --project --local"
27
27
 
@@ -89,6 +89,7 @@ var ZSH_FUNCTION_BODY = `_cf() {
89
89
  'update:Update coding-friend plugin and refresh statusline'
90
90
  'dev:Switch between local and remote plugin for development'
91
91
  'status:Show comprehensive Coding Friend status'
92
+ 'clean:Clean files generated by Coding Friend in docs/'
92
93
  'session:Save and load Claude Code sessions across machines'
93
94
  )
94
95
 
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  ALL_COMPONENT_IDS,
3
3
  STATUSLINE_COMPONENTS
4
- } from "./chunk-DHH6SRXV.js";
4
+ } from "./chunk-57EGAXYI.js";
5
5
  import {
6
6
  claudeSettingsPath,
7
7
  globalConfigPath,