coding-friend-cli 1.18.0 → 1.19.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,15 +1,30 @@
1
+ import {
2
+ editMemoryAutoCapture,
3
+ editMemoryAutoStart,
4
+ editMemoryDaemonTimeout,
5
+ editMemoryEmbedding,
6
+ editMemoryTier,
7
+ memoryConfigMenu
8
+ } from "./chunk-WEMDLEK5.js";
1
9
  import {
2
10
  loadConfig,
3
11
  resolveMemoryDir
4
- } from "./chunk-KTX4MGMR.js";
12
+ } from "./chunk-QMD7P67N.js";
5
13
  import {
6
14
  getLibPath
7
15
  } from "./chunk-RZRT7NGT.js";
8
16
  import "./chunk-POC2WHU2.js";
17
+ import {
18
+ showConfigHint
19
+ } from "./chunk-C5LYVVEI.js";
9
20
  import {
10
21
  run
11
22
  } from "./chunk-CYQU33FY.js";
12
- import "./chunk-RWUTFVRB.js";
23
+ import {
24
+ globalConfigPath,
25
+ localConfigPath,
26
+ readJson
27
+ } from "./chunk-RWUTFVRB.js";
13
28
  import {
14
29
  log
15
30
  } from "./chunk-W5CD7WTX.js";
@@ -82,7 +97,7 @@ async function memoryStatusCommand() {
82
97
  console.log();
83
98
  log.info(`Tier: ${tierLabel}`);
84
99
  log.info(`Memory dir: ${chalk.cyan(memoryDir)}`);
85
- log.info(`Memories: ${chalk.green(String(docCount))}`);
100
+ log.info(`Memories in this dir: ${chalk.green(String(docCount))}`);
86
101
  if (running && daemonInfo) {
87
102
  const uptime = (Date.now() - daemonInfo.startedAt) / 1e3;
88
103
  log.info(
@@ -104,6 +119,17 @@ async function memoryStatusCommand() {
104
119
  `SQLite deps: ${chalk.dim("not installed")} (run "cf memory init" to enable Tier 1)`
105
120
  );
106
121
  }
122
+ const config = loadConfig();
123
+ const embeddingConfig = config.memory?.embedding;
124
+ if (embeddingConfig?.provider || embeddingConfig?.model) {
125
+ const provider = embeddingConfig.provider ?? "transformers";
126
+ const model = embeddingConfig.model ?? (provider === "ollama" ? "all-minilm:l6-v2" : "Xenova/all-MiniLM-L6-v2");
127
+ log.info(`Embedding: ${chalk.cyan(model)} ${chalk.dim(`(${provider})`)}`);
128
+ }
129
+ const autoCapture = config.memory?.autoCapture ?? false;
130
+ log.info(
131
+ `Auto-capture: ${autoCapture ? chalk.green("on") : chalk.dim("off")}`
132
+ );
107
133
  if (existsSync(memoryDir)) {
108
134
  const categories = readdirSync(memoryDir, { withFileTypes: true }).filter((d) => d.isDirectory() && !d.name.startsWith(".")).map((d) => {
109
135
  const catCount = countMdFiles(join(memoryDir, d.name));
@@ -286,11 +312,95 @@ async function memoryRebuildCommand() {
286
312
  log.error("Rebuild failed or not supported.");
287
313
  }
288
314
  }
315
+ function getDbPath(memoryDir) {
316
+ try {
317
+ const resolved = resolve(memoryDir).replace(/\/+$/, "");
318
+ const stripped = resolved.replace(/\/docs\/memory$/, "").replace(/\/memory$/, "");
319
+ const id = stripped.replace(/\//g, "-");
320
+ const home = homedir();
321
+ const dbPath = join(
322
+ home,
323
+ ".coding-friend",
324
+ "memory",
325
+ "projects",
326
+ id,
327
+ "db.sqlite"
328
+ );
329
+ return existsSync(dbPath) ? dbPath : null;
330
+ } catch {
331
+ return null;
332
+ }
333
+ }
334
+ var em = chalk.hex("#10b981");
289
335
  async function memoryInitCommand() {
290
336
  const memoryDir = getMemoryDir();
291
337
  const mcpDir = getLibPath("cf-memory");
292
338
  ensureBuilt(mcpDir);
293
- log.step("Initializing Tier 1 (SQLite + Hybrid Search)...");
339
+ const dbExists = getDbPath(memoryDir) !== null;
340
+ if (dbExists) {
341
+ console.log();
342
+ log.info(
343
+ "Memory already initialized. Opening config menu to adjust settings."
344
+ );
345
+ log.dim('To re-import memories, run "cf memory rebuild".');
346
+ console.log();
347
+ await memoryConfigMenu({ exitLabel: "Done" });
348
+ return;
349
+ }
350
+ console.log();
351
+ 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"));
352
+ console.log(
353
+ em(" \u2502 ") + "\u{1F9E0}" + em(" ") + chalk.bold.white("Memory Setup") + em(" \u2502")
354
+ );
355
+ 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"));
356
+ console.log(em(" \u2570\u2500\u25B8"));
357
+ console.log();
358
+ showConfigHint();
359
+ log.step("Step 1/5: Search tier");
360
+ await editMemoryTier(
361
+ readJson(globalConfigPath()),
362
+ readJson(localConfigPath())
363
+ );
364
+ console.log();
365
+ log.step("Step 2/5: Embedding provider");
366
+ await editMemoryEmbedding(
367
+ readJson(globalConfigPath()),
368
+ readJson(localConfigPath())
369
+ );
370
+ console.log();
371
+ log.step("Step 3/5: Auto-capture");
372
+ await editMemoryAutoCapture(
373
+ readJson(globalConfigPath()),
374
+ readJson(localConfigPath())
375
+ );
376
+ console.log();
377
+ log.step("Step 4/5: Auto-start daemon");
378
+ await editMemoryAutoStart(
379
+ readJson(globalConfigPath()),
380
+ readJson(localConfigPath())
381
+ );
382
+ console.log();
383
+ log.step("Step 5/5: Daemon idle timeout");
384
+ await editMemoryDaemonTimeout(
385
+ readJson(globalConfigPath()),
386
+ readJson(localConfigPath())
387
+ );
388
+ console.log();
389
+ const config = loadConfig();
390
+ const tier = config.memory?.tier ?? "auto";
391
+ if (tier === "markdown") {
392
+ log.success(
393
+ 'Memory initialized with Tier 3 (markdown). Run "cf memory status" to verify.'
394
+ );
395
+ return;
396
+ }
397
+ if (tier === "lite") {
398
+ log.success(
399
+ 'Memory initialized. Run "cf memory start-daemon" to enable Tier 2 search.'
400
+ );
401
+ return;
402
+ }
403
+ log.step("Installing SQLite dependencies...");
294
404
  const { ensureDeps, areSqliteDepsAvailable } = await import(join(mcpDir, "dist/lib/lazy-install.js"));
295
405
  if (areSqliteDepsAvailable()) {
296
406
  log.info("SQLite dependencies already installed.");
@@ -299,32 +409,32 @@ async function memoryInitCommand() {
299
409
  onProgress: (msg) => log.step(msg)
300
410
  });
301
411
  if (!installed) {
302
- log.error("Failed to install dependencies.");
412
+ log.error("Failed to install SQLite dependencies.");
303
413
  log.dim(
304
414
  "Ensure you have a C++ compiler installed (Xcode CLT on macOS, build-essential on Linux)."
305
415
  );
306
- process.exit(1);
416
+ log.dim(
417
+ 'Memory will fall back to a lower tier. You can retry later with "cf memory init".'
418
+ );
419
+ return;
307
420
  }
308
421
  log.success("Dependencies installed.");
309
422
  }
310
423
  if (!existsSync(memoryDir)) {
311
- log.info("No memory directory found. Nothing to import.");
312
- log.success(
313
- "Tier 1 is ready. Memories will be indexed as they're created."
424
+ log.info(
425
+ "No memory directory found. Memories will be indexed as they're created."
314
426
  );
427
+ log.success('Memory initialized. Run "cf memory status" to verify.');
315
428
  return;
316
429
  }
317
430
  const docCount = countMdFiles(memoryDir);
318
431
  if (docCount === 0) {
319
432
  log.info("No existing memories to import.");
320
- log.success(
321
- "Tier 1 is ready. Memories will be indexed as they're created."
322
- );
433
+ log.success('Memory initialized. Run "cf memory status" to verify.');
323
434
  return;
324
435
  }
325
436
  log.step(`Importing ${docCount} existing memories into SQLite...`);
326
437
  const { SqliteBackend } = await import(join(mcpDir, "dist/backends/sqlite/index.js"));
327
- const config = loadConfig();
328
438
  const embedding = config.memory?.embedding;
329
439
  const backend = new SqliteBackend(memoryDir, {
330
440
  skipVec: false,
@@ -334,21 +444,30 @@ async function memoryInitCommand() {
334
444
  await backend.rebuild();
335
445
  const stats = await backend.stats();
336
446
  log.success(`Imported ${stats.total} memories. DB: ${backend.getDbPath()}`);
337
- if (backend.isVecEnabled()) {
338
- log.info(`Vector search: ${chalk.green("enabled")}`);
339
- } else {
340
- log.info(
341
- `Vector search: ${chalk.dim("disabled")} (sqlite-vec not available)`
342
- );
343
- }
447
+ log.info(
448
+ `Vector search: ${backend.isVecEnabled() ? chalk.green("enabled") : chalk.dim("disabled (sqlite-vec not available)")}`
449
+ );
344
450
  } finally {
345
451
  await backend.close();
346
452
  }
347
- log.success('Tier 1 initialized. Run "cf memory status" to verify.');
453
+ console.log();
454
+ log.success('Memory initialized! Run "cf memory status" to verify.');
348
455
  log.info(
349
456
  `Tip: Run ${chalk.cyan("/cf-scan")} in Claude Code to populate memory with project knowledge.`
350
457
  );
351
458
  }
459
+ async function memoryConfigCommand() {
460
+ console.log();
461
+ 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"));
462
+ console.log(
463
+ em(" \u2502 ") + "\u{1F9E0}" + em(" ") + chalk.bold.white("Memory Config") + em(" \u2502")
464
+ );
465
+ 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"));
466
+ console.log(em(" \u2570\u2500\u25B8"));
467
+ console.log();
468
+ showConfigHint();
469
+ await memoryConfigMenu({ exitLabel: "Done" });
470
+ }
352
471
  function getProjectsBaseDir() {
353
472
  const home = homedir();
354
473
  return join(home, ".coding-friend", "memory", "projects");
@@ -637,6 +756,7 @@ Transport: stdio
637
756
  `);
638
757
  }
639
758
  export {
759
+ memoryConfigCommand,
640
760
  memoryInitCommand,
641
761
  memoryListCommand,
642
762
  memoryMcpCommand,
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  ensureShellCompletion
4
- } from "./chunk-VUAUAO2R.js";
4
+ } from "./chunk-NEQZP5D4.js";
5
5
  import "./chunk-W5CD7WTX.js";
6
6
 
7
7
  // src/postinstall.ts
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  loadConfig
3
- } from "./chunk-KTX4MGMR.js";
3
+ } from "./chunk-QMD7P67N.js";
4
4
  import "./chunk-POC2WHU2.js";
5
5
  import {
6
6
  claudeSessionDir,
@@ -0,0 +1,223 @@
1
+ import {
2
+ resolveMemoryDir
3
+ } from "./chunk-QMD7P67N.js";
4
+ import {
5
+ getCliVersion,
6
+ getLatestCliVersion,
7
+ getLatestVersion,
8
+ semverCompare
9
+ } from "./chunk-IRPW2BMP.js";
10
+ import {
11
+ detectPluginScope,
12
+ isPluginDisabled
13
+ } from "./chunk-JFGLNTZI.js";
14
+ import {
15
+ getExistingRules
16
+ } from "./chunk-7CAIGH2Y.js";
17
+ import {
18
+ getLibPath
19
+ } from "./chunk-RZRT7NGT.js";
20
+ import {
21
+ getInstalledVersion
22
+ } from "./chunk-ORACWEDN.js";
23
+ import "./chunk-POC2WHU2.js";
24
+ import "./chunk-NEQZP5D4.js";
25
+ import "./chunk-C5LYVVEI.js";
26
+ import "./chunk-CYQU33FY.js";
27
+ import {
28
+ claudeSettingsPath,
29
+ devStatePath,
30
+ globalConfigPath,
31
+ localConfigPath,
32
+ readJson
33
+ } from "./chunk-RWUTFVRB.js";
34
+ import "./chunk-W5CD7WTX.js";
35
+
36
+ // src/commands/status.ts
37
+ import { existsSync, readdirSync } from "fs";
38
+ import { join } from "path";
39
+ import chalk from "chalk";
40
+ var VERSION_COL = 11;
41
+ var CONFIG_KEY_COL = 16;
42
+ var CONFIG_SUB_COL = 16;
43
+ function countMdFiles(dir) {
44
+ if (!existsSync(dir)) return 0;
45
+ let count = 0;
46
+ for (const entry of readdirSync(dir, { withFileTypes: true })) {
47
+ if (entry.isDirectory()) {
48
+ count += countMdFiles(join(dir, entry.name));
49
+ } else if (entry.name.endsWith(".md") && entry.name !== "README.md") {
50
+ count++;
51
+ }
52
+ }
53
+ return count;
54
+ }
55
+ function formatUptime(seconds) {
56
+ if (seconds < 60) return `${Math.round(seconds)}s`;
57
+ if (seconds < 3600) return `${Math.round(seconds / 60)}m`;
58
+ return `${Math.round(seconds / 3600)}h ${Math.round(seconds % 3600 / 60)}m`;
59
+ }
60
+ function formatScalar(value) {
61
+ if (typeof value === "string") return value;
62
+ if (typeof value === "boolean") return value ? "true" : "false";
63
+ if (typeof value === "number") return String(value);
64
+ return String(value);
65
+ }
66
+ function pad(label, width) {
67
+ return ` ${label}${" ".repeat(Math.max(1, width - label.length))}`;
68
+ }
69
+ function subLine(key, value, overrides) {
70
+ console.log(
71
+ ` ${key}${" ".repeat(Math.max(1, CONFIG_SUB_COL - key.length))}${value}${overrides}`
72
+ );
73
+ }
74
+ function printConfig(obj, otherConfig) {
75
+ for (const [key, value] of Object.entries(obj)) {
76
+ const overrides = otherConfig && key in otherConfig ? ` ${chalk.yellow("(overrides global)")}` : "";
77
+ if (typeof value === "object" && value !== null && !Array.isArray(value)) {
78
+ console.log(pad(key, CONFIG_KEY_COL) + chalk.dim("\u2500") + overrides);
79
+ const nested = value;
80
+ const nestedOther = otherConfig && typeof otherConfig[key] === "object" ? otherConfig[key] : null;
81
+ for (const [subKey, subVal] of Object.entries(nested)) {
82
+ const subOverrides = nestedOther && subKey in nestedOther ? ` ${chalk.yellow("(overrides global)")}` : "";
83
+ if (subKey === "categories" && Array.isArray(subVal)) {
84
+ const names = subVal.map(
85
+ (c) => typeof c === "object" && c !== null && "name" in c ? c.name : String(c)
86
+ ).join(", ");
87
+ subLine(subKey, names, subOverrides);
88
+ continue;
89
+ }
90
+ if (typeof subVal === "object" && subVal !== null && !Array.isArray(subVal)) {
91
+ const innerEntries = Object.entries(
92
+ subVal
93
+ );
94
+ const inline = innerEntries.map(([k, v]) => `${k}: ${formatScalar(v)}`).join(", ");
95
+ subLine(subKey, inline, subOverrides);
96
+ continue;
97
+ }
98
+ if (Array.isArray(subVal)) {
99
+ subLine(
100
+ subKey,
101
+ subVal.map((v) => formatScalar(v)).join(", "),
102
+ subOverrides
103
+ );
104
+ continue;
105
+ }
106
+ subLine(subKey, formatScalar(subVal), subOverrides);
107
+ }
108
+ continue;
109
+ }
110
+ if (Array.isArray(value)) {
111
+ console.log(
112
+ `${pad(key, CONFIG_KEY_COL)}${value.map((v) => formatScalar(v)).join(", ")}${overrides}`
113
+ );
114
+ continue;
115
+ }
116
+ console.log(
117
+ `${pad(key, CONFIG_KEY_COL)}${formatScalar(value)}${overrides}`
118
+ );
119
+ }
120
+ }
121
+ function versionLine(label, current, latest) {
122
+ const padded = pad(label, VERSION_COL);
123
+ if (!latest) {
124
+ return `${padded}${current} ${chalk.dim("(latest unavailable)")}`;
125
+ }
126
+ const cmp = semverCompare(current, latest);
127
+ const indicator = cmp >= 0 ? chalk.green("\u2714 up to date") : chalk.yellow("\u26A0 update available");
128
+ return `${padded}${current} \u2192 ${latest} ${indicator}`;
129
+ }
130
+ async function statusCommand() {
131
+ const pluginVersion = getInstalledVersion();
132
+ const latestPlugin = getLatestVersion();
133
+ const cliVersion = getCliVersion();
134
+ const latestCli = getLatestCliVersion();
135
+ console.log();
136
+ console.log(chalk.bold("\u{1F4E6} Versions"));
137
+ if (pluginVersion) {
138
+ console.log(versionLine("Plugin", pluginVersion, latestPlugin));
139
+ } else {
140
+ console.log(`${pad("Plugin", VERSION_COL)}${chalk.dim("not installed")}`);
141
+ }
142
+ console.log(versionLine("CLI", cliVersion, latestCli));
143
+ console.log();
144
+ console.log(chalk.bold("\u{1F527} Plugin"));
145
+ const detectedScope = detectPluginScope();
146
+ console.log(`${pad("Scope", VERSION_COL)}${detectedScope}`);
147
+ const disabled = isPluginDisabled(detectedScope);
148
+ console.log(
149
+ `${pad("Status", VERSION_COL)}${disabled ? chalk.yellow("disabled") : chalk.green("enabled")}`
150
+ );
151
+ try {
152
+ const claudeSettings = readJson(claudeSettingsPath());
153
+ const marketplaces = claudeSettings?.extraKnownMarketplaces;
154
+ const autoUpdate = marketplaces?.["coding-friend-marketplace"]?.autoUpdate === true;
155
+ console.log(
156
+ `${pad("Auto-update", VERSION_COL)}${autoUpdate ? chalk.green("on") : chalk.dim("off")}`
157
+ );
158
+ } catch {
159
+ console.log(`${pad("Auto-update", VERSION_COL)}${chalk.dim("unknown")}`);
160
+ }
161
+ const devState = readJson(devStatePath());
162
+ console.log(
163
+ `${pad("Dev mode", VERSION_COL)}${devState ? chalk.cyan("on") : chalk.dim("off")}`
164
+ );
165
+ const rules = getExistingRules(claudeSettingsPath());
166
+ console.log(
167
+ `${pad("Permissions", VERSION_COL)}${rules.length} rules ${chalk.dim('\u2192 Run "cf permission" for details')}`
168
+ );
169
+ console.log();
170
+ console.log(chalk.bold("\u{1F9E0} Memory"));
171
+ const memoryDir = resolveMemoryDir();
172
+ const docCount = countMdFiles(memoryDir);
173
+ let tierLabel = chalk.dim("unavailable");
174
+ let daemonLabel = chalk.dim("unavailable");
175
+ try {
176
+ const mcpDir = getLibPath("cf-memory");
177
+ if (existsSync(join(mcpDir, "dist"))) {
178
+ const { areSqliteDepsAvailable } = await import(join(mcpDir, "dist/lib/lazy-install.js"));
179
+ const { isDaemonRunning, getDaemonInfo } = await import(join(mcpDir, "dist/daemon/process.js"));
180
+ const sqliteAvailable = areSqliteDepsAvailable();
181
+ const running = await isDaemonRunning();
182
+ if (sqliteAvailable) {
183
+ tierLabel = chalk.cyan("Tier 1 (SQLite + Hybrid)");
184
+ } else if (running) {
185
+ tierLabel = chalk.cyan("Tier 2 (MiniSearch + Daemon)");
186
+ } else {
187
+ tierLabel = chalk.cyan("Tier 3 (Markdown)");
188
+ }
189
+ if (running) {
190
+ const info = getDaemonInfo();
191
+ if (info) {
192
+ const uptime = (Date.now() - info.startedAt) / 1e3;
193
+ daemonLabel = `${chalk.green("running")} (PID ${info.pid}, uptime ${formatUptime(uptime)}) ${chalk.dim('\u2192 "cf memory stop-daemon" to stop')}`;
194
+ } else {
195
+ daemonLabel = `${chalk.green("running")} ${chalk.dim('\u2192 "cf memory stop-daemon" to stop')}`;
196
+ }
197
+ } else {
198
+ daemonLabel = `${chalk.dim("stopped")} ${chalk.dim('\u2192 "cf memory start-daemon" to start')}`;
199
+ }
200
+ }
201
+ } catch {
202
+ }
203
+ console.log(`${pad("Tier", VERSION_COL)}${tierLabel}`);
204
+ console.log(`${pad("Daemon", VERSION_COL)}${daemonLabel}`);
205
+ console.log(`${pad("Documents", VERSION_COL)}${docCount} files`);
206
+ console.log(chalk.dim(` \u2192 Run "cf memory status" for details`));
207
+ const globalConfig = readJson(globalConfigPath());
208
+ const localConfig = readJson(localConfigPath());
209
+ if (globalConfig && Object.keys(globalConfig).length > 0) {
210
+ console.log();
211
+ console.log(chalk.bold(`\u2699\uFE0F Config (global: ${globalConfigPath()})`));
212
+ printConfig(globalConfig, null);
213
+ }
214
+ if (localConfig && Object.keys(localConfig).length > 0) {
215
+ console.log();
216
+ console.log(chalk.bold(`\u2699\uFE0F Config (local: ${localConfigPath()})`));
217
+ printConfig(localConfig, globalConfig);
218
+ }
219
+ console.log();
220
+ }
221
+ export {
222
+ statusCommand
223
+ };
@@ -1,11 +1,11 @@
1
1
  import {
2
2
  isMarketplaceRegistered,
3
3
  isPluginInstalled
4
- } from "./chunk-YC6MBHCT.js";
4
+ } from "./chunk-JFGLNTZI.js";
5
5
  import {
6
6
  hasShellCompletion,
7
7
  removeShellCompletion
8
- } from "./chunk-VUAUAO2R.js";
8
+ } from "./chunk-NEQZP5D4.js";
9
9
  import {
10
10
  resolveScope
11
11
  } from "./chunk-C5LYVVEI.js";
@@ -1,16 +1,20 @@
1
1
  import {
2
+ getCliVersion,
3
+ getLatestCliVersion,
2
4
  getLatestVersion,
3
5
  semverCompare,
4
6
  updateCommand
5
- } from "./chunk-A427XMWE.js";
7
+ } from "./chunk-IRPW2BMP.js";
6
8
  import "./chunk-ORACWEDN.js";
7
9
  import "./chunk-POC2WHU2.js";
8
- import "./chunk-VUAUAO2R.js";
10
+ import "./chunk-NEQZP5D4.js";
9
11
  import "./chunk-C5LYVVEI.js";
10
12
  import "./chunk-CYQU33FY.js";
11
13
  import "./chunk-RWUTFVRB.js";
12
14
  import "./chunk-W5CD7WTX.js";
13
15
  export {
16
+ getCliVersion,
17
+ getLatestCliVersion,
14
18
  getLatestVersion,
15
19
  semverCompare,
16
20
  updateCommand
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "coding-friend-cli",
3
- "version": "1.18.0",
3
+ "version": "1.19.0",
4
4
  "description": "CLI for coding-friend — host learning docs, setup MCP server, initialize projects",
5
5
  "type": "module",
6
6
  "bin": {