coding-friend-cli 1.17.0 → 1.17.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.
package/dist/index.js CHANGED
@@ -30,7 +30,7 @@ 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-YK6YRTOT.js");
33
+ const { initCommand } = await import("./init-MF7ISADJ.js");
34
34
  await initCommand();
35
35
  });
36
36
  program.command("config").description("Manage Coding Friend configuration").action(async () => {
@@ -92,39 +92,39 @@ Memory subcommands:
92
92
  memory mcp Show MCP server setup instructions`
93
93
  );
94
94
  memory.command("status").description("Show memory system status").action(async () => {
95
- const { memoryStatusCommand } = await import("./memory-7RM67ZLS.js");
95
+ const { memoryStatusCommand } = await import("./memory-RGLM35HC.js");
96
96
  await memoryStatusCommand();
97
97
  });
98
98
  memory.command("search").description("Search memories by query").argument("<query>", "search query").action(async (query) => {
99
- const { memorySearchCommand } = await import("./memory-7RM67ZLS.js");
99
+ const { memorySearchCommand } = await import("./memory-RGLM35HC.js");
100
100
  await memorySearchCommand(query);
101
101
  });
102
102
  memory.command("list").description(
103
103
  "List memories in current project, or all projects with --projects"
104
104
  ).option("--projects", "List all project databases with size and metadata").action(async (opts) => {
105
- const { memoryListCommand } = await import("./memory-7RM67ZLS.js");
105
+ const { memoryListCommand } = await import("./memory-RGLM35HC.js");
106
106
  await memoryListCommand(opts);
107
107
  });
108
108
  memory.command("init").description(
109
109
  "Initialize Tier 1 \u2014 install SQLite deps and import existing memories"
110
110
  ).action(async () => {
111
- const { memoryInitCommand } = await import("./memory-7RM67ZLS.js");
111
+ const { memoryInitCommand } = await import("./memory-RGLM35HC.js");
112
112
  await memoryInitCommand();
113
113
  });
114
114
  memory.command("start").description("Start the memory daemon (Tier 2 \u2014 MiniSearch)").action(async () => {
115
- const { memoryStartCommand } = await import("./memory-7RM67ZLS.js");
115
+ const { memoryStartCommand } = await import("./memory-RGLM35HC.js");
116
116
  await memoryStartCommand();
117
117
  });
118
118
  memory.command("stop").description("Stop the memory daemon").action(async () => {
119
- const { memoryStopCommand } = await import("./memory-7RM67ZLS.js");
119
+ const { memoryStopCommand } = await import("./memory-RGLM35HC.js");
120
120
  await memoryStopCommand();
121
121
  });
122
122
  memory.command("rebuild").description("Rebuild the daemon search index").action(async () => {
123
- const { memoryRebuildCommand } = await import("./memory-7RM67ZLS.js");
123
+ const { memoryRebuildCommand } = await import("./memory-RGLM35HC.js");
124
124
  await memoryRebuildCommand();
125
125
  });
126
126
  memory.command("mcp").description("Show MCP server setup instructions").action(async () => {
127
- const { memoryMcpCommand } = await import("./memory-7RM67ZLS.js");
127
+ const { memoryMcpCommand } = await import("./memory-RGLM35HC.js");
128
128
  await memoryMcpCommand();
129
129
  });
130
130
  memory.command("rm").description("Remove a project database").option("--project-id <id>", "Project ID to remove").option("--all", "Remove all project databases").option(
@@ -132,7 +132,7 @@ memory.command("rm").description("Remove a project database").option("--project-
132
132
  "Remove orphaned projects (source dir missing or 0 memories)"
133
133
  ).action(
134
134
  async (opts) => {
135
- const { memoryRmCommand } = await import("./memory-7RM67ZLS.js");
135
+ const { memoryRmCommand } = await import("./memory-RGLM35HC.js");
136
136
  await memoryRmCommand(opts);
137
137
  }
138
138
  );
@@ -636,7 +636,7 @@ async function stepMemory(docsDir) {
636
636
  }
637
637
  }
638
638
  log.info(
639
- `Tip: Run ${chalk.cyan("/cf-onboard")} in Claude Code to populate memory with project knowledge.`
639
+ `Tip: Run ${chalk.cyan("/cf-scan")} in Claude Code to populate memory with project knowledge.`
640
640
  );
641
641
  }
642
642
  async function stepClaudePermissions(outputDir, autoCommit) {
@@ -773,7 +773,7 @@ async function initCommand() {
773
773
  console.log();
774
774
  log.congrats("Setup complete!");
775
775
  log.dim(
776
- "Available commands: /cf-ask, /cf-plan, /cf-fix, /cf-commit, /cf-review, /cf-ship, /cf-optimize, /cf-onboard, /cf-remember, /cf-learn, /cf-research, /cf-session, /cf-help"
776
+ "Available commands: /cf-ask, /cf-plan, /cf-fix, /cf-commit, /cf-review, /cf-ship, /cf-optimize, /cf-scan, /cf-remember, /cf-learn, /cf-research, /cf-session, /cf-help"
777
777
  );
778
778
  }
779
779
  export {
@@ -19,7 +19,6 @@ import { createHash } from "crypto";
19
19
  import { existsSync, readdirSync, statSync, rmSync } from "fs";
20
20
  import { join, resolve, sep } from "path";
21
21
  import { homedir } from "os";
22
- import { spawn } from "child_process";
23
22
  import { confirm } from "@inquirer/prompts";
24
23
  import chalk from "chalk";
25
24
  function countMdFiles(dir) {
@@ -88,7 +87,7 @@ async function memoryStatusCommand() {
88
87
  if (running && daemonInfo) {
89
88
  const uptime = (Date.now() - daemonInfo.startedAt) / 1e3;
90
89
  log.info(
91
- `Daemon: ${chalk.green("running")} (PID ${daemonInfo.pid}, uptime ${formatUptime(uptime)})`
90
+ `Daemon: ${chalk.green("running")} (PID ${daemonInfo.pid}, uptime ${formatUptime(uptime)}) ${chalk.dim('Turn it off by "cf memory stop"')}`
92
91
  );
93
92
  } else if (sqliteAvailable) {
94
93
  log.info(
@@ -162,7 +161,7 @@ async function memoryListCommand(opts) {
162
161
  "This folder has no memories yet. Use --projects to list all project databases."
163
162
  );
164
163
  log.dim(
165
- 'Or run "cf init" to set up this project, then "/cf-onboard" in Claude Code.'
164
+ 'Or run "cf init" to set up this project, then "/cf-scan" in Claude Code.'
166
165
  );
167
166
  return;
168
167
  }
@@ -209,43 +208,23 @@ async function memoryStartCommand() {
209
208
  const memoryDir = getMemoryDir();
210
209
  const mcpDir = getLibPath("cf-memory");
211
210
  ensureBuilt(mcpDir);
212
- const { isDaemonRunning, getDaemonInfo } = await import(join(mcpDir, "dist/daemon/process.js"));
211
+ const { isDaemonRunning, getDaemonInfo, spawnDaemon } = await import(join(mcpDir, "dist/daemon/process.js"));
213
212
  if (await isDaemonRunning()) {
214
213
  const info = getDaemonInfo();
215
214
  log.info(`Daemon already running (PID ${info?.pid})`);
216
215
  return;
217
216
  }
218
- const entryPath = join(mcpDir, "dist/daemon/entry.js");
219
- if (!existsSync(entryPath)) {
220
- log.error("Daemon entry point not found. Try rebuilding: npm run build");
221
- process.exit(1);
222
- }
223
217
  log.step("Starting memory daemon...");
224
- const args = [entryPath, memoryDir];
225
218
  const config = loadConfig();
226
219
  const embedding = config.memory?.embedding;
227
- if (embedding?.provider)
228
- args.push(`--embedding-provider=${embedding.provider}`);
229
- if (embedding?.model) args.push(`--embedding-model=${embedding.model}`);
230
- if (embedding?.ollamaUrl)
231
- args.push(`--embedding-ollama-url=${embedding.ollamaUrl}`);
232
- const child = spawn("node", args, {
233
- detached: true,
234
- stdio: "ignore",
235
- env: { ...process.env }
236
- });
237
- child.unref();
238
- for (let i = 0; i < 50; i++) {
239
- await new Promise((r) => setTimeout(r, 100));
240
- if (await isDaemonRunning()) {
241
- const info = getDaemonInfo();
242
- log.success(`Daemon started (PID ${info?.pid})`);
243
- log.info(`Tier: ${chalk.cyan("Tier 2 (MiniSearch + Daemon)")}`);
244
- return;
245
- }
220
+ const result = await spawnDaemon(memoryDir, embedding);
221
+ if (result) {
222
+ log.success(`Daemon started (PID ${result.pid})`);
223
+ log.info(`Watching ${chalk.cyan(memoryDir)} for changes`);
224
+ } else {
225
+ log.error("Daemon did not start within 3 seconds");
226
+ process.exit(1);
246
227
  }
247
- log.error("Daemon did not start within 5 seconds");
248
- process.exit(1);
249
228
  }
250
229
  async function memoryStopCommand() {
251
230
  const mcpDir = getLibPath("cf-memory");
@@ -368,7 +347,7 @@ async function memoryInitCommand() {
368
347
  }
369
348
  log.success('Tier 1 initialized. Run "cf memory status" to verify.');
370
349
  log.info(
371
- `Tip: Run ${chalk.cyan("/cf-onboard")} in Claude Code to populate memory with project knowledge.`
350
+ `Tip: Run ${chalk.cyan("/cf-scan")} in Claude Code to populate memory with project knowledge.`
372
351
  );
373
352
  }
374
353
  function getProjectsBaseDir() {
@@ -1,6 +1,11 @@
1
1
  # CF Memory Changelog
2
2
 
3
- ## v0.0.1 (unpublished)
3
+ ## v0.1.0 (2026-03-17)
4
+
5
+ - Auto-start daemon from MCP server for file watching — daemon spawns automatically when Tier 1 or 2 is detected, no manual `cf memory start` needed [#2211b84](https://github.com/dinhanhthi/coding-friend/commit/2211b84)
6
+ - Add `spawnDaemon()` function to `daemon/process.ts` with dynamic path resolution via `import.meta.url` [#2211b84](https://github.com/dinhanhthi/coding-friend/commit/2211b84)
7
+
8
+ ## v0.0.1 (2026-03-16)
4
9
 
5
10
  - Add persistent memory system — MCP server with 6 tools (`memory_store`, `memory_search`, `memory_retrieve`, `memory_list`, `memory_update`, `memory_delete`) + 2 resources (`memory://index`, `memory://stats`)
6
11
  - Add 3-tier graceful degradation: SQLite + hybrid search (Tier 1) → MiniSearch daemon + BM25/fuzzy (Tier 2) → Markdown file I/O (Tier 3)
@@ -13,3 +18,8 @@
13
18
  - Add deduplication detection and temporal decay scoring
14
19
  - Add frontmatter migration script for existing `docs/memory/` files
15
20
  - Migrate `/cf-remember` and Frontmatter Recall to use `memory_search` MCP tool
21
+ - Add `cf memory rm` and `cf memory ls` commands for project database management [#1da99b1](https://github.com/dinhanhthi/coding-friend/commit/1da99b1)
22
+ - Add dynamic embedding dimensions support and config wiring [#b7f6eba](https://github.com/dinhanhthi/coding-friend/commit/b7f6eba)
23
+ - Fix correct embedding model name in v1→v2 migration [#04d3c19](https://github.com/dinhanhthi/coding-friend/commit/04d3c19)
24
+ - Fix empty project directories created on SQLite backend failure [#4ab7570](https://github.com/dinhanhthi/coding-friend/commit/4ab7570)
25
+ - Fix unit tests creating orphaned SQLite databases [#ec0bfee](https://github.com/dinhanhthi/coding-friend/commit/ec0bfee)
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "coding-friend-cf-memory",
3
- "version": "0.0.1",
3
+ "version": "0.1.0",
4
4
  "private": true,
5
5
  "type": "module",
6
6
  "bin": {
@@ -176,6 +176,57 @@ export function startDaemonServer(
176
176
  return { close: shutdown, server };
177
177
  }
178
178
 
179
+ /**
180
+ * Spawn the daemon as a detached background process (if not already running).
181
+ *
182
+ * Resolves `daemon/entry.js` relative to this file so it works from both
183
+ * the MCP server and the CLI without hardcoding paths.
184
+ */
185
+ export async function spawnDaemon(
186
+ docsDir: string,
187
+ embeddingConfig?: {
188
+ provider?: string;
189
+ model?: string;
190
+ ollamaUrl?: string;
191
+ },
192
+ ): Promise<{ pid: number } | null> {
193
+ if (await isDaemonRunning()) return null;
194
+
195
+ const { spawn } = await import("node:child_process");
196
+ const { fileURLToPath } = await import("node:url");
197
+
198
+ const thisDir = path.dirname(fileURLToPath(import.meta.url));
199
+ const entryPath = path.join(thisDir, "entry.js");
200
+
201
+ if (!fs.existsSync(entryPath)) return null;
202
+
203
+ const args = [entryPath, docsDir];
204
+ if (embeddingConfig?.provider)
205
+ args.push(`--embedding-provider=${embeddingConfig.provider}`);
206
+ if (embeddingConfig?.model)
207
+ args.push(`--embedding-model=${embeddingConfig.model}`);
208
+ if (embeddingConfig?.ollamaUrl)
209
+ args.push(`--embedding-ollama-url=${embeddingConfig.ollamaUrl}`);
210
+
211
+ const child = spawn("node", args, {
212
+ detached: true,
213
+ stdio: "ignore",
214
+ env: { ...process.env },
215
+ });
216
+ child.unref();
217
+
218
+ // Wait for daemon to be ready (max 3 seconds)
219
+ for (let i = 0; i < 30; i++) {
220
+ await new Promise((r) => setTimeout(r, 100));
221
+ if (await isDaemonRunning()) {
222
+ const info = getDaemonInfo();
223
+ return info ? { pid: info.pid } : null;
224
+ }
225
+ }
226
+
227
+ return null;
228
+ }
229
+
179
230
  /**
180
231
  * Stop the daemon by sending SIGTERM.
181
232
  */
@@ -33,6 +33,14 @@ const { backend, tier } = await createBackendForTier(
33
33
  embeddingConfig,
34
34
  );
35
35
 
36
+ // Auto-start daemon for file watching (Tier 1 & 2)
37
+ // Daemon watches docs/memory/ for external changes (git pull, manual edits)
38
+ // and rebuilds the search index automatically.
39
+ if (tier.name !== "markdown") {
40
+ const { spawnDaemon } = await import("./daemon/process.js");
41
+ spawnDaemon(docsDir, embeddingConfig).catch(() => {});
42
+ }
43
+
36
44
  const server = new McpServer({
37
45
  name: "coding-friend-memory",
38
46
  version: "0.0.1",
@@ -1,9 +1,5 @@
1
1
  # Changelog (Learn Host)
2
2
 
3
- ## v0.2.2 (2026-03-16)
4
-
5
- - Improve `cf init` to be more interactive with step-by-step guided setup ([#d5bbc9c](https://github.com/dinhanhthi/coding-friend/commit/d5bbc9c))
6
-
7
3
  ## v0.2.1 (2026-03-05)
8
4
 
9
5
  - Fix TOC heading text stripping markdown links from slug generation ([#9a8fb5c](https://github.com/dinhanhthi/coding-friend/commit/9a8fb5c))
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "coding-friend-learn-host",
3
- "version": "0.2.2",
3
+ "version": "0.2.1",
4
4
  "private": true,
5
5
  "scripts": {
6
6
  "dev": "next dev -p 3333",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "coding-friend-cli",
3
- "version": "1.17.0",
3
+ "version": "1.17.1",
4
4
  "description": "CLI for coding-friend — host learning docs, setup MCP server, initialize projects",
5
5
  "type": "module",
6
6
  "bin": {