coding-friend-cli 1.31.3 → 1.32.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/{chunk-ZEROY3CW.js → chunk-355AR2X5.js} +161 -116
- package/dist/{chunk-YMCAGIYB.js → chunk-5W7ZOVDU.js} +7 -58
- package/dist/{chunk-65S7Q5PW.js → chunk-GTX6I57V.js} +0 -3
- package/dist/{config-OLEUUS32.js → config-FUEEFM5N.js} +3 -3
- package/dist/{host-DTWSRS2U.js → host-RNYPAE4D.js} +3 -3
- package/dist/index.js +21 -17
- package/dist/{init-W55DKHKZ.js → init-3UITY4LD.js} +3 -3
- package/dist/{mcp-JL2EUJBT.js → mcp-LJ5NX6XE.js} +11 -7
- package/dist/mcp-serve-3FEPFVVQ.js +27 -0
- package/dist/{memory-VOLXT5SX.js → memory-2ZD4WU3S.js} +3 -3
- package/dist/{session-COIV2PO5.js → session-FMC2MG6C.js} +1 -1
- package/dist/{status-CYAKIHFW.js → status-FU5GR3MP.js} +3 -3
- package/package.json +1 -1
|
@@ -1,20 +1,19 @@
|
|
|
1
1
|
import {
|
|
2
2
|
editMemoryAutoCapture,
|
|
3
3
|
editMemoryAutoStart,
|
|
4
|
-
editMemoryDaemonTimeout,
|
|
5
4
|
editMemoryEmbedding,
|
|
6
5
|
editMemoryTier,
|
|
7
6
|
getMemoryMcpStatus,
|
|
8
7
|
memoryConfigMenu,
|
|
9
8
|
writeMemoryMcpEntry
|
|
10
|
-
} from "./chunk-
|
|
11
|
-
import {
|
|
12
|
-
getLibPath
|
|
13
|
-
} from "./chunk-RZRT7NGT.js";
|
|
9
|
+
} from "./chunk-5W7ZOVDU.js";
|
|
14
10
|
import {
|
|
15
11
|
loadConfig,
|
|
16
12
|
resolveMemoryDir
|
|
17
|
-
} from "./chunk-
|
|
13
|
+
} from "./chunk-GTX6I57V.js";
|
|
14
|
+
import {
|
|
15
|
+
getLibPath
|
|
16
|
+
} from "./chunk-RZRT7NGT.js";
|
|
18
17
|
import {
|
|
19
18
|
showConfigHint
|
|
20
19
|
} from "./chunk-HPNRQYLM.js";
|
|
@@ -35,17 +34,74 @@ import {
|
|
|
35
34
|
} from "./chunk-5UVDWG5L.js";
|
|
36
35
|
|
|
37
36
|
// src/commands/memory.ts
|
|
38
|
-
import { existsSync, readdirSync, statSync, rmSync } from "fs";
|
|
39
|
-
import { join, resolve, sep } from "path";
|
|
37
|
+
import { existsSync as existsSync2, readdirSync, statSync, rmSync } from "fs";
|
|
38
|
+
import { join as join2, resolve, sep } from "path";
|
|
40
39
|
import { homedir } from "os";
|
|
41
40
|
import { confirm } from "@inquirer/prompts";
|
|
41
|
+
|
|
42
|
+
// src/lib/mcp-state.ts
|
|
43
|
+
import { existsSync } from "fs";
|
|
44
|
+
import { join } from "path";
|
|
42
45
|
import chalk from "chalk";
|
|
46
|
+
function warnStaleMcpJson(memoryDir) {
|
|
47
|
+
const localMcpPath = join(process.cwd(), ".mcp.json");
|
|
48
|
+
if (!existsSync(localMcpPath)) return;
|
|
49
|
+
const mcpJson = readJson(localMcpPath);
|
|
50
|
+
if (mcpJson === null) {
|
|
51
|
+
log.warn(".mcp.json exists but could not be parsed \u2014 check for syntax errors.");
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
const state = detectMemoryMcpState(mcpJson, existsSync);
|
|
55
|
+
if (state.kind === "stale" || state.kind === "legacy-valid") {
|
|
56
|
+
if (memoryDir) {
|
|
57
|
+
writeMemoryMcpEntry(memoryDir);
|
|
58
|
+
if (state.kind === "stale") {
|
|
59
|
+
log.success("Auto-updated stale .mcp.json to version-stable npx format.");
|
|
60
|
+
} else {
|
|
61
|
+
log.success("Updated .mcp.json to version-stable npx format.");
|
|
62
|
+
}
|
|
63
|
+
console.log();
|
|
64
|
+
} else if (state.kind === "stale") {
|
|
65
|
+
console.log(chalk.yellow(`\u26A0 Stale MCP config detected in .mcp.json`));
|
|
66
|
+
console.log(chalk.dim(` Path no longer exists: ${state.path}`));
|
|
67
|
+
console.log(chalk.dim(` Run "cf memory mcp" to update to the new format.`));
|
|
68
|
+
console.log();
|
|
69
|
+
} else {
|
|
70
|
+
console.log(chalk.cyan(`\u2139 .mcp.json uses an absolute path for coding-friend-memory.`));
|
|
71
|
+
console.log(chalk.dim(` Consider running "cf memory mcp" to switch to the version-stable format.`));
|
|
72
|
+
console.log();
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
function detectMemoryMcpState(mcpJson, pathExists) {
|
|
77
|
+
if (!mcpJson) return { kind: "none" };
|
|
78
|
+
const servers = mcpJson.mcpServers;
|
|
79
|
+
if (!servers) return { kind: "none" };
|
|
80
|
+
const entry = servers["coding-friend-memory"];
|
|
81
|
+
if (!entry) return { kind: "none" };
|
|
82
|
+
const command = entry.command;
|
|
83
|
+
if (!command) return { kind: "none" };
|
|
84
|
+
if (command === "npx") return { kind: "npx" };
|
|
85
|
+
if (command === "node") {
|
|
86
|
+
const args = entry.args;
|
|
87
|
+
const serverPath = args?.[0];
|
|
88
|
+
if (!serverPath) return { kind: "none" };
|
|
89
|
+
if (!pathExists(serverPath)) {
|
|
90
|
+
return { kind: "stale", path: serverPath };
|
|
91
|
+
}
|
|
92
|
+
return { kind: "legacy-valid", path: serverPath };
|
|
93
|
+
}
|
|
94
|
+
return { kind: "none" };
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// src/commands/memory.ts
|
|
98
|
+
import chalk2 from "chalk";
|
|
43
99
|
function countMdFiles(dir) {
|
|
44
|
-
if (!
|
|
100
|
+
if (!existsSync2(dir)) return 0;
|
|
45
101
|
let count = 0;
|
|
46
102
|
for (const entry of readdirSync(dir, { withFileTypes: true })) {
|
|
47
103
|
if (entry.isDirectory()) {
|
|
48
|
-
count += countMdFiles(
|
|
104
|
+
count += countMdFiles(join2(dir, entry.name));
|
|
49
105
|
} else if (entry.name.endsWith(".md") && entry.name !== "README.md") {
|
|
50
106
|
count++;
|
|
51
107
|
}
|
|
@@ -65,7 +121,7 @@ function truncateError(text) {
|
|
|
65
121
|
return [...head, ` ... (${skipped} lines omitted) ...`, ...tail].join("\n");
|
|
66
122
|
}
|
|
67
123
|
function ensureMemoryBuilt(mcpDir) {
|
|
68
|
-
if (!
|
|
124
|
+
if (!existsSync2(join2(mcpDir, "node_modules"))) {
|
|
69
125
|
log.step("Installing memory server dependencies (one-time setup)...");
|
|
70
126
|
const result = runWithStderr("npm", ["install"], { cwd: mcpDir });
|
|
71
127
|
if (result.exitCode !== 0) {
|
|
@@ -75,7 +131,7 @@ function ensureMemoryBuilt(mcpDir) {
|
|
|
75
131
|
}
|
|
76
132
|
log.success("Done.");
|
|
77
133
|
}
|
|
78
|
-
if (!
|
|
134
|
+
if (!existsSync2(join2(mcpDir, "dist"))) {
|
|
79
135
|
log.step("Building memory server...");
|
|
80
136
|
const result = runWithStderr("npm", ["run", "build"], { cwd: mcpDir });
|
|
81
137
|
if (result.exitCode !== 0) {
|
|
@@ -87,23 +143,23 @@ function ensureMemoryBuilt(mcpDir) {
|
|
|
87
143
|
}
|
|
88
144
|
}
|
|
89
145
|
function printMemoryMcpConfig(serverPath, memoryDir) {
|
|
90
|
-
console.log(
|
|
146
|
+
console.log(chalk2.dim("Add this to your MCP client config:"));
|
|
91
147
|
console.log();
|
|
92
148
|
console.log(
|
|
93
|
-
|
|
149
|
+
chalk2.yellow.bold("--- Claude Code (.mcp.json in project root) ---")
|
|
94
150
|
);
|
|
95
151
|
console.log(`
|
|
96
152
|
{
|
|
97
153
|
"mcpServers": {
|
|
98
154
|
"coding-friend-memory": {
|
|
99
|
-
"command": "
|
|
100
|
-
"args": ["
|
|
155
|
+
"command": "npx",
|
|
156
|
+
"args": ["-y", "coding-friend-cli", "mcp-serve", "${memoryDir}"]
|
|
101
157
|
}
|
|
102
158
|
}
|
|
103
159
|
}`);
|
|
104
160
|
console.log();
|
|
105
161
|
console.log(
|
|
106
|
-
|
|
162
|
+
chalk2.yellow.bold(
|
|
107
163
|
"--- Claude Desktop / Claude Chat (claude_desktop_config.json) ---"
|
|
108
164
|
)
|
|
109
165
|
);
|
|
@@ -117,39 +173,39 @@ function printMemoryMcpConfig(serverPath, memoryDir) {
|
|
|
117
173
|
}
|
|
118
174
|
}`);
|
|
119
175
|
console.log();
|
|
120
|
-
console.log(
|
|
176
|
+
console.log(chalk2.yellow.bold("--- Generic MCP client ---"));
|
|
121
177
|
console.log(`
|
|
122
178
|
Server command: node ${serverPath} ${memoryDir}
|
|
123
179
|
Transport: stdio`);
|
|
124
180
|
console.log();
|
|
125
|
-
console.log(
|
|
181
|
+
console.log(chalk2.yellow.bold("--- Available tools ---"));
|
|
126
182
|
console.log();
|
|
127
183
|
console.log(
|
|
128
|
-
` ${
|
|
184
|
+
` ${chalk2.white("memory_store")} ${chalk2.dim("Store a new memory")}`
|
|
129
185
|
);
|
|
130
186
|
console.log(
|
|
131
|
-
` ${
|
|
187
|
+
` ${chalk2.white("memory_search")} ${chalk2.dim("Search memories (keyword match)")}`
|
|
132
188
|
);
|
|
133
189
|
console.log(
|
|
134
|
-
` ${
|
|
190
|
+
` ${chalk2.white("memory_retrieve")} ${chalk2.dim("Get a specific memory by ID")}`
|
|
135
191
|
);
|
|
136
192
|
console.log(
|
|
137
|
-
` ${
|
|
193
|
+
` ${chalk2.white("memory_list")} ${chalk2.dim("List memories with filtering")}`
|
|
138
194
|
);
|
|
139
195
|
console.log(
|
|
140
|
-
` ${
|
|
196
|
+
` ${chalk2.white("memory_update")} ${chalk2.dim("Update existing memory")}`
|
|
141
197
|
);
|
|
142
198
|
console.log(
|
|
143
|
-
` ${
|
|
199
|
+
` ${chalk2.white("memory_delete")} ${chalk2.dim("Delete a memory")}`
|
|
144
200
|
);
|
|
145
201
|
console.log();
|
|
146
|
-
console.log(
|
|
202
|
+
console.log(chalk2.yellow.bold("--- Resources ---"));
|
|
147
203
|
console.log();
|
|
148
204
|
console.log(
|
|
149
|
-
` ${
|
|
205
|
+
` ${chalk2.white("memory://index")} ${chalk2.dim("Browse all memories")}`
|
|
150
206
|
);
|
|
151
207
|
console.log(
|
|
152
|
-
` ${
|
|
208
|
+
` ${chalk2.white("memory://stats")} ${chalk2.dim("Storage statistics")}`
|
|
153
209
|
);
|
|
154
210
|
console.log();
|
|
155
211
|
log.warn(
|
|
@@ -167,43 +223,43 @@ async function memoryStatusCommand() {
|
|
|
167
223
|
const docCount = countMdFiles(memoryDir);
|
|
168
224
|
const mcpDir = getLibPath("cf-memory");
|
|
169
225
|
ensureMemoryBuilt(mcpDir);
|
|
170
|
-
const { isDaemonRunning, getDaemonInfo } = await import(
|
|
171
|
-
const { areSqliteDepsAvailable } = await import(
|
|
226
|
+
const { isDaemonRunning, getDaemonInfo } = await import(join2(mcpDir, "dist/daemon/process.js"));
|
|
227
|
+
const { areSqliteDepsAvailable } = await import(join2(mcpDir, "dist/lib/lazy-install.js"));
|
|
172
228
|
const sqliteAvailable = areSqliteDepsAvailable();
|
|
173
229
|
const running = await isDaemonRunning();
|
|
174
230
|
const daemonInfo = getDaemonInfo();
|
|
175
231
|
let tierLabel;
|
|
176
232
|
if (sqliteAvailable) {
|
|
177
|
-
tierLabel =
|
|
233
|
+
tierLabel = chalk2.cyan("Tier 1 (SQLite + Hybrid)");
|
|
178
234
|
} else if (running) {
|
|
179
|
-
tierLabel =
|
|
235
|
+
tierLabel = chalk2.cyan("Tier 2 (MiniSearch + Daemon)");
|
|
180
236
|
} else {
|
|
181
|
-
tierLabel =
|
|
237
|
+
tierLabel = chalk2.cyan("Tier 3 (Markdown)");
|
|
182
238
|
}
|
|
183
239
|
printBanner("\u{1F9E0} Coding Friend Memory");
|
|
184
240
|
console.log();
|
|
185
241
|
log.info(`Tier: ${tierLabel}`);
|
|
186
|
-
log.info(`Memory dir: ${
|
|
187
|
-
log.info(`Memories in this dir: ${
|
|
242
|
+
log.info(`Memory dir: ${chalk2.cyan(memoryDir)}`);
|
|
243
|
+
log.info(`Memories in this dir: ${chalk2.green(String(docCount))}`);
|
|
188
244
|
if (running && daemonInfo) {
|
|
189
245
|
const uptime = (Date.now() - daemonInfo.startedAt) / 1e3;
|
|
190
246
|
log.info(
|
|
191
|
-
`Daemon: ${
|
|
247
|
+
`Daemon: ${chalk2.green("running")} (PID ${daemonInfo.pid}, uptime ${formatUptime(uptime)}) ${chalk2.dim('Turn it off by "cf memory stop-daemon"')}`
|
|
192
248
|
);
|
|
193
249
|
} else if (sqliteAvailable) {
|
|
194
250
|
log.info(
|
|
195
|
-
`Daemon: ${
|
|
251
|
+
`Daemon: ${chalk2.dim("stopped")} ${chalk2.dim("(not needed \u2014 Tier 1 uses SQLite directly)")}`
|
|
196
252
|
);
|
|
197
253
|
} else {
|
|
198
254
|
log.info(
|
|
199
|
-
`Daemon: ${
|
|
255
|
+
`Daemon: ${chalk2.dim("stopped")} ${chalk2.dim('(run "cf memory start-daemon" for Tier 2 search)')}`
|
|
200
256
|
);
|
|
201
257
|
}
|
|
202
258
|
if (sqliteAvailable) {
|
|
203
|
-
log.info(`SQLite deps: ${
|
|
259
|
+
log.info(`SQLite deps: ${chalk2.green("installed")}`);
|
|
204
260
|
} else {
|
|
205
261
|
log.info(
|
|
206
|
-
`SQLite deps: ${
|
|
262
|
+
`SQLite deps: ${chalk2.dim("not installed")} (run "cf memory init" to enable Tier 1)`
|
|
207
263
|
);
|
|
208
264
|
}
|
|
209
265
|
const config = loadConfig();
|
|
@@ -211,33 +267,33 @@ async function memoryStatusCommand() {
|
|
|
211
267
|
if (embeddingConfig?.provider || embeddingConfig?.model) {
|
|
212
268
|
const provider = embeddingConfig.provider ?? "transformers";
|
|
213
269
|
const model = embeddingConfig.model ?? (provider === "ollama" ? "all-minilm:l6-v2" : "Xenova/all-MiniLM-L6-v2");
|
|
214
|
-
log.info(`Embedding: ${
|
|
270
|
+
log.info(`Embedding: ${chalk2.cyan(model)} ${chalk2.dim(`(${provider})`)}`);
|
|
215
271
|
}
|
|
216
272
|
const mcpStatus = getMemoryMcpStatus();
|
|
217
273
|
if (mcpStatus.configured && mcpStatus.scope === "local") {
|
|
218
274
|
log.info(
|
|
219
|
-
`MCP: ${
|
|
275
|
+
`MCP: ${chalk2.green("configured")} ${chalk2.dim("(local .mcp.json)")}`
|
|
220
276
|
);
|
|
221
277
|
} else if (mcpStatus.configured && mcpStatus.scope === "global") {
|
|
222
278
|
log.info(
|
|
223
|
-
`MCP: ${
|
|
279
|
+
`MCP: ${chalk2.green("configured")} ${chalk2.dim("(global ~/.claude/.mcp.json)")} ${chalk2.yellow("\u26A0 global config uses a fixed path \u2014 only works for one project")}`
|
|
224
280
|
);
|
|
225
281
|
} else {
|
|
226
282
|
log.info(
|
|
227
|
-
`MCP: ${
|
|
283
|
+
`MCP: ${chalk2.dim("not configured in this project")} ${chalk2.dim('(run "cf memory init" or add manually via "cf memory mcp")')}`
|
|
228
284
|
);
|
|
229
285
|
}
|
|
230
286
|
const autoCapture = config.memory?.autoCapture ?? false;
|
|
231
287
|
log.info(
|
|
232
|
-
`Auto-capture: ${autoCapture ?
|
|
288
|
+
`Auto-capture: ${autoCapture ? chalk2.green("on") : chalk2.dim("off")}`
|
|
233
289
|
);
|
|
234
|
-
if (
|
|
290
|
+
if (existsSync2(memoryDir)) {
|
|
235
291
|
const categories = readdirSync(memoryDir, { withFileTypes: true }).filter((d) => d.isDirectory() && !d.name.startsWith(".")).map((d) => {
|
|
236
|
-
const catCount = countMdFiles(
|
|
292
|
+
const catCount = countMdFiles(join2(memoryDir, d.name));
|
|
237
293
|
return `${d.name} (${catCount})`;
|
|
238
294
|
}).filter((s) => !s.endsWith("(0)"));
|
|
239
295
|
if (categories.length > 0) {
|
|
240
|
-
log.info(`Categories: ${
|
|
296
|
+
log.info(`Categories: ${chalk2.dim(categories.join(", "))}`);
|
|
241
297
|
}
|
|
242
298
|
}
|
|
243
299
|
console.log();
|
|
@@ -246,7 +302,7 @@ async function memoryStatusCommand() {
|
|
|
246
302
|
}
|
|
247
303
|
async function memorySearchCommand(query) {
|
|
248
304
|
const memoryDir = getMemoryDir();
|
|
249
|
-
if (!
|
|
305
|
+
if (!existsSync2(memoryDir)) {
|
|
250
306
|
log.error(`Memory dir not found: ${memoryDir}`);
|
|
251
307
|
log.dim("Run `cf init` to create project folders.");
|
|
252
308
|
process.exit(1);
|
|
@@ -258,7 +314,7 @@ async function memorySearchCommand(query) {
|
|
|
258
314
|
[
|
|
259
315
|
"-e",
|
|
260
316
|
`
|
|
261
|
-
import { MarkdownBackend } from ${JSON.stringify(
|
|
317
|
+
import { MarkdownBackend } from ${JSON.stringify(join2(mcpDir, "dist/backends/markdown.js"))};
|
|
262
318
|
const backend = new MarkdownBackend(${JSON.stringify(memoryDir)});
|
|
263
319
|
const results = await backend.search({ query: process.env.CF_SEARCH_QUERY, limit: 10 });
|
|
264
320
|
for (const r of results) {
|
|
@@ -281,7 +337,7 @@ async function memoryListCommand(opts) {
|
|
|
281
337
|
return memoryListProjectsCommand();
|
|
282
338
|
}
|
|
283
339
|
const memoryDir = getMemoryDir();
|
|
284
|
-
if (!
|
|
340
|
+
if (!existsSync2(memoryDir)) {
|
|
285
341
|
log.info(`No memory directory found at: ${memoryDir}`);
|
|
286
342
|
log.dim(
|
|
287
343
|
"This folder has no memories yet. Use --projects to list all project databases."
|
|
@@ -298,7 +354,7 @@ async function memoryListCommand(opts) {
|
|
|
298
354
|
[
|
|
299
355
|
"-e",
|
|
300
356
|
`
|
|
301
|
-
import { MarkdownBackend } from ${JSON.stringify(
|
|
357
|
+
import { MarkdownBackend } from ${JSON.stringify(join2(mcpDir, "dist/backends/markdown.js"))};
|
|
302
358
|
const backend = new MarkdownBackend(${JSON.stringify(memoryDir)});
|
|
303
359
|
const metas = await backend.list({});
|
|
304
360
|
if (metas.length === 0) { console.log("No memories found."); process.exit(0); }
|
|
@@ -334,7 +390,7 @@ async function memoryStartDaemonCommand() {
|
|
|
334
390
|
const memoryDir = getMemoryDir();
|
|
335
391
|
const mcpDir = getLibPath("cf-memory");
|
|
336
392
|
ensureMemoryBuilt(mcpDir);
|
|
337
|
-
const { isDaemonRunning, getDaemonInfo, spawnDaemon } = await import(
|
|
393
|
+
const { isDaemonRunning, getDaemonInfo, spawnDaemon } = await import(join2(mcpDir, "dist/daemon/process.js"));
|
|
338
394
|
if (await isDaemonRunning()) {
|
|
339
395
|
const info = getDaemonInfo();
|
|
340
396
|
log.info(`Daemon already running (PID ${info?.pid})`);
|
|
@@ -343,11 +399,10 @@ async function memoryStartDaemonCommand() {
|
|
|
343
399
|
log.step("Starting memory daemon...");
|
|
344
400
|
const config = loadConfig();
|
|
345
401
|
const embedding = config.memory?.embedding;
|
|
346
|
-
const
|
|
347
|
-
const result = await spawnDaemon(memoryDir, embedding, { idleTimeoutMs });
|
|
402
|
+
const result = await spawnDaemon(memoryDir, embedding, {});
|
|
348
403
|
if (result) {
|
|
349
404
|
log.success(`Daemon started (PID ${result.pid})`);
|
|
350
|
-
log.info(`Watching ${
|
|
405
|
+
log.info(`Watching ${chalk2.cyan(memoryDir)} for changes`);
|
|
351
406
|
} else {
|
|
352
407
|
log.error("Daemon did not start within 3 seconds");
|
|
353
408
|
process.exit(1);
|
|
@@ -356,7 +411,7 @@ async function memoryStartDaemonCommand() {
|
|
|
356
411
|
async function memoryStopDaemonCommand() {
|
|
357
412
|
const mcpDir = getLibPath("cf-memory");
|
|
358
413
|
ensureMemoryBuilt(mcpDir);
|
|
359
|
-
const { stopDaemon, isDaemonRunning } = await import(
|
|
414
|
+
const { stopDaemon, isDaemonRunning } = await import(join2(mcpDir, "dist/daemon/process.js"));
|
|
360
415
|
if (!await isDaemonRunning()) {
|
|
361
416
|
log.info("Daemon is not running.");
|
|
362
417
|
return;
|
|
@@ -373,10 +428,10 @@ async function memoryRebuildCommand() {
|
|
|
373
428
|
const memoryDir = getMemoryDir();
|
|
374
429
|
const mcpDir = getLibPath("cf-memory");
|
|
375
430
|
ensureMemoryBuilt(mcpDir);
|
|
376
|
-
const { areSqliteDepsAvailable } = await import(
|
|
431
|
+
const { areSqliteDepsAvailable } = await import(join2(mcpDir, "dist/lib/lazy-install.js"));
|
|
377
432
|
if (areSqliteDepsAvailable()) {
|
|
378
433
|
log.step("Rebuilding SQLite index + embeddings...");
|
|
379
|
-
const { SqliteBackend } = await import(
|
|
434
|
+
const { SqliteBackend } = await import(join2(mcpDir, "dist/backends/sqlite/index.js"));
|
|
380
435
|
const config = loadConfig();
|
|
381
436
|
const embedding = config.memory?.embedding;
|
|
382
437
|
const opts = embedding ? { embedding, skipVec: false } : { skipVec: false };
|
|
@@ -386,7 +441,7 @@ async function memoryRebuildCommand() {
|
|
|
386
441
|
const stats = await backend.stats();
|
|
387
442
|
log.success(`Rebuilt: ${stats.total} memories indexed.`);
|
|
388
443
|
if (backend.isVecEnabled()) {
|
|
389
|
-
log.info(`Vector search: ${
|
|
444
|
+
log.info(`Vector search: ${chalk2.green("enabled")}`);
|
|
390
445
|
}
|
|
391
446
|
if (!backend.isRebuildNeeded()) {
|
|
392
447
|
log.info("Embedding dimensions: up to date");
|
|
@@ -396,14 +451,14 @@ async function memoryRebuildCommand() {
|
|
|
396
451
|
}
|
|
397
452
|
return;
|
|
398
453
|
}
|
|
399
|
-
const { isDaemonRunning, getDaemonPaths } = await import(
|
|
454
|
+
const { isDaemonRunning, getDaemonPaths } = await import(join2(mcpDir, "dist/daemon/process.js"));
|
|
400
455
|
if (!await isDaemonRunning()) {
|
|
401
456
|
log.info("No SQLite deps and daemon not running. Nothing to rebuild.");
|
|
402
457
|
log.dim("Install Tier 1 deps: cf memory init");
|
|
403
458
|
log.dim("Or start the daemon: cf memory start-daemon");
|
|
404
459
|
return;
|
|
405
460
|
}
|
|
406
|
-
const { DaemonClient } = await import(
|
|
461
|
+
const { DaemonClient } = await import(join2(mcpDir, "dist/lib/daemon-client.js"));
|
|
407
462
|
const paths = getDaemonPaths();
|
|
408
463
|
const client = new DaemonClient(paths.socketPath);
|
|
409
464
|
log.step("Rebuilding search index via daemon...");
|
|
@@ -420,7 +475,7 @@ function getDbPath(memoryDir) {
|
|
|
420
475
|
const stripped = resolved.replace(/\/docs\/memory$/, "").replace(/\/memory$/, "");
|
|
421
476
|
const id = stripped.replace(/\//g, "-");
|
|
422
477
|
const home = homedir();
|
|
423
|
-
const dbPath =
|
|
478
|
+
const dbPath = join2(
|
|
424
479
|
home,
|
|
425
480
|
".coding-friend",
|
|
426
481
|
"memory",
|
|
@@ -428,7 +483,7 @@ function getDbPath(memoryDir) {
|
|
|
428
483
|
id,
|
|
429
484
|
"db.sqlite"
|
|
430
485
|
);
|
|
431
|
-
return
|
|
486
|
+
return existsSync2(dbPath) ? dbPath : null;
|
|
432
487
|
} catch {
|
|
433
488
|
return null;
|
|
434
489
|
}
|
|
@@ -437,7 +492,7 @@ async function ensureSqliteDepsIfNeeded(mcpDir) {
|
|
|
437
492
|
const config = loadConfig();
|
|
438
493
|
const tier = config.memory?.tier ?? "auto";
|
|
439
494
|
if (tier === "markdown" || tier === "lite") return true;
|
|
440
|
-
const { ensureDeps, areSqliteDepsAvailable } = await import(
|
|
495
|
+
const { ensureDeps, areSqliteDepsAvailable } = await import(join2(mcpDir, "dist/lib/lazy-install.js"));
|
|
441
496
|
if (areSqliteDepsAvailable()) return true;
|
|
442
497
|
log.step("Installing SQLite dependencies...");
|
|
443
498
|
const installed = await ensureDeps({
|
|
@@ -466,36 +521,30 @@ function isMemoryInitialized() {
|
|
|
466
521
|
return getDbPath(memoryDir) !== null;
|
|
467
522
|
}
|
|
468
523
|
async function memoryInitWizard(memoryDir, mcpDir) {
|
|
469
|
-
log.step("Step 1/
|
|
524
|
+
log.step("Step 1/4: Search tier");
|
|
470
525
|
await editMemoryTier(
|
|
471
526
|
readJson(globalConfigPath()),
|
|
472
527
|
readJson(localConfigPath())
|
|
473
528
|
);
|
|
474
529
|
console.log();
|
|
475
|
-
log.step("Step 2/
|
|
530
|
+
log.step("Step 2/4: Embedding provider");
|
|
476
531
|
await editMemoryEmbedding(
|
|
477
532
|
readJson(globalConfigPath()),
|
|
478
533
|
readJson(localConfigPath())
|
|
479
534
|
);
|
|
480
535
|
console.log();
|
|
481
|
-
log.step("Step 3/
|
|
536
|
+
log.step("Step 3/4: Auto-capture");
|
|
482
537
|
await editMemoryAutoCapture(
|
|
483
538
|
readJson(globalConfigPath()),
|
|
484
539
|
readJson(localConfigPath())
|
|
485
540
|
);
|
|
486
541
|
console.log();
|
|
487
|
-
log.step("Step 4/
|
|
542
|
+
log.step("Step 4/4: Auto-start daemon");
|
|
488
543
|
await editMemoryAutoStart(
|
|
489
544
|
readJson(globalConfigPath()),
|
|
490
545
|
readJson(localConfigPath())
|
|
491
546
|
);
|
|
492
547
|
console.log();
|
|
493
|
-
log.step("Step 5/5: Daemon idle timeout");
|
|
494
|
-
await editMemoryDaemonTimeout(
|
|
495
|
-
readJson(globalConfigPath()),
|
|
496
|
-
readJson(localConfigPath())
|
|
497
|
-
);
|
|
498
|
-
console.log();
|
|
499
548
|
const config = loadConfig();
|
|
500
549
|
const tier = config.memory?.tier ?? "auto";
|
|
501
550
|
if (tier === "markdown") {
|
|
@@ -514,7 +563,7 @@ async function memoryInitWizard(memoryDir, mcpDir) {
|
|
|
514
563
|
}
|
|
515
564
|
const depsOk = await ensureSqliteDepsIfNeeded(mcpDir);
|
|
516
565
|
if (!depsOk) return;
|
|
517
|
-
if (!
|
|
566
|
+
if (!existsSync2(memoryDir)) {
|
|
518
567
|
log.info(
|
|
519
568
|
"No memory directory found. Memories will be indexed as they're created."
|
|
520
569
|
);
|
|
@@ -530,7 +579,7 @@ async function memoryInitWizard(memoryDir, mcpDir) {
|
|
|
530
579
|
return;
|
|
531
580
|
}
|
|
532
581
|
log.step(`Importing ${docCount} existing memories into SQLite...`);
|
|
533
|
-
const { SqliteBackend } = await import(
|
|
582
|
+
const { SqliteBackend } = await import(join2(mcpDir, "dist/backends/sqlite/index.js"));
|
|
534
583
|
const embedding = config.memory?.embedding;
|
|
535
584
|
const backend = new SqliteBackend(memoryDir, {
|
|
536
585
|
skipVec: false,
|
|
@@ -541,7 +590,7 @@ async function memoryInitWizard(memoryDir, mcpDir) {
|
|
|
541
590
|
const stats = await backend.stats();
|
|
542
591
|
log.success(`Imported ${stats.total} memories. DB: ${backend.getDbPath()}`);
|
|
543
592
|
log.info(
|
|
544
|
-
`Vector search: ${backend.isVecEnabled() ?
|
|
593
|
+
`Vector search: ${backend.isVecEnabled() ? chalk2.green("enabled") : chalk2.dim("disabled (sqlite-vec not available)")}`
|
|
545
594
|
);
|
|
546
595
|
} finally {
|
|
547
596
|
await backend.close();
|
|
@@ -550,13 +599,13 @@ async function memoryInitWizard(memoryDir, mcpDir) {
|
|
|
550
599
|
console.log();
|
|
551
600
|
log.success('Memory initialized! Run "cf memory status" to verify.');
|
|
552
601
|
log.info(
|
|
553
|
-
`Tip: Run ${
|
|
602
|
+
`Tip: Run ${chalk2.cyan("/cf-scan")} in Claude Code to populate memory with project knowledge.`
|
|
554
603
|
);
|
|
555
604
|
}
|
|
556
|
-
async function setupMemoryMcp(memoryDir,
|
|
605
|
+
async function setupMemoryMcp(memoryDir, _mcpDir) {
|
|
557
606
|
const mcpStatus = getMemoryMcpStatus();
|
|
558
607
|
if (mcpStatus.configured && mcpStatus.scope === "local") {
|
|
559
|
-
log.info(`MCP: ${
|
|
608
|
+
log.info(`MCP: ${chalk2.green("already configured")} in .mcp.json`);
|
|
560
609
|
return;
|
|
561
610
|
}
|
|
562
611
|
console.log();
|
|
@@ -572,14 +621,7 @@ async function setupMemoryMcp(memoryDir, mcpDir) {
|
|
|
572
621
|
log.dim('Skipped. Run "cf memory mcp" anytime to get the config.');
|
|
573
622
|
return;
|
|
574
623
|
}
|
|
575
|
-
|
|
576
|
-
if (!existsSync(serverPath)) {
|
|
577
|
-
log.warn(
|
|
578
|
-
"cf-memory not built yet. Run `cf memory mcp` after building to get the config."
|
|
579
|
-
);
|
|
580
|
-
return;
|
|
581
|
-
}
|
|
582
|
-
writeMemoryMcpEntry(serverPath, memoryDir);
|
|
624
|
+
writeMemoryMcpEntry(memoryDir);
|
|
583
625
|
}
|
|
584
626
|
async function memoryInitCommand() {
|
|
585
627
|
const memoryDir = getMemoryDir();
|
|
@@ -612,7 +654,7 @@ async function memoryConfigCommand() {
|
|
|
612
654
|
}
|
|
613
655
|
function getProjectsBaseDir() {
|
|
614
656
|
const home = homedir();
|
|
615
|
-
return
|
|
657
|
+
return join2(home, ".coding-friend", "memory", "projects");
|
|
616
658
|
}
|
|
617
659
|
function formatSize(bytes) {
|
|
618
660
|
if (bytes < 1024) return `${bytes} B`;
|
|
@@ -630,9 +672,9 @@ function formatDate(raw) {
|
|
|
630
672
|
}
|
|
631
673
|
function dirSize(dir) {
|
|
632
674
|
let total = 0;
|
|
633
|
-
if (!
|
|
675
|
+
if (!existsSync2(dir)) return 0;
|
|
634
676
|
for (const entry of readdirSync(dir, { withFileTypes: true })) {
|
|
635
|
-
const p =
|
|
677
|
+
const p = join2(dir, entry.name);
|
|
636
678
|
if (entry.isFile()) {
|
|
637
679
|
total += statSync(p).size;
|
|
638
680
|
} else if (entry.isDirectory()) {
|
|
@@ -642,7 +684,7 @@ function dirSize(dir) {
|
|
|
642
684
|
return total;
|
|
643
685
|
}
|
|
644
686
|
function getProjectInfo(projectDir, projectId, mcpDir, knownSourceDir) {
|
|
645
|
-
const dbPath =
|
|
687
|
+
const dbPath = join2(projectDir, "db.sqlite");
|
|
646
688
|
const size = dirSize(projectDir);
|
|
647
689
|
const info = {
|
|
648
690
|
id: projectId,
|
|
@@ -651,7 +693,7 @@ function getProjectInfo(projectDir, projectId, mcpDir, knownSourceDir) {
|
|
|
651
693
|
size,
|
|
652
694
|
lastUpdated: null
|
|
653
695
|
};
|
|
654
|
-
if (!
|
|
696
|
+
if (!existsSync2(dbPath)) return info;
|
|
655
697
|
try {
|
|
656
698
|
const backfillDir = knownSourceDir ? JSON.stringify(knownSourceDir) : "null";
|
|
657
699
|
const result = run(
|
|
@@ -692,7 +734,7 @@ async function memoryListProjectsCommand() {
|
|
|
692
734
|
const baseDir = getProjectsBaseDir();
|
|
693
735
|
const mcpDir = getLibPath("cf-memory");
|
|
694
736
|
ensureMemoryBuilt(mcpDir);
|
|
695
|
-
if (!
|
|
737
|
+
if (!existsSync2(baseDir)) {
|
|
696
738
|
log.info("No memory projects found.");
|
|
697
739
|
log.dim('Run "cf memory init" in a project to create one.');
|
|
698
740
|
return;
|
|
@@ -703,35 +745,35 @@ async function memoryListProjectsCommand() {
|
|
|
703
745
|
return;
|
|
704
746
|
}
|
|
705
747
|
const currentMemoryDir = resolve(getMemoryDir());
|
|
706
|
-
const { projectId } = await import(
|
|
748
|
+
const { projectId } = await import(join2(mcpDir, "dist/backends/sqlite/index.js"));
|
|
707
749
|
const currentProjectId = projectId(currentMemoryDir);
|
|
708
750
|
log.step(`Scanning ${dirs.length} project(s)...
|
|
709
751
|
`);
|
|
710
752
|
const projects = [];
|
|
711
753
|
for (const id of dirs) {
|
|
712
754
|
const knownDir = id === currentProjectId ? currentMemoryDir : void 0;
|
|
713
|
-
projects.push(getProjectInfo(
|
|
755
|
+
projects.push(getProjectInfo(join2(baseDir, id), id, mcpDir, knownDir));
|
|
714
756
|
}
|
|
715
757
|
projects.sort((a, b) => b.size - a.size);
|
|
716
758
|
const totalSize = projects.reduce((sum, p) => sum + p.size, 0);
|
|
717
759
|
const idxW = String(projects.length).length;
|
|
718
760
|
const header = `${"#".padStart(idxW)} ${"SIZE".padStart(10)} ${"MEMS".padStart(4)} ${"PROJECT ID".padEnd(12)} ${"UPDATED".padEnd(16)} PATH`;
|
|
719
|
-
console.log(
|
|
720
|
-
console.log(
|
|
761
|
+
console.log(chalk2.bold(header));
|
|
762
|
+
console.log(chalk2.dim("-".repeat(header.length + 10)));
|
|
721
763
|
projects.forEach((p, i) => {
|
|
722
|
-
const idx =
|
|
723
|
-
const sizeStr =
|
|
724
|
-
const memCount =
|
|
725
|
-
const idStr =
|
|
726
|
-
const dateStr = p.lastUpdated ? formatDate(p.lastUpdated).padEnd(16) :
|
|
727
|
-
const pathStr = p.sourceDir ?
|
|
764
|
+
const idx = chalk2.dim(String(i + 1).padStart(idxW));
|
|
765
|
+
const sizeStr = chalk2.yellow(formatSize(p.size).padStart(10));
|
|
766
|
+
const memCount = chalk2.green(String(p.memories).padStart(4));
|
|
767
|
+
const idStr = chalk2.cyan(p.id.padEnd(12));
|
|
768
|
+
const dateStr = p.lastUpdated ? formatDate(p.lastUpdated).padEnd(16) : chalk2.dim("n/a".padEnd(16));
|
|
769
|
+
const pathStr = p.sourceDir ? chalk2.dim(p.sourceDir) : chalk2.dim("(unknown)");
|
|
728
770
|
console.log(
|
|
729
771
|
`${idx} ${sizeStr} ${memCount} ${idStr} ${dateStr} ${pathStr}`
|
|
730
772
|
);
|
|
731
773
|
});
|
|
732
774
|
console.log();
|
|
733
775
|
console.log(
|
|
734
|
-
|
|
776
|
+
chalk2.bold(
|
|
735
777
|
`Total: ${projects.length} project(s), ${formatSize(totalSize)}`
|
|
736
778
|
)
|
|
737
779
|
);
|
|
@@ -739,7 +781,7 @@ async function memoryListProjectsCommand() {
|
|
|
739
781
|
}
|
|
740
782
|
async function memoryRmCommand(opts) {
|
|
741
783
|
const baseDir = getProjectsBaseDir();
|
|
742
|
-
if (!
|
|
784
|
+
if (!existsSync2(baseDir)) {
|
|
743
785
|
log.info("No memory projects found. Nothing to remove.");
|
|
744
786
|
return;
|
|
745
787
|
}
|
|
@@ -749,9 +791,9 @@ async function memoryRmCommand(opts) {
|
|
|
749
791
|
const dirs = readdirSync(baseDir, { withFileTypes: true }).filter((d) => d.isDirectory() && !d.name.startsWith(".")).map((d) => d.name);
|
|
750
792
|
const orphaned = [];
|
|
751
793
|
for (const id of dirs) {
|
|
752
|
-
const projectDir =
|
|
794
|
+
const projectDir = join2(baseDir, id);
|
|
753
795
|
const info = getProjectInfo(projectDir, id, mcpDir);
|
|
754
|
-
if (info.sourceDir && !
|
|
796
|
+
if (info.sourceDir && !existsSync2(info.sourceDir)) {
|
|
755
797
|
orphaned.push({
|
|
756
798
|
id,
|
|
757
799
|
reason: `source dir missing: ${info.sourceDir}`,
|
|
@@ -775,7 +817,7 @@ async function memoryRmCommand(opts) {
|
|
|
775
817
|
);
|
|
776
818
|
console.log();
|
|
777
819
|
for (const o of orphaned) {
|
|
778
|
-
console.log(` ${
|
|
820
|
+
console.log(` ${chalk2.cyan(o.id)} ${chalk2.dim(o.reason)}`);
|
|
779
821
|
}
|
|
780
822
|
console.log();
|
|
781
823
|
const ok = await confirm({
|
|
@@ -787,7 +829,7 @@ async function memoryRmCommand(opts) {
|
|
|
787
829
|
return;
|
|
788
830
|
}
|
|
789
831
|
for (const o of orphaned) {
|
|
790
|
-
rmSync(
|
|
832
|
+
rmSync(join2(baseDir, o.id), { recursive: true, force: true });
|
|
791
833
|
}
|
|
792
834
|
log.success(
|
|
793
835
|
`Deleted ${orphaned.length} orphaned project(s) (${formatSize(totalSize)}).`
|
|
@@ -803,7 +845,7 @@ async function memoryRmCommand(opts) {
|
|
|
803
845
|
return;
|
|
804
846
|
}
|
|
805
847
|
const totalSize = dirs.reduce(
|
|
806
|
-
(sum, d) => sum + dirSize(
|
|
848
|
+
(sum, d) => sum + dirSize(join2(baseDir, d.name)),
|
|
807
849
|
0
|
|
808
850
|
);
|
|
809
851
|
log.warn(
|
|
@@ -820,19 +862,19 @@ async function memoryRmCommand(opts) {
|
|
|
820
862
|
return;
|
|
821
863
|
}
|
|
822
864
|
for (const d of dirs) {
|
|
823
|
-
rmSync(
|
|
865
|
+
rmSync(join2(baseDir, d.name), { recursive: true, force: true });
|
|
824
866
|
}
|
|
825
867
|
log.success(`Deleted ${dirs.length} project database(s).`);
|
|
826
868
|
return;
|
|
827
869
|
}
|
|
828
870
|
if (opts.projectId) {
|
|
829
|
-
const projectDir =
|
|
871
|
+
const projectDir = join2(baseDir, opts.projectId);
|
|
830
872
|
const resolved = resolve(projectDir);
|
|
831
873
|
if (!resolved.startsWith(resolve(baseDir) + sep)) {
|
|
832
874
|
log.error("Invalid project ID.");
|
|
833
875
|
process.exit(1);
|
|
834
876
|
}
|
|
835
|
-
if (!
|
|
877
|
+
if (!existsSync2(projectDir)) {
|
|
836
878
|
log.error(`Project "${opts.projectId}" not found.`);
|
|
837
879
|
log.dim('Run "cf memory list --projects" to see available projects.');
|
|
838
880
|
process.exit(1);
|
|
@@ -863,11 +905,14 @@ async function memoryMcpCommand() {
|
|
|
863
905
|
const memoryDir = getMemoryDir();
|
|
864
906
|
const mcpDir = getLibPath("cf-memory");
|
|
865
907
|
ensureMemoryBuilt(mcpDir);
|
|
866
|
-
|
|
908
|
+
warnStaleMcpJson(memoryDir);
|
|
909
|
+
const serverPath = join2(mcpDir, "dist", "index.js");
|
|
867
910
|
printMemoryMcpConfig(serverPath, memoryDir);
|
|
868
911
|
}
|
|
869
912
|
|
|
870
913
|
export {
|
|
914
|
+
warnStaleMcpJson,
|
|
915
|
+
detectMemoryMcpState,
|
|
871
916
|
ensureMemoryBuilt,
|
|
872
917
|
printMemoryMcpConfig,
|
|
873
918
|
memoryStatusCommand,
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
+
import {
|
|
2
|
+
resolveMemoryDir
|
|
3
|
+
} from "./chunk-GTX6I57V.js";
|
|
1
4
|
import {
|
|
2
5
|
getLibPath
|
|
3
6
|
} from "./chunk-RZRT7NGT.js";
|
|
4
|
-
import {
|
|
5
|
-
resolveMemoryDir
|
|
6
|
-
} from "./chunk-65S7Q5PW.js";
|
|
7
7
|
import {
|
|
8
8
|
BACK,
|
|
9
9
|
askScope,
|
|
@@ -24,7 +24,6 @@ import {
|
|
|
24
24
|
} from "./chunk-5UVDWG5L.js";
|
|
25
25
|
|
|
26
26
|
// src/lib/memory-prompts.ts
|
|
27
|
-
import { existsSync } from "fs";
|
|
28
27
|
import { homedir } from "os";
|
|
29
28
|
import { confirm, input, select } from "@inquirer/prompts";
|
|
30
29
|
import chalk from "chalk";
|
|
@@ -219,31 +218,7 @@ async function editMemoryEmbedding(globalCfg, localCfg) {
|
|
|
219
218
|
if (ollamaUrl) embedding.ollamaUrl = ollamaUrl;
|
|
220
219
|
writeMemoryField(scope, "embedding", embedding);
|
|
221
220
|
}
|
|
222
|
-
|
|
223
|
-
const currentDaemon = getMergedMemoryValue("daemon", globalCfg, localCfg);
|
|
224
|
-
const currentMs = currentDaemon?.idleTimeout;
|
|
225
|
-
const currentMin = currentMs !== void 0 ? currentMs / 6e4 : void 0;
|
|
226
|
-
if (currentMin !== void 0) {
|
|
227
|
-
log.dim(`Current: ${currentMin} minutes`);
|
|
228
|
-
}
|
|
229
|
-
const value = await input({
|
|
230
|
-
message: "Daemon idle timeout (minutes, 0 = no timeout):",
|
|
231
|
-
default: String(currentMin ?? 0),
|
|
232
|
-
validate: (val) => {
|
|
233
|
-
const n = Number(val);
|
|
234
|
-
if (isNaN(n) || n < 0)
|
|
235
|
-
return "Must be 0 (no timeout) or a positive number";
|
|
236
|
-
return true;
|
|
237
|
-
}
|
|
238
|
-
});
|
|
239
|
-
const scope = await askScope();
|
|
240
|
-
if (scope === "back") return;
|
|
241
|
-
writeMemoryField(scope, "daemon", {
|
|
242
|
-
...currentDaemon,
|
|
243
|
-
idleTimeout: Number(value) * 6e4
|
|
244
|
-
});
|
|
245
|
-
}
|
|
246
|
-
function writeMemoryMcpEntry(serverPath, memoryDir) {
|
|
221
|
+
function writeMemoryMcpEntry(memoryDir) {
|
|
247
222
|
const mcpPath = join(process.cwd(), ".mcp.json");
|
|
248
223
|
const existing = readJson(mcpPath) ?? {};
|
|
249
224
|
const servers = existing.mcpServers ?? {};
|
|
@@ -252,8 +227,8 @@ function writeMemoryMcpEntry(serverPath, memoryDir) {
|
|
|
252
227
|
mcpServers: {
|
|
253
228
|
...servers,
|
|
254
229
|
"coding-friend-memory": {
|
|
255
|
-
command: "
|
|
256
|
-
args: [
|
|
230
|
+
command: "npx",
|
|
231
|
+
args: ["-y", "coding-friend-cli", "mcp-serve", memoryDir]
|
|
257
232
|
}
|
|
258
233
|
}
|
|
259
234
|
});
|
|
@@ -285,24 +260,8 @@ async function editMemoryMcp() {
|
|
|
285
260
|
});
|
|
286
261
|
if (!reconfigure) return;
|
|
287
262
|
}
|
|
288
|
-
let mcpDir;
|
|
289
|
-
try {
|
|
290
|
-
mcpDir = getLibPath("cf-memory");
|
|
291
|
-
} catch {
|
|
292
|
-
log.warn(
|
|
293
|
-
"cf-memory package not found. Install the CLI first: npm i -g coding-friend-cli"
|
|
294
|
-
);
|
|
295
|
-
return;
|
|
296
|
-
}
|
|
297
|
-
const serverPath = join(mcpDir, "dist", "index.js");
|
|
298
|
-
if (!existsSync(serverPath)) {
|
|
299
|
-
log.warn(
|
|
300
|
-
'cf-memory not built yet. Run "cf memory mcp" after building to get the config.'
|
|
301
|
-
);
|
|
302
|
-
return;
|
|
303
|
-
}
|
|
304
263
|
const memoryDir = resolveMemoryDir();
|
|
305
|
-
writeMemoryMcpEntry(
|
|
264
|
+
writeMemoryMcpEntry(memoryDir);
|
|
306
265
|
}
|
|
307
266
|
async function memoryConfigMenu(opts) {
|
|
308
267
|
while (true) {
|
|
@@ -340,8 +299,6 @@ async function memoryConfigMenu(opts) {
|
|
|
340
299
|
globalCfg,
|
|
341
300
|
localCfg
|
|
342
301
|
);
|
|
343
|
-
const daemonScope = getMemoryFieldScope("daemon", globalCfg, localCfg);
|
|
344
|
-
const daemonVal = getMergedMemoryValue("daemon", globalCfg, localCfg);
|
|
345
302
|
const embeddingLabel = embeddingVal?.provider ? embeddingVal.model ? `${embeddingVal.model} (${embeddingVal.provider})` : embeddingVal.provider : "";
|
|
346
303
|
const mcpStatus = getMemoryMcpStatus();
|
|
347
304
|
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");
|
|
@@ -365,10 +322,6 @@ async function memoryConfigMenu(opts) {
|
|
|
365
322
|
name: `Embedding ${formatScopeLabel(embeddingScope)}${embeddingLabel ? ` (${embeddingLabel})` : ""}`,
|
|
366
323
|
value: "embedding"
|
|
367
324
|
},
|
|
368
|
-
{
|
|
369
|
-
name: `Daemon timeout ${formatScopeLabel(daemonScope)}${daemonVal?.idleTimeout ? ` (${daemonVal.idleTimeout / 6e4}min)` : ""}`,
|
|
370
|
-
value: "daemon"
|
|
371
|
-
},
|
|
372
325
|
{
|
|
373
326
|
name: `MCP setup (${mcpLabel})`,
|
|
374
327
|
value: "mcp"
|
|
@@ -391,9 +344,6 @@ async function memoryConfigMenu(opts) {
|
|
|
391
344
|
case "embedding":
|
|
392
345
|
await editMemoryEmbedding(globalCfg, localCfg);
|
|
393
346
|
break;
|
|
394
|
-
case "daemon":
|
|
395
|
-
await editMemoryDaemonTimeout(globalCfg, localCfg);
|
|
396
|
-
break;
|
|
397
347
|
case "mcp":
|
|
398
348
|
await editMemoryMcp();
|
|
399
349
|
break;
|
|
@@ -406,7 +356,6 @@ export {
|
|
|
406
356
|
editMemoryAutoCapture,
|
|
407
357
|
editMemoryAutoStart,
|
|
408
358
|
editMemoryEmbedding,
|
|
409
|
-
editMemoryDaemonTimeout,
|
|
410
359
|
writeMemoryMcpEntry,
|
|
411
360
|
getMemoryMcpStatus,
|
|
412
361
|
memoryConfigMenu
|
|
@@ -58,9 +58,6 @@ var StatuslineConfigSchema = z.object({
|
|
|
58
58
|
});
|
|
59
59
|
var MemoryConfigSchema = z.object({
|
|
60
60
|
tier: z.enum(["auto", "full", "lite", "markdown"]).optional(),
|
|
61
|
-
daemon: z.object({
|
|
62
|
-
idleTimeout: z.number().optional()
|
|
63
|
-
}).optional(),
|
|
64
61
|
embedding: z.object({
|
|
65
62
|
provider: z.enum(["transformers", "ollama"]).optional(),
|
|
66
63
|
model: z.string().optional(),
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import {
|
|
2
2
|
memoryConfigMenu
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-5W7ZOVDU.js";
|
|
4
|
+
import "./chunk-GTX6I57V.js";
|
|
4
5
|
import "./chunk-RZRT7NGT.js";
|
|
5
|
-
import "./chunk-65S7Q5PW.js";
|
|
6
6
|
import {
|
|
7
7
|
findStatuslineHookPath,
|
|
8
8
|
getCurrentAccountEmail,
|
|
@@ -659,7 +659,7 @@ async function configCommand() {
|
|
|
659
659
|
{
|
|
660
660
|
name: `Memory settings ${formatScopeLabel(memoryScope)}`,
|
|
661
661
|
value: "memory",
|
|
662
|
-
description: " Tier, auto-capture, auto-start, embedding provider
|
|
662
|
+
description: " Tier, auto-capture, auto-start, embedding provider"
|
|
663
663
|
},
|
|
664
664
|
{
|
|
665
665
|
name: `Auto-approve ${formatScopeLabel(autoApproveScope)}${autoApproveVal !== void 0 ? ` (${autoApproveVal})` : ""}`,
|
package/dist/index.js
CHANGED
|
@@ -30,21 +30,25 @@ 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-
|
|
33
|
+
const { initCommand } = await import("./init-3UITY4LD.js");
|
|
34
34
|
await initCommand();
|
|
35
35
|
});
|
|
36
36
|
program.command("config").description("Manage Coding Friend configuration").action(async () => {
|
|
37
|
-
const { configCommand } = await import("./config-
|
|
37
|
+
const { configCommand } = await import("./config-FUEEFM5N.js");
|
|
38
38
|
await configCommand();
|
|
39
39
|
});
|
|
40
40
|
program.command("host").description("Build and serve learning docs as a static website").argument("[path]", "path to docs folder").option("-p, --port <port>", "port number", "3333").action(async (path, opts) => {
|
|
41
|
-
const { hostCommand } = await import("./host-
|
|
41
|
+
const { hostCommand } = await import("./host-RNYPAE4D.js");
|
|
42
42
|
await hostCommand(path, opts);
|
|
43
43
|
});
|
|
44
44
|
program.command("mcp").description("Setup MCP server for learning docs").argument("[path]", "path to docs folder").action(async (path) => {
|
|
45
|
-
const { mcpCommand } = await import("./mcp-
|
|
45
|
+
const { mcpCommand } = await import("./mcp-LJ5NX6XE.js");
|
|
46
46
|
await mcpCommand(path);
|
|
47
47
|
});
|
|
48
|
+
program.command("mcp-serve").description("Start the cf-memory MCP server (used internally by npx)").argument("<memoryDir>", "path to memory directory").action(async (memoryDir) => {
|
|
49
|
+
const { mcpServeCommand } = await import("./mcp-serve-3FEPFVVQ.js");
|
|
50
|
+
await mcpServeCommand(memoryDir);
|
|
51
|
+
});
|
|
48
52
|
program.command("permission").description("Manage Claude Code permission rules for Coding Friend").option("--all", "Apply all recommended permissions without prompts").option("--user", "Save to user-level settings (~/.claude/settings.json)").option(
|
|
49
53
|
"--project",
|
|
50
54
|
"Save to project-level settings (.claude/settings.local.json)"
|
|
@@ -61,7 +65,7 @@ program.command("update").description("Update coding-friend plugin, CLI, and sta
|
|
|
61
65
|
await updateCommand(opts);
|
|
62
66
|
});
|
|
63
67
|
program.command("status").description("Show comprehensive Coding Friend status").action(async () => {
|
|
64
|
-
const { statusCommand } = await import("./status-
|
|
68
|
+
const { statusCommand } = await import("./status-FU5GR3MP.js");
|
|
65
69
|
await statusCommand();
|
|
66
70
|
});
|
|
67
71
|
var session = program.command("session").description("Save and load Claude Code sessions across machines");
|
|
@@ -76,11 +80,11 @@ session.command("save").description("Save current Claude Code session to sync fo
|
|
|
76
80
|
"-s, --session-id <id>",
|
|
77
81
|
"session UUID to save (default: auto-detect newest)"
|
|
78
82
|
).option("-l, --label <label>", "label for this session").action(async (opts) => {
|
|
79
|
-
const { sessionSaveCommand } = await import("./session-
|
|
83
|
+
const { sessionSaveCommand } = await import("./session-FMC2MG6C.js");
|
|
80
84
|
await sessionSaveCommand(opts);
|
|
81
85
|
});
|
|
82
86
|
session.command("load").description("Load a saved session from sync folder").action(async () => {
|
|
83
|
-
const { sessionLoadCommand } = await import("./session-
|
|
87
|
+
const { sessionLoadCommand } = await import("./session-FMC2MG6C.js");
|
|
84
88
|
await sessionLoadCommand();
|
|
85
89
|
});
|
|
86
90
|
var memory = program.command("memory").description("AI memory system \u2014 store and search project knowledge");
|
|
@@ -100,43 +104,43 @@ Memory subcommands:
|
|
|
100
104
|
memory mcp Show MCP server setup instructions`
|
|
101
105
|
);
|
|
102
106
|
memory.command("status").description("Show memory system status").action(async () => {
|
|
103
|
-
const { memoryStatusCommand } = await import("./memory-
|
|
107
|
+
const { memoryStatusCommand } = await import("./memory-2ZD4WU3S.js");
|
|
104
108
|
await memoryStatusCommand();
|
|
105
109
|
});
|
|
106
110
|
memory.command("search").description("Search memories by query").argument("<query>", "search query").action(async (query) => {
|
|
107
|
-
const { memorySearchCommand } = await import("./memory-
|
|
111
|
+
const { memorySearchCommand } = await import("./memory-2ZD4WU3S.js");
|
|
108
112
|
await memorySearchCommand(query);
|
|
109
113
|
});
|
|
110
114
|
memory.command("list").description(
|
|
111
115
|
"List memories in current project, or all projects with --projects"
|
|
112
116
|
).option("--projects", "List all project databases with size and metadata").action(async (opts) => {
|
|
113
|
-
const { memoryListCommand } = await import("./memory-
|
|
117
|
+
const { memoryListCommand } = await import("./memory-2ZD4WU3S.js");
|
|
114
118
|
await memoryListCommand(opts);
|
|
115
119
|
});
|
|
116
120
|
memory.command("init").description(
|
|
117
121
|
"Initialize memory system \u2014 interactive wizard (first time) or config menu"
|
|
118
122
|
).action(async () => {
|
|
119
|
-
const { memoryInitCommand } = await import("./memory-
|
|
123
|
+
const { memoryInitCommand } = await import("./memory-2ZD4WU3S.js");
|
|
120
124
|
await memoryInitCommand();
|
|
121
125
|
});
|
|
122
126
|
memory.command("config").description("Configure memory system settings").action(async () => {
|
|
123
|
-
const { memoryConfigCommand } = await import("./memory-
|
|
127
|
+
const { memoryConfigCommand } = await import("./memory-2ZD4WU3S.js");
|
|
124
128
|
await memoryConfigCommand();
|
|
125
129
|
});
|
|
126
130
|
memory.command("start-daemon").description("Start the memory daemon (Tier 2 \u2014 MiniSearch)").action(async () => {
|
|
127
|
-
const { memoryStartDaemonCommand } = await import("./memory-
|
|
131
|
+
const { memoryStartDaemonCommand } = await import("./memory-2ZD4WU3S.js");
|
|
128
132
|
await memoryStartDaemonCommand();
|
|
129
133
|
});
|
|
130
134
|
memory.command("stop-daemon").description("Stop the memory daemon").action(async () => {
|
|
131
|
-
const { memoryStopDaemonCommand } = await import("./memory-
|
|
135
|
+
const { memoryStopDaemonCommand } = await import("./memory-2ZD4WU3S.js");
|
|
132
136
|
await memoryStopDaemonCommand();
|
|
133
137
|
});
|
|
134
138
|
memory.command("rebuild").description("Rebuild the daemon search index").action(async () => {
|
|
135
|
-
const { memoryRebuildCommand } = await import("./memory-
|
|
139
|
+
const { memoryRebuildCommand } = await import("./memory-2ZD4WU3S.js");
|
|
136
140
|
await memoryRebuildCommand();
|
|
137
141
|
});
|
|
138
142
|
memory.command("mcp").description("Show MCP server setup instructions").action(async () => {
|
|
139
|
-
const { memoryMcpCommand } = await import("./memory-
|
|
143
|
+
const { memoryMcpCommand } = await import("./memory-2ZD4WU3S.js");
|
|
140
144
|
await memoryMcpCommand();
|
|
141
145
|
});
|
|
142
146
|
memory.command("rm").description("Remove a project database").option("--project-id <id>", "Project ID to remove").option("--all", "Remove all project databases").option(
|
|
@@ -144,7 +148,7 @@ memory.command("rm").description("Remove a project database").option("--project-
|
|
|
144
148
|
"Remove orphaned projects (source dir missing or 0 memories)"
|
|
145
149
|
).action(
|
|
146
150
|
async (opts) => {
|
|
147
|
-
const { memoryRmCommand } = await import("./memory-
|
|
151
|
+
const { memoryRmCommand } = await import("./memory-2ZD4WU3S.js");
|
|
148
152
|
await memoryRmCommand(opts);
|
|
149
153
|
}
|
|
150
154
|
);
|
|
@@ -2,14 +2,14 @@ import {
|
|
|
2
2
|
ensureMemoryBuilt,
|
|
3
3
|
isMemoryInitialized,
|
|
4
4
|
memoryInitWizard
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-355AR2X5.js";
|
|
6
6
|
import {
|
|
7
7
|
memoryConfigMenu
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-5W7ZOVDU.js";
|
|
9
|
+
import "./chunk-GTX6I57V.js";
|
|
9
10
|
import {
|
|
10
11
|
getLibPath
|
|
11
12
|
} from "./chunk-RZRT7NGT.js";
|
|
12
|
-
import "./chunk-65S7Q5PW.js";
|
|
13
13
|
import {
|
|
14
14
|
findStatuslineHookPath,
|
|
15
15
|
getCurrentAccountEmail,
|
|
@@ -1,15 +1,17 @@
|
|
|
1
1
|
import {
|
|
2
|
+
detectMemoryMcpState,
|
|
2
3
|
ensureMemoryBuilt,
|
|
3
|
-
printMemoryMcpConfig
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
import
|
|
7
|
-
getLibPath
|
|
8
|
-
} from "./chunk-RZRT7NGT.js";
|
|
4
|
+
printMemoryMcpConfig,
|
|
5
|
+
warnStaleMcpJson
|
|
6
|
+
} from "./chunk-355AR2X5.js";
|
|
7
|
+
import "./chunk-5W7ZOVDU.js";
|
|
9
8
|
import {
|
|
10
9
|
resolveDocsDir,
|
|
11
10
|
resolveMemoryDir
|
|
12
|
-
} from "./chunk-
|
|
11
|
+
} from "./chunk-GTX6I57V.js";
|
|
12
|
+
import {
|
|
13
|
+
getLibPath
|
|
14
|
+
} from "./chunk-RZRT7NGT.js";
|
|
13
15
|
import "./chunk-DHH6SRXV.js";
|
|
14
16
|
import "./chunk-HPNRQYLM.js";
|
|
15
17
|
import {
|
|
@@ -38,6 +40,7 @@ function countMdFiles(dir) {
|
|
|
38
40
|
return count;
|
|
39
41
|
}
|
|
40
42
|
async function mcpCommand(path) {
|
|
43
|
+
warnStaleMcpJson(resolveMemoryDir());
|
|
41
44
|
const docsDir = resolveDocsDir(path);
|
|
42
45
|
const mcpDir = getLibPath("learn-mcp");
|
|
43
46
|
if (!existsSync(docsDir)) {
|
|
@@ -152,5 +155,6 @@ function printMemoryMcp() {
|
|
|
152
155
|
printMemoryMcpConfig(serverPath, memoryDir);
|
|
153
156
|
}
|
|
154
157
|
export {
|
|
158
|
+
detectMemoryMcpState,
|
|
155
159
|
mcpCommand
|
|
156
160
|
};
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import {
|
|
2
|
+
getLibPath
|
|
3
|
+
} from "./chunk-RZRT7NGT.js";
|
|
4
|
+
import {
|
|
5
|
+
log
|
|
6
|
+
} from "./chunk-NREZK463.js";
|
|
7
|
+
|
|
8
|
+
// src/commands/mcp-serve.ts
|
|
9
|
+
import { spawn } from "child_process";
|
|
10
|
+
import { join } from "path";
|
|
11
|
+
async function mcpServeCommand(memoryDir) {
|
|
12
|
+
const mcpDir = getLibPath("cf-memory");
|
|
13
|
+
const serverPath = join(mcpDir, "dist", "index.js");
|
|
14
|
+
const child = spawn("node", [serverPath, memoryDir], {
|
|
15
|
+
stdio: "inherit"
|
|
16
|
+
});
|
|
17
|
+
child.on("error", (err) => {
|
|
18
|
+
log.error(`Failed to start memory MCP server: ${err.message}`);
|
|
19
|
+
process.exit(1);
|
|
20
|
+
});
|
|
21
|
+
child.on("exit", (code) => {
|
|
22
|
+
process.exit(code ?? 0);
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
export {
|
|
26
|
+
mcpServeCommand
|
|
27
|
+
};
|
|
@@ -13,10 +13,10 @@ import {
|
|
|
13
13
|
memoryStatusCommand,
|
|
14
14
|
memoryStopDaemonCommand,
|
|
15
15
|
printMemoryMcpConfig
|
|
16
|
-
} from "./chunk-
|
|
17
|
-
import "./chunk-
|
|
16
|
+
} from "./chunk-355AR2X5.js";
|
|
17
|
+
import "./chunk-5W7ZOVDU.js";
|
|
18
|
+
import "./chunk-GTX6I57V.js";
|
|
18
19
|
import "./chunk-RZRT7NGT.js";
|
|
19
|
-
import "./chunk-65S7Q5PW.js";
|
|
20
20
|
import "./chunk-DHH6SRXV.js";
|
|
21
21
|
import "./chunk-HPNRQYLM.js";
|
|
22
22
|
import "./chunk-EVGXUDX4.js";
|