coding-friend-cli 1.22.0 → 1.23.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 (35) hide show
  1. package/dist/{chunk-PDVSGGH6.js → chunk-2ETE7NEL.js} +32 -28
  2. package/dist/{chunk-PYTEAC6K.js → chunk-DMJCO6LJ.js} +1 -1
  3. package/dist/{chunk-LCABODSZ.js → chunk-LTHJTWJ6.js} +2 -2
  4. package/dist/chunk-NREZK463.js +41 -0
  5. package/dist/{chunk-BIX7AJU3.js → chunk-O4CNZB5H.js} +1 -1
  6. package/dist/{chunk-TPLH46BH.js → chunk-WCSNAEOF.js} +58 -46
  7. package/dist/{config-K2S23A5R.js → config-5DKOGWWJ.js} +8 -14
  8. package/dist/{dev-4A5CTBGK.js → dev-S7S3SFKI.js} +19 -18
  9. package/dist/{disable-FNMRPUO7.js → disable-V7OLXEUH.js} +2 -2
  10. package/dist/{enable-HUKTMK5U.js → enable-OLHRKZCV.js} +2 -2
  11. package/dist/{host-E644BDZK.js → host-RR6FT7AX.js} +4 -3
  12. package/dist/index.js +31 -31
  13. package/dist/{init-FMPOE2MN.js → init-6B2OHKFX.js} +15 -19
  14. package/dist/{install-5ZBHH2OM.js → install-BURZ5QEM.js} +7 -6
  15. package/dist/{mcp-SQWZXF5Q.js → mcp-CB4PUUES.js} +55 -32
  16. package/dist/{memory-7WLVIFAZ.js → memory-TJZNGTOA.js} +4 -4
  17. package/dist/{permission-BEC6YZQA.js → permission-QIJLEMJ6.js} +4 -3
  18. package/dist/postinstall.js +2 -2
  19. package/dist/{session-ZDJ2APGL.js → session-IECOM63R.js} +1 -1
  20. package/dist/{status-CGYNNMYF.js → status-ZZRJ3VGF.js} +4 -4
  21. package/dist/{statusline-AISH77XP.js → statusline-YL44XYGF.js} +4 -3
  22. package/dist/{uninstall-RJ6SBB76.js → uninstall-OXWRPWYF.js} +7 -6
  23. package/dist/{update-536C3YG7.js → update-6N7EASFT.js} +4 -4
  24. package/dist/{update-check-L5TNWT2J.js → update-check-XSETLRVR.js} +1 -1
  25. package/lib/cf-memory/README.md +1 -1
  26. package/lib/cf-memory/package.json +0 -1
  27. package/lib/cf-memory/src/index.ts +8 -1
  28. package/lib/learn-host/package.json +0 -1
  29. package/lib/learn-mcp/package.json +0 -1
  30. package/lib/learn-mcp/src/index.ts +8 -1
  31. package/package.json +1 -1
  32. package/dist/chunk-W5CD7WTX.js +0 -15
  33. package/lib/cf-memory/CHANGELOG.md +0 -48
  34. package/lib/learn-host/CHANGELOG.md +0 -35
  35. package/lib/learn-mcp/CHANGELOG.md +0 -16
@@ -4,10 +4,10 @@ import {
4
4
  } from "./chunk-TRIALFOQ.js";
5
5
  import {
6
6
  ensureShellCompletion
7
- } from "./chunk-PYTEAC6K.js";
7
+ } from "./chunk-DMJCO6LJ.js";
8
8
  import {
9
9
  resolveScope
10
- } from "./chunk-BIX7AJU3.js";
10
+ } from "./chunk-O4CNZB5H.js";
11
11
  import {
12
12
  commandExists,
13
13
  run,
@@ -21,8 +21,9 @@ import {
21
21
  claudeSettingsPath
22
22
  } from "./chunk-DO6FV6BL.js";
23
23
  import {
24
- log
25
- } from "./chunk-W5CD7WTX.js";
24
+ log,
25
+ printBanner
26
+ } from "./chunk-NREZK463.js";
26
27
 
27
28
  // src/commands/update.ts
28
29
  import { readFileSync } from "fs";
@@ -85,7 +86,7 @@ function getStatuslineVersion() {
85
86
  const match = sl.command.match(
86
87
  /coding-friend-marketplace\/coding-friend\/([^/]+)\//
87
88
  );
88
- return match?.[1] ?? null;
89
+ return match?.[1]?.replace(/^v/, "") ?? null;
89
90
  }
90
91
  async function updateCommand(opts) {
91
92
  const updateAll = !opts.cli && !opts.plugin && !opts.statusline;
@@ -99,26 +100,38 @@ async function updateCommand(opts) {
99
100
  } else if (doPlugin && !hasScopeFlag) {
100
101
  scope = "user";
101
102
  }
102
- console.log("=== \u{1F33F} Coding Friend Update \u{1F33F} ===");
103
+ printBanner("\u2728 Coding Friend Update \u2728");
103
104
  console.log();
104
105
  const currentVersion = getInstalledVersion();
105
106
  const latestVersion = getLatestVersion();
106
107
  const statuslineVersion = getStatuslineVersion();
107
108
  const cliVersion = getCliVersion();
108
109
  const latestCliVersion = getLatestCliVersion();
109
- log.info(
110
- `Plugin version: ${currentVersion ? `v${currentVersion}` : chalk.yellow("not found")}`
111
- );
112
- log.info(
113
- `Latest plugin version: ${latestVersion ? chalk.green(`v${latestVersion}`) : chalk.yellow("unknown (cannot reach GitHub)")}`
114
- );
115
- log.info(`CLI version: v${cliVersion}`);
116
- log.info(
117
- `Latest CLI version: ${latestCliVersion ? chalk.green(`v${latestCliVersion}`) : chalk.yellow("unknown (cannot reach npm)")}`
118
- );
119
- log.info(
120
- `Statusline version: ${statuslineVersion ? chalk.green(`v${statuslineVersion}`) : chalk.yellow("not configured")}`
121
- );
110
+ const VERSION_COL = 13;
111
+ const pad = (label, width) => ` ${label}${" ".repeat(Math.max(1, width - label.length))}`;
112
+ const versionLine = (label, current, latest) => {
113
+ const padded = pad(label, VERSION_COL);
114
+ if (!current) return `${padded}${chalk.dim("not installed")}`;
115
+ if (!latest)
116
+ return `${padded}${current} ${chalk.dim("(latest unavailable)")}`;
117
+ const cmp = semverCompare(
118
+ current.replace(/^v/, ""),
119
+ latest.replace(/^v/, "")
120
+ );
121
+ const indicator = cmp >= 0 ? chalk.green("\u2714 up to date") : chalk.yellow("\u26A0 update available");
122
+ return `${padded}${current} \u2192 ${latest} ${indicator}`;
123
+ };
124
+ console.log(versionLine("Plugin", currentVersion, latestVersion));
125
+ console.log(versionLine("CLI", cliVersion, latestCliVersion));
126
+ if (statuslineVersion) {
127
+ console.log(
128
+ versionLine("Statusline", statuslineVersion, currentVersion ?? null)
129
+ );
130
+ } else {
131
+ console.log(
132
+ `${pad("Statusline", VERSION_COL)}${chalk.dim("not configured")}`
133
+ );
134
+ }
122
135
  console.log();
123
136
  if (doPlugin) {
124
137
  if (!latestVersion) {
@@ -132,9 +145,6 @@ async function updateCommand(opts) {
132
145
  } else {
133
146
  const cmp = semverCompare(currentVersion, latestVersion);
134
147
  if (cmp === 0) {
135
- log.success(
136
- `Plugin already on the latest version (${chalk.green(`v${latestVersion}`)}).`
137
- );
138
148
  } else if (cmp > 0) {
139
149
  log.info(
140
150
  `Plugin is ahead of latest release (local: ${chalk.cyan(`v${currentVersion}`)}, latest: v${latestVersion}). Skipping.`
@@ -214,9 +224,6 @@ async function updateCommand(opts) {
214
224
  } else {
215
225
  const cmp = semverCompare(cliVersion, latestCliVersion);
216
226
  if (cmp === 0) {
217
- log.success(
218
- `CLI already on the latest version (${chalk.green(`v${latestCliVersion}`)}).`
219
- );
220
227
  } else if (cmp > 0) {
221
228
  log.info(
222
229
  `CLI is ahead of latest release (local: ${chalk.cyan(`v${cliVersion}`)}, latest: v${latestCliVersion}). Skipping.`
@@ -242,12 +249,9 @@ async function updateCommand(opts) {
242
249
  }
243
250
  }
244
251
  if (doStatusline) {
245
- log.step("Updating statusline...");
246
252
  const updatedVersion = ensureStatusline();
247
253
  if (updatedVersion) {
248
254
  log.success(`Statusline updated to ${chalk.green(`v${updatedVersion}`)}`);
249
- } else {
250
- log.dim("Statusline already up-to-date.");
251
255
  }
252
256
  }
253
257
  ensureShellCompletion({ silent: false });
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  log
3
- } from "./chunk-W5CD7WTX.js";
3
+ } from "./chunk-NREZK463.js";
4
4
 
5
5
  // src/lib/shell-completion.ts
6
6
  import {
@@ -9,7 +9,7 @@ import {
9
9
  askScope,
10
10
  formatScopeLabel,
11
11
  injectBackChoice
12
- } from "./chunk-BIX7AJU3.js";
12
+ } from "./chunk-O4CNZB5H.js";
13
13
  import {
14
14
  mergeJson,
15
15
  readJson,
@@ -21,7 +21,7 @@ import {
21
21
  } from "./chunk-DO6FV6BL.js";
22
22
  import {
23
23
  log
24
- } from "./chunk-W5CD7WTX.js";
24
+ } from "./chunk-NREZK463.js";
25
25
 
26
26
  // src/lib/memory-prompts.ts
27
27
  import { existsSync } from "fs";
@@ -0,0 +1,41 @@
1
+ // src/lib/log.ts
2
+ import chalk from "chalk";
3
+ var log = {
4
+ info: (msg) => console.log(chalk.blue("\u2139"), msg),
5
+ success: (msg) => console.log(chalk.green("\u2714"), msg),
6
+ congrats: (msg) => console.log(chalk.green("\u{1F389}"), msg),
7
+ warn: (msg) => console.log(chalk.yellow("\u26A0"), msg),
8
+ error: (msg) => console.log(chalk.red("\u2716"), msg),
9
+ step: (msg) => console.log(chalk.cyan("\u2192"), msg),
10
+ dim: (msg) => console.log(chalk.dim(msg))
11
+ };
12
+ function visualWidth(str) {
13
+ const emojiRe = /\p{Emoji_Presentation}|\p{Emoji}\uFE0F|\p{Emoji_Modifier_Base}\p{Emoji_Modifier}?|\p{Regional_Indicator}{2}|[#*0-9]\uFE0F?\u20E3/gu;
14
+ let width = 0;
15
+ let lastIndex = 0;
16
+ for (const m of str.matchAll(emojiRe)) {
17
+ width += m.index - lastIndex;
18
+ width += 2;
19
+ lastIndex = m.index + m[0].length;
20
+ }
21
+ width += str.length - lastIndex;
22
+ return width;
23
+ }
24
+ function printBanner(title, opts = {}) {
25
+ const colorFn = opts.color ?? chalk.green;
26
+ const padding = 3;
27
+ const pad = " ".repeat(padding);
28
+ const inner = `${pad}${title}${pad}`;
29
+ const vw = visualWidth(inner);
30
+ const top = `\u250C${"\u2500".repeat(vw)}\u2510`;
31
+ const mid = `\u2502${inner}\u2502`;
32
+ const bot = `\u2514${"\u2500".repeat(vw)}\u2518`;
33
+ console.log(colorFn(top));
34
+ console.log(colorFn(mid));
35
+ console.log(colorFn(bot));
36
+ }
37
+
38
+ export {
39
+ log,
40
+ printBanner
41
+ };
@@ -10,7 +10,7 @@ import {
10
10
  } from "./chunk-DO6FV6BL.js";
11
11
  import {
12
12
  log
13
- } from "./chunk-W5CD7WTX.js";
13
+ } from "./chunk-NREZK463.js";
14
14
 
15
15
  // src/lib/prompt-utils.ts
16
16
  import { select, Separator } from "@inquirer/prompts";
@@ -7,7 +7,7 @@ import {
7
7
  getMemoryMcpStatus,
8
8
  memoryConfigMenu,
9
9
  writeMemoryMcpEntry
10
- } from "./chunk-LCABODSZ.js";
10
+ } from "./chunk-LTHJTWJ6.js";
11
11
  import {
12
12
  getLibPath
13
13
  } from "./chunk-RZRT7NGT.js";
@@ -17,7 +17,7 @@ import {
17
17
  } from "./chunk-SIQJPT47.js";
18
18
  import {
19
19
  showConfigHint
20
- } from "./chunk-BIX7AJU3.js";
20
+ } from "./chunk-O4CNZB5H.js";
21
21
  import {
22
22
  run,
23
23
  runWithStderr
@@ -30,8 +30,9 @@ import {
30
30
  localConfigPath
31
31
  } from "./chunk-DO6FV6BL.js";
32
32
  import {
33
- log
34
- } from "./chunk-W5CD7WTX.js";
33
+ log,
34
+ printBanner
35
+ } from "./chunk-NREZK463.js";
35
36
 
36
37
  // src/commands/memory.ts
37
38
  import { existsSync, readdirSync, statSync, rmSync } from "fs";
@@ -86,10 +87,12 @@ function ensureMemoryBuilt(mcpDir) {
86
87
  }
87
88
  }
88
89
  function printMemoryMcpConfig(serverPath, memoryDir) {
89
- console.log(`Add this to your MCP client config:
90
-
91
- --- Claude Code (.mcp.json in project root) ---
92
-
90
+ console.log(chalk.dim("Add this to your MCP client config:"));
91
+ console.log();
92
+ console.log(
93
+ chalk.yellow.bold("--- Claude Code (.mcp.json in project root) ---")
94
+ );
95
+ console.log(`
93
96
  {
94
97
  "mcpServers": {
95
98
  "coding-friend-memory": {
@@ -97,10 +100,14 @@ function printMemoryMcpConfig(serverPath, memoryDir) {
97
100
  "args": ["${serverPath}", "${memoryDir}"]
98
101
  }
99
102
  }
100
- }
101
-
102
- --- Claude Desktop / Claude Chat (claude_desktop_config.json) ---
103
-
103
+ }`);
104
+ console.log();
105
+ console.log(
106
+ chalk.yellow.bold(
107
+ "--- Claude Desktop / Claude Chat (claude_desktop_config.json) ---"
108
+ )
109
+ );
110
+ console.log(`
104
111
  {
105
112
  "mcpServers": {
106
113
  "coding-friend-memory": {
@@ -108,27 +115,43 @@ function printMemoryMcpConfig(serverPath, memoryDir) {
108
115
  "args": ["${serverPath}", "${memoryDir}"]
109
116
  }
110
117
  }
111
- }
112
-
113
- --- Generic MCP client ---
114
-
118
+ }`);
119
+ console.log();
120
+ console.log(chalk.yellow.bold("--- Generic MCP client ---"));
121
+ console.log(`
115
122
  Server command: node ${serverPath} ${memoryDir}
116
- Transport: stdio
117
-
118
- --- Available tools ---
119
-
120
- memory_store Store a new memory
121
- memory_search Search memories (keyword match)
122
- memory_retrieve Get a specific memory by ID
123
- memory_list List memories with filtering
124
- memory_update Update existing memory
125
- memory_delete Delete a memory
126
-
127
- --- Resources ---
128
-
129
- memory://index Browse all memories
130
- memory://stats Storage statistics
131
- `);
123
+ Transport: stdio`);
124
+ console.log();
125
+ console.log(chalk.yellow.bold("--- Available tools ---"));
126
+ console.log();
127
+ console.log(
128
+ ` ${chalk.white("memory_store")} ${chalk.dim("Store a new memory")}`
129
+ );
130
+ console.log(
131
+ ` ${chalk.white("memory_search")} ${chalk.dim("Search memories (keyword match)")}`
132
+ );
133
+ console.log(
134
+ ` ${chalk.white("memory_retrieve")} ${chalk.dim("Get a specific memory by ID")}`
135
+ );
136
+ console.log(
137
+ ` ${chalk.white("memory_list")} ${chalk.dim("List memories with filtering")}`
138
+ );
139
+ console.log(
140
+ ` ${chalk.white("memory_update")} ${chalk.dim("Update existing memory")}`
141
+ );
142
+ console.log(
143
+ ` ${chalk.white("memory_delete")} ${chalk.dim("Delete a memory")}`
144
+ );
145
+ console.log();
146
+ console.log(chalk.yellow.bold("--- Resources ---"));
147
+ console.log();
148
+ console.log(
149
+ ` ${chalk.white("memory://index")} ${chalk.dim("Browse all memories")}`
150
+ );
151
+ console.log(
152
+ ` ${chalk.white("memory://stats")} ${chalk.dim("Storage statistics")}`
153
+ );
154
+ console.log();
132
155
  log.warn(
133
156
  "Note: The memory path is project-specific. Use local .mcp.json (per project), not global ~/.claude/.mcp.json."
134
157
  );
@@ -157,7 +180,7 @@ async function memoryStatusCommand() {
157
180
  } else {
158
181
  tierLabel = chalk.cyan("Tier 3 (Markdown)");
159
182
  }
160
- console.log("=== \u{1F9E0} Coding Friend Memory ===");
183
+ printBanner("\u{1F9E0} Coding Friend Memory");
161
184
  console.log();
162
185
  log.info(`Tier: ${tierLabel}`);
163
186
  log.info(`Memory dir: ${chalk.cyan(memoryDir)}`);
@@ -410,7 +433,6 @@ function getDbPath(memoryDir) {
410
433
  return null;
411
434
  }
412
435
  }
413
- var em = chalk.hex("#10b981");
414
436
  async function ensureSqliteDepsIfNeeded(mcpDir) {
415
437
  const config = loadConfig();
416
438
  const tier = config.memory?.tier ?? "auto";
@@ -479,12 +501,7 @@ async function memoryInitCommand() {
479
501
  return;
480
502
  }
481
503
  console.log();
482
- console.log(em(" \u256D\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u256E"));
483
- console.log(
484
- em(" \u2502 ") + "\u{1F9E0}" + em(" ") + chalk.bold.white("Memory Setup") + em(" \u2502")
485
- );
486
- console.log(em(" \u2570\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u256E\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u256F"));
487
- console.log(em(" \u2570\u2500\u25B8"));
504
+ printBanner("\u{1F9E0} Memory Setup");
488
505
  console.log();
489
506
  showConfigHint();
490
507
  log.step("Step 1/5: Search tier");
@@ -576,12 +593,7 @@ async function memoryInitCommand() {
576
593
  }
577
594
  async function memoryConfigCommand() {
578
595
  console.log();
579
- console.log(em(" \u256D\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u256E"));
580
- console.log(
581
- em(" \u2502 ") + "\u{1F9E0}" + em(" ") + chalk.bold.white("Memory Config") + em(" \u2502")
582
- );
583
- console.log(em(" \u2570\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u256E\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u256F"));
584
- console.log(em(" \u2570\u2500\u25B8"));
596
+ printBanner("\u{1F9E0} Memory Config");
585
597
  console.log();
586
598
  showConfigHint();
587
599
  await memoryConfigMenu({ exitLabel: "Done" });
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  memoryConfigMenu
3
- } from "./chunk-LCABODSZ.js";
3
+ } from "./chunk-LTHJTWJ6.js";
4
4
  import {
5
5
  getAllRules,
6
6
  getExistingRules
@@ -22,7 +22,7 @@ import {
22
22
  ensureShellCompletion,
23
23
  hasShellCompletion,
24
24
  removeShellCompletion
25
- } from "./chunk-PYTEAC6K.js";
25
+ } from "./chunk-DMJCO6LJ.js";
26
26
  import {
27
27
  BACK,
28
28
  applyDocsDirChange,
@@ -32,7 +32,7 @@ import {
32
32
  getScopeLabel,
33
33
  injectBackChoice,
34
34
  showConfigHint
35
- } from "./chunk-BIX7AJU3.js";
35
+ } from "./chunk-O4CNZB5H.js";
36
36
  import {
37
37
  run
38
38
  } from "./chunk-EVGXUDX4.js";
@@ -48,8 +48,9 @@ import {
48
48
  resolvePath
49
49
  } from "./chunk-DO6FV6BL.js";
50
50
  import {
51
- log
52
- } from "./chunk-W5CD7WTX.js";
51
+ log,
52
+ printBanner
53
+ } from "./chunk-NREZK463.js";
53
54
 
54
55
  // src/commands/config.ts
55
56
  import { checkbox, confirm, input, select } from "@inquirer/prompts";
@@ -543,7 +544,7 @@ async function editPermissions() {
543
544
  )
544
545
  });
545
546
  if (choice === BACK) return;
546
- const { permissionCommand } = await import("./permission-BEC6YZQA.js");
547
+ const { permissionCommand } = await import("./permission-QIJLEMJ6.js");
547
548
  switch (choice) {
548
549
  case "interactive":
549
550
  await permissionCommand({});
@@ -556,16 +557,9 @@ async function editPermissions() {
556
557
  break;
557
558
  }
558
559
  }
559
- var em = chalk.hex("#10b981");
560
560
  async function configCommand() {
561
561
  console.log();
562
- console.log(em(" \u256D\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u256E"));
563
- console.log(
564
- em(" \u2502 ") + "\u2726" + em(" ") + chalk.bold.white("Coding Friend") + em(" \u2726 \u2502")
565
- );
566
- console.log(em(" \u2502 ") + chalk.dim("Config") + em(" \u2502"));
567
- console.log(em(" \u2570\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u256E\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u256F"));
568
- console.log(em(" \u2570\u2500\u25B8"));
562
+ printBanner("\u2728 Coding Friend Config \u2728");
569
563
  console.log();
570
564
  showConfigHint();
571
565
  while (true) {
@@ -8,7 +8,7 @@ import {
8
8
  import "./chunk-PRIH34UB.js";
9
9
  import {
10
10
  ensureShellCompletion
11
- } from "./chunk-PYTEAC6K.js";
11
+ } from "./chunk-DMJCO6LJ.js";
12
12
  import {
13
13
  commandExists,
14
14
  run
@@ -23,8 +23,9 @@ import {
23
23
  pluginCachePath
24
24
  } from "./chunk-DO6FV6BL.js";
25
25
  import {
26
- log
27
- } from "./chunk-W5CD7WTX.js";
26
+ log,
27
+ printBanner
28
+ } from "./chunk-NREZK463.js";
28
29
 
29
30
  // src/commands/dev.ts
30
31
  import {
@@ -88,12 +89,12 @@ async function devOnCommand(path) {
88
89
  return;
89
90
  }
90
91
  const version = getLocalPluginVersion(localPath);
91
- const versionLabel = version ? ` ${chalk.dim(`(v${version})`)}` : "";
92
- console.log(
93
- `
94
- === ${chalk.green("Switching to local dev mode")}${versionLabel} ===
95
- `
96
- );
92
+ const versionLabel = version ? ` (v${version})` : "";
93
+ console.log();
94
+ printBanner(`Switching to local dev mode${versionLabel}`, {
95
+ color: chalk.green
96
+ });
97
+ console.log();
97
98
  log.info(`Local path: ${chalk.cyan(localPath)}`);
98
99
  if (isPluginInstalled()) {
99
100
  if (!runClaude(
@@ -145,11 +146,11 @@ async function devOffCommand() {
145
146
  return;
146
147
  }
147
148
  if (!ensureClaude()) return;
148
- console.log(
149
- `
150
- === ${chalk.yellow("Switching back to remote marketplace")} ===
151
- `
152
- );
149
+ console.log();
150
+ printBanner("Switching back to remote marketplace", {
151
+ color: chalk.yellow
152
+ });
153
+ console.log();
153
154
  if (isPluginInstalled()) {
154
155
  if (!runClaude(
155
156
  ["plugin", "uninstall", PLUGIN_ID],
@@ -265,10 +266,10 @@ async function devReinstall(path, label) {
265
266
  if (!ensureClaude()) return;
266
267
  const localPath = path ?? state?.localPath;
267
268
  const version = localPath ? getLocalPluginVersion(localPath) : null;
268
- const versionLabel = version ? ` ${chalk.dim(`(v${version})`)}` : "";
269
- console.log(`
270
- === ${chalk.cyan(label)}${versionLabel} ===
271
- `);
269
+ const bannerTitle = version ? `${label} (v${version})` : label;
270
+ console.log();
271
+ printBanner(bannerTitle, { color: chalk.cyan });
272
+ console.log();
272
273
  if (state) {
273
274
  await devOffCommand();
274
275
  console.log();
@@ -4,13 +4,13 @@ import {
4
4
  } from "./chunk-5UEY7HQX.js";
5
5
  import {
6
6
  resolveScope
7
- } from "./chunk-BIX7AJU3.js";
7
+ } from "./chunk-O4CNZB5H.js";
8
8
  import "./chunk-EVGXUDX4.js";
9
9
  import "./chunk-5UVDWG5L.js";
10
10
  import "./chunk-DO6FV6BL.js";
11
11
  import {
12
12
  log
13
- } from "./chunk-W5CD7WTX.js";
13
+ } from "./chunk-NREZK463.js";
14
14
 
15
15
  // src/commands/disable.ts
16
16
  import chalk from "chalk";
@@ -4,13 +4,13 @@ import {
4
4
  } from "./chunk-5UEY7HQX.js";
5
5
  import {
6
6
  resolveScope
7
- } from "./chunk-BIX7AJU3.js";
7
+ } from "./chunk-O4CNZB5H.js";
8
8
  import "./chunk-EVGXUDX4.js";
9
9
  import "./chunk-5UVDWG5L.js";
10
10
  import "./chunk-DO6FV6BL.js";
11
11
  import {
12
12
  log
13
- } from "./chunk-W5CD7WTX.js";
13
+ } from "./chunk-NREZK463.js";
14
14
 
15
15
  // src/commands/enable.ts
16
16
  import chalk from "chalk";
@@ -12,8 +12,9 @@ import {
12
12
  import "./chunk-5UVDWG5L.js";
13
13
  import "./chunk-DO6FV6BL.js";
14
14
  import {
15
- log
16
- } from "./chunk-W5CD7WTX.js";
15
+ log,
16
+ printBanner
17
+ } from "./chunk-NREZK463.js";
17
18
 
18
19
  // src/commands/host.ts
19
20
  import { existsSync, readdirSync } from "fs";
@@ -50,7 +51,7 @@ async function hostCommand(path, opts) {
50
51
  process.exit(1);
51
52
  }
52
53
  const catCount = countCategories(docsDir);
53
- console.log("=== \u{1F33F} Coding Friend Host \u{1F33F} ===");
54
+ printBanner("\u2728 Coding Friend Host \u2728");
54
55
  log.info(`Docs folder: ${docsDir}`);
55
56
  log.info(`Found: ${docCount} docs in ${catCount} categories`);
56
57
  console.log();