coding-friend-cli 1.21.1 → 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 (40) 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-3KXYEC2T.js → chunk-LTHJTWJ6.js} +82 -3
  4. package/dist/chunk-NREZK463.js +41 -0
  5. package/dist/{chunk-BIX7AJU3.js → chunk-O4CNZB5H.js} +1 -1
  6. package/dist/{memory-27ZSRPM7.js → chunk-WCSNAEOF.js} +155 -73
  7. package/dist/{config-5I3XJEEL.js → config-5DKOGWWJ.js} +9 -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-V5O3PDCF.js → host-RR6FT7AX.js} +7 -6
  12. package/dist/index.js +31 -31
  13. package/dist/{init-3XMC6YJI.js → init-6B2OHKFX.js} +22 -40
  14. package/dist/{install-5ZBHH2OM.js → install-BURZ5QEM.js} +7 -6
  15. package/dist/mcp-CB4PUUES.js +156 -0
  16. package/dist/memory-TJZNGTOA.js +37 -0
  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-F6WASK7N.js → status-ZZRJ3VGF.js} +7 -7
  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 +4 -1
  26. package/lib/cf-memory/package.json +0 -1
  27. package/lib/cf-memory/src/__tests__/daemon-client.test.ts +146 -0
  28. package/lib/cf-memory/src/daemon/process.ts +7 -0
  29. package/lib/cf-memory/src/index.ts +21 -2
  30. package/lib/cf-memory/src/lib/daemon-client.ts +54 -3
  31. package/lib/cf-memory/src/lib/tier.ts +27 -3
  32. package/lib/learn-host/package.json +0 -1
  33. package/lib/learn-mcp/package.json +0 -1
  34. package/lib/learn-mcp/src/index.ts +8 -1
  35. package/package.json +1 -1
  36. package/dist/chunk-W5CD7WTX.js +0 -15
  37. package/dist/mcp-U34LFD34.js +0 -106
  38. package/lib/cf-memory/CHANGELOG.md +0 -42
  39. package/lib/learn-host/CHANGELOG.md +0 -35
  40. 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 {
@@ -1,15 +1,19 @@
1
1
  import {
2
2
  getLibPath
3
3
  } from "./chunk-RZRT7NGT.js";
4
+ import {
5
+ resolveMemoryDir
6
+ } from "./chunk-SIQJPT47.js";
4
7
  import {
5
8
  BACK,
6
9
  askScope,
7
10
  formatScopeLabel,
8
11
  injectBackChoice
9
- } from "./chunk-BIX7AJU3.js";
12
+ } from "./chunk-O4CNZB5H.js";
10
13
  import {
11
14
  mergeJson,
12
- readJson
15
+ readJson,
16
+ writeJson
13
17
  } from "./chunk-5UVDWG5L.js";
14
18
  import {
15
19
  globalConfigPath,
@@ -17,10 +21,13 @@ import {
17
21
  } from "./chunk-DO6FV6BL.js";
18
22
  import {
19
23
  log
20
- } from "./chunk-W5CD7WTX.js";
24
+ } from "./chunk-NREZK463.js";
21
25
 
22
26
  // src/lib/memory-prompts.ts
27
+ import { existsSync } from "fs";
28
+ import { homedir } from "os";
23
29
  import { confirm, input, select } from "@inquirer/prompts";
30
+ import chalk from "chalk";
24
31
  import { join } from "path";
25
32
  function getMemoryFieldScope(field, globalCfg, localCfg) {
26
33
  const globalSection = globalCfg?.memory;
@@ -235,6 +242,67 @@ async function editMemoryDaemonTimeout(globalCfg, localCfg) {
235
242
  idleTimeout: Number(value) * 6e4
236
243
  });
237
244
  }
245
+ function writeMemoryMcpEntry(serverPath, memoryDir) {
246
+ const mcpPath = join(process.cwd(), ".mcp.json");
247
+ const existing = readJson(mcpPath) ?? {};
248
+ const servers = existing.mcpServers ?? {};
249
+ writeJson(mcpPath, {
250
+ ...existing,
251
+ mcpServers: {
252
+ ...servers,
253
+ "coding-friend-memory": {
254
+ command: "node",
255
+ args: [serverPath, memoryDir]
256
+ }
257
+ }
258
+ });
259
+ log.success("Added coding-friend-memory to .mcp.json");
260
+ }
261
+ function getMemoryMcpStatus() {
262
+ const localMcpPath = join(process.cwd(), ".mcp.json");
263
+ const localMcp = readJson(localMcpPath);
264
+ const localServers = localMcp?.mcpServers;
265
+ if (localServers != null && "coding-friend-memory" in localServers) {
266
+ return { configured: true, scope: "local" };
267
+ }
268
+ const globalMcpPath = join(homedir(), ".claude", ".mcp.json");
269
+ const globalMcp = readJson(globalMcpPath);
270
+ const globalServers = globalMcp?.mcpServers;
271
+ if (globalServers != null && "coding-friend-memory" in globalServers) {
272
+ return { configured: true, scope: "global" };
273
+ }
274
+ return { configured: false, scope: null };
275
+ }
276
+ async function editMemoryMcp() {
277
+ const status = getMemoryMcpStatus();
278
+ if (status.configured) {
279
+ const label = status.scope === "local" ? chalk.green("configured") + chalk.dim(" (local .mcp.json)") : chalk.green("configured") + chalk.dim(" (global ~/.claude/.mcp.json)") + " " + chalk.yellow("\u26A0 only works for one project");
280
+ log.info(`MCP: ${label}`);
281
+ const reconfigure = await confirm({
282
+ message: "Reconfigure Memory MCP in local .mcp.json?",
283
+ default: false
284
+ });
285
+ if (!reconfigure) return;
286
+ }
287
+ let mcpDir;
288
+ try {
289
+ mcpDir = getLibPath("cf-memory");
290
+ } catch {
291
+ log.warn(
292
+ "cf-memory package not found. Install the CLI first: npm i -g coding-friend-cli"
293
+ );
294
+ return;
295
+ }
296
+ const serverPath = join(mcpDir, "dist", "index.js");
297
+ if (!existsSync(serverPath)) {
298
+ log.warn(
299
+ 'cf-memory not built yet. Run "cf memory mcp" after building to get the config.'
300
+ );
301
+ return;
302
+ }
303
+ const memoryDir = resolveMemoryDir();
304
+ writeMemoryMcpEntry(serverPath, memoryDir);
305
+ }
238
306
  async function memoryConfigMenu(opts) {
239
307
  while (true) {
240
308
  const globalCfg = readJson(globalConfigPath());
@@ -274,6 +342,8 @@ async function memoryConfigMenu(opts) {
274
342
  const daemonScope = getMemoryFieldScope("daemon", globalCfg, localCfg);
275
343
  const daemonVal = getMergedMemoryValue("daemon", globalCfg, localCfg);
276
344
  const embeddingLabel = embeddingVal?.provider ? embeddingVal.model ? `${embeddingVal.model} (${embeddingVal.provider})` : embeddingVal.provider : "";
345
+ const mcpStatus = getMemoryMcpStatus();
346
+ const mcpLabel = mcpStatus.configured ? mcpStatus.scope === "local" ? chalk.green("configured") + chalk.dim(" (.mcp.json)") : chalk.green("configured") + chalk.dim(" (global)") + " " + chalk.yellow("\u26A0") : chalk.dim("not configured");
277
347
  const choice = await select({
278
348
  message: "Memory settings:",
279
349
  choices: injectBackChoice(
@@ -297,6 +367,10 @@ async function memoryConfigMenu(opts) {
297
367
  {
298
368
  name: `Daemon timeout ${formatScopeLabel(daemonScope)}${daemonVal?.idleTimeout ? ` (${daemonVal.idleTimeout / 6e4}min)` : ""}`,
299
369
  value: "daemon"
370
+ },
371
+ {
372
+ name: `MCP setup (${mcpLabel})`,
373
+ value: "mcp"
300
374
  }
301
375
  ],
302
376
  opts?.exitLabel ?? "Back"
@@ -319,6 +393,9 @@ async function memoryConfigMenu(opts) {
319
393
  case "daemon":
320
394
  await editMemoryDaemonTimeout(globalCfg, localCfg);
321
395
  break;
396
+ case "mcp":
397
+ await editMemoryMcp();
398
+ break;
322
399
  }
323
400
  }
324
401
  }
@@ -329,5 +406,7 @@ export {
329
406
  editMemoryAutoStart,
330
407
  editMemoryEmbedding,
331
408
  editMemoryDaemonTimeout,
409
+ writeMemoryMcpEntry,
410
+ getMemoryMcpStatus,
332
411
  memoryConfigMenu
333
412
  };
@@ -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";