coding-friend-cli 1.34.0 → 1.35.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.
- package/README.md +1 -0
- package/dist/chunk-2DTXVMPQ.js +26 -0
- package/dist/{chunk-OYPOADCX.js → chunk-57EGAXYI.js} +3 -3
- package/dist/{chunk-K6EDQGDN.js → chunk-B2NFXRFP.js} +17 -12
- package/dist/{chunk-3UIH5U3Y.js → chunk-C5YWESSF.js} +2 -6
- package/dist/chunk-E5J24GD5.js +190 -0
- package/dist/{chunk-UGTNIFBH.js → chunk-H5TXJZD7.js} +6 -6
- package/dist/{chunk-5X3JZKYW.js → chunk-L53RNEV3.js} +116 -73
- package/dist/{chunk-DMJCO6LJ.js → chunk-RLUNNJWJ.js} +2 -1
- package/dist/{chunk-XOHLBLN2.js → chunk-WNTMKIKC.js} +1 -1
- package/dist/chunk-WSUYLIYX.js +61 -0
- package/dist/clean-67ACB4OS.js +95 -0
- package/dist/{chunk-CXG6I6ZF.js → config-HTQAALMA.js} +182 -194
- package/dist/{dev-S6VZUD33.js → dev-PFUF67SH.js} +7 -7
- package/dist/{disable-ZWQVU3QZ.js → disable-CDOM7SSI.js} +2 -2
- package/dist/{enable-NXP6APNI.js → enable-SF7742UR.js} +2 -2
- package/dist/{host-Y3WIXDSS.js → host-HKA4OHBM.js} +3 -3
- package/dist/index.js +38 -34
- package/dist/{init-WDFYY5MM.js → init-YM7YEEGK.js} +154 -99
- package/dist/{install-FKL6WLIG.js → install-VYZSTXN6.js} +8 -8
- package/dist/{mcp-J4SD646S.js → mcp-7MO2MCLE.js} +72 -67
- package/dist/{mcp-serve-learn-UNW6VH5M.js → mcp-serve-learn-OKVC5WV3.js} +19 -3
- package/dist/{memory-RLZR7XQY.js → memory-XALEMWQF.js} +7 -7
- package/dist/{permission-DNW7DE24.js → permission-C5R3LMZQ.js} +2 -2
- package/dist/postinstall.js +1 -1
- package/dist/{session-6WQGKIRG.js → session-M4KRQ4VU.js} +2 -2
- package/dist/{status-BX2SVBBW.js → status-2XM5OZDR.js} +19 -14
- package/dist/{statusline-MLLKBHHR.js → statusline-VFIA7OS2.js} +3 -3
- package/dist/{uninstall-6ZJRS2H4.js → uninstall-QKEC3O64.js} +6 -6
- package/dist/{update-G5HZINBK.js → update-IV4Y3T72.js} +6 -6
- package/package.json +1 -1
- package/dist/chunk-AYIREX73.js +0 -85
- package/dist/chunk-TVAY4OQL.js +0 -118
- package/dist/config-MCEQOLE5.js +0 -22
- package/dist/{chunk-Z5Q533UG.js → chunk-HPNRQYLM.js} +3 -3
- package/dist/{update-check-5SCD2JR2.js → update-check-BPCQ25AR.js} +3 -3
package/README.md
CHANGED
|
@@ -101,6 +101,7 @@ cf memory config # Configure memory system settings
|
|
|
101
101
|
cf memory mcp # Show MCP server config for clients
|
|
102
102
|
cf status # Show comprehensive Coding Friend status
|
|
103
103
|
# (versions, plugin, memory, config)
|
|
104
|
+
cf clean # Clean files generated by Coding Friend in docs/
|
|
104
105
|
cf session save # Save current Claude Code session to docs/sessions/
|
|
105
106
|
cf session load # Load a saved session from docs/sessions/
|
|
106
107
|
cf help # Show all commands
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
// src/lib/fs-utils.ts
|
|
2
|
+
import { readdirSync } from "fs";
|
|
3
|
+
import { join } from "path";
|
|
4
|
+
function listMdFilesRecursive(dir, maxDepth = 15) {
|
|
5
|
+
try {
|
|
6
|
+
const results = [];
|
|
7
|
+
for (const entry of readdirSync(dir, { withFileTypes: true })) {
|
|
8
|
+
if (entry.isDirectory()) {
|
|
9
|
+
if (maxDepth > 0) {
|
|
10
|
+
results.push(
|
|
11
|
+
...listMdFilesRecursive(join(dir, entry.name), maxDepth - 1)
|
|
12
|
+
);
|
|
13
|
+
}
|
|
14
|
+
} else if (entry.name.endsWith(".md") && entry.name !== "README.md") {
|
|
15
|
+
results.push(entry.name);
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
return results;
|
|
19
|
+
} catch {
|
|
20
|
+
return [];
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export {
|
|
25
|
+
listMdFilesRecursive
|
|
26
|
+
};
|
|
@@ -22,10 +22,9 @@ var ALL_COMPONENT_IDS = STATUSLINE_COMPONENTS.map((c) => c.id);
|
|
|
22
22
|
var DEFAULT_CONFIG = {
|
|
23
23
|
language: "en",
|
|
24
24
|
docsDir: "docs",
|
|
25
|
-
codex: { enabled: false, modes: ["STANDARD", "DEEP"], effort: "medium" },
|
|
26
25
|
learn: {
|
|
27
26
|
language: "en",
|
|
28
|
-
outputDir: "
|
|
27
|
+
outputDir: "~/.coding-friend/learn",
|
|
29
28
|
categories: [
|
|
30
29
|
{
|
|
31
30
|
name: "concepts",
|
|
@@ -43,7 +42,8 @@ var DEFAULT_CONFIG = {
|
|
|
43
42
|
}
|
|
44
43
|
],
|
|
45
44
|
autoCommit: false,
|
|
46
|
-
readmeIndex: false
|
|
45
|
+
readmeIndex: false,
|
|
46
|
+
disabled: false
|
|
47
47
|
}
|
|
48
48
|
};
|
|
49
49
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
DEFAULT_CONFIG
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-57EGAXYI.js";
|
|
4
4
|
import {
|
|
5
5
|
globalConfigPath,
|
|
6
6
|
localConfigPath,
|
|
@@ -40,7 +40,8 @@ var LearnConfigSchema = z.object({
|
|
|
40
40
|
outputDir: z.string().optional(),
|
|
41
41
|
categories: z.array(LearnCategorySchema).optional(),
|
|
42
42
|
autoCommit: z.boolean().optional(),
|
|
43
|
-
readmeIndex: z.union([z.boolean(), z.literal("per-category")]).optional()
|
|
43
|
+
readmeIndex: z.union([z.boolean(), z.literal("per-category")]).optional(),
|
|
44
|
+
disabled: z.boolean().optional()
|
|
44
45
|
});
|
|
45
46
|
var StatuslineComponentSchema = z.enum([
|
|
46
47
|
"version",
|
|
@@ -66,32 +67,27 @@ var MemoryConfigSchema = z.object({
|
|
|
66
67
|
autoCapture: z.boolean().optional(),
|
|
67
68
|
autoStart: z.boolean().optional()
|
|
68
69
|
});
|
|
69
|
-
var CodexConfigSchema = z.strictObject({
|
|
70
|
-
enabled: z.boolean().optional(),
|
|
71
|
-
modes: z.array(z.enum(["QUICK", "STANDARD", "DEEP"])).optional(),
|
|
72
|
-
effort: z.enum(["minimal", "low", "medium", "high", "xhigh"]).optional()
|
|
73
|
-
});
|
|
74
70
|
var ConfigSchema = z.strictObject({
|
|
75
71
|
language: z.string().optional(),
|
|
76
72
|
docsDir: z.string().optional(),
|
|
73
|
+
tdd: z.boolean().optional(),
|
|
77
74
|
learn: LearnConfigSchema.optional(),
|
|
78
75
|
statusline: StatuslineConfigSchema.optional(),
|
|
79
76
|
memory: MemoryConfigSchema.optional(),
|
|
80
77
|
autoApprove: z.boolean().optional(),
|
|
81
78
|
autoApproveIgnore: z.array(z.string()).optional(),
|
|
82
|
-
autoApproveAllowExtra: z.array(z.string()).optional()
|
|
83
|
-
codex: CodexConfigSchema.optional()
|
|
79
|
+
autoApproveAllowExtra: z.array(z.string()).optional()
|
|
84
80
|
});
|
|
85
81
|
var KNOWN_KEYS = [
|
|
86
82
|
"language",
|
|
87
83
|
"docsDir",
|
|
84
|
+
"tdd",
|
|
88
85
|
"learn",
|
|
89
86
|
"statusline",
|
|
90
87
|
"memory",
|
|
91
88
|
"autoApprove",
|
|
92
89
|
"autoApproveIgnore",
|
|
93
|
-
"autoApproveAllowExtra"
|
|
94
|
-
"codex"
|
|
90
|
+
"autoApproveAllowExtra"
|
|
95
91
|
];
|
|
96
92
|
function suggestKey(unknown) {
|
|
97
93
|
let best = null;
|
|
@@ -173,7 +169,9 @@ function sanitizeRawConfig(raw) {
|
|
|
173
169
|
}
|
|
174
170
|
}
|
|
175
171
|
const keyList = unknownKeys.length > 0 ? `: ${unknownKeys.join(", ")}` : "";
|
|
176
|
-
log.warn(
|
|
172
|
+
log.warn(
|
|
173
|
+
`sanitizeRawConfig: unrecognized config keys${keyList} \u2014 returning raw config`
|
|
174
|
+
);
|
|
177
175
|
return raw;
|
|
178
176
|
}
|
|
179
177
|
function loadConfig() {
|
|
@@ -200,6 +198,12 @@ function resolveDocsDir(explicitPath) {
|
|
|
200
198
|
}
|
|
201
199
|
return resolvePath("docs/learn");
|
|
202
200
|
}
|
|
201
|
+
function resolveLearnDir(globalCfg) {
|
|
202
|
+
if (globalCfg?.learn?.outputDir) {
|
|
203
|
+
return resolvePath(globalCfg.learn.outputDir);
|
|
204
|
+
}
|
|
205
|
+
return resolvePath("~/.coding-friend/learn");
|
|
206
|
+
}
|
|
203
207
|
function resolveMemoryDir(explicitPath) {
|
|
204
208
|
if (explicitPath) {
|
|
205
209
|
return resolvePath(explicitPath);
|
|
@@ -215,5 +219,6 @@ export {
|
|
|
215
219
|
sanitizeRawConfig,
|
|
216
220
|
loadConfig,
|
|
217
221
|
resolveDocsDir,
|
|
222
|
+
resolveLearnDir,
|
|
218
223
|
resolveMemoryDir
|
|
219
224
|
};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
resolveMemoryDir
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-B2NFXRFP.js";
|
|
4
4
|
import {
|
|
5
5
|
getLibPath
|
|
6
6
|
} from "./chunk-RZRT7NGT.js";
|
|
@@ -9,7 +9,7 @@ import {
|
|
|
9
9
|
askScope,
|
|
10
10
|
formatScopeLabel,
|
|
11
11
|
injectBackChoice
|
|
12
|
-
} from "./chunk-
|
|
12
|
+
} from "./chunk-HPNRQYLM.js";
|
|
13
13
|
import {
|
|
14
14
|
globalConfigPath,
|
|
15
15
|
localConfigPath
|
|
@@ -352,10 +352,6 @@ async function memoryConfigMenu(opts) {
|
|
|
352
352
|
}
|
|
353
353
|
|
|
354
354
|
export {
|
|
355
|
-
editMemoryTier,
|
|
356
|
-
editMemoryAutoCapture,
|
|
357
|
-
editMemoryAutoStart,
|
|
358
|
-
editMemoryEmbedding,
|
|
359
355
|
writeMemoryMcpEntry,
|
|
360
356
|
getMemoryMcpStatus,
|
|
361
357
|
memoryConfigMenu
|
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
import {
|
|
2
|
+
writeMemoryMcpEntry
|
|
3
|
+
} from "./chunk-C5YWESSF.js";
|
|
4
|
+
import {
|
|
5
|
+
log
|
|
6
|
+
} from "./chunk-NREZK463.js";
|
|
7
|
+
import {
|
|
8
|
+
readJson
|
|
9
|
+
} from "./chunk-5UVDWG5L.js";
|
|
10
|
+
|
|
11
|
+
// src/lib/mcp-state.ts
|
|
12
|
+
import { existsSync } from "fs";
|
|
13
|
+
import { join } from "path";
|
|
14
|
+
import chalk from "chalk";
|
|
15
|
+
function warnStaleMcpJson(memoryDir) {
|
|
16
|
+
const localMcpPath = join(process.cwd(), ".mcp.json");
|
|
17
|
+
if (!existsSync(localMcpPath)) return;
|
|
18
|
+
const mcpJson = readJson(localMcpPath);
|
|
19
|
+
if (mcpJson === null) {
|
|
20
|
+
log.warn(
|
|
21
|
+
".mcp.json exists but could not be parsed \u2014 check for syntax errors."
|
|
22
|
+
);
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
const state = detectMemoryMcpState(mcpJson, existsSync);
|
|
26
|
+
if (state.kind === "stale" || state.kind === "legacy-valid") {
|
|
27
|
+
if (memoryDir) {
|
|
28
|
+
writeMemoryMcpEntry(memoryDir);
|
|
29
|
+
if (state.kind === "stale") {
|
|
30
|
+
log.success(
|
|
31
|
+
"Auto-updated stale .mcp.json to version-stable npx format."
|
|
32
|
+
);
|
|
33
|
+
} else {
|
|
34
|
+
log.success("Updated .mcp.json to version-stable npx format.");
|
|
35
|
+
}
|
|
36
|
+
console.log();
|
|
37
|
+
} else if (state.kind === "stale") {
|
|
38
|
+
console.log(
|
|
39
|
+
chalk.yellow(`\u26A0 Stale MCP config detected in .mcp.json`)
|
|
40
|
+
);
|
|
41
|
+
console.log(chalk.dim(` Path no longer exists: ${state.path}`));
|
|
42
|
+
console.log(
|
|
43
|
+
chalk.dim(` Run "cf memory mcp" to update to the new format.`)
|
|
44
|
+
);
|
|
45
|
+
console.log();
|
|
46
|
+
} else {
|
|
47
|
+
console.log(
|
|
48
|
+
chalk.cyan(
|
|
49
|
+
`\u2139 .mcp.json uses an absolute path for coding-friend-memory.`
|
|
50
|
+
)
|
|
51
|
+
);
|
|
52
|
+
console.log(
|
|
53
|
+
chalk.dim(
|
|
54
|
+
` Consider running "cf memory mcp" to switch to the version-stable format.`
|
|
55
|
+
)
|
|
56
|
+
);
|
|
57
|
+
console.log();
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
function detectMemoryMcpState(mcpJson, pathExists) {
|
|
62
|
+
if (!mcpJson) return { kind: "none" };
|
|
63
|
+
const servers = mcpJson.mcpServers;
|
|
64
|
+
if (!servers) return { kind: "none" };
|
|
65
|
+
const entry = servers["coding-friend-memory"];
|
|
66
|
+
if (!entry) return { kind: "none" };
|
|
67
|
+
const command = entry.command;
|
|
68
|
+
if (!command) return { kind: "none" };
|
|
69
|
+
if (command === "npx") return { kind: "npx" };
|
|
70
|
+
if (command === "node") {
|
|
71
|
+
const args = entry.args;
|
|
72
|
+
const serverPath = args?.[0];
|
|
73
|
+
if (!serverPath) return { kind: "none" };
|
|
74
|
+
if (!pathExists(serverPath)) {
|
|
75
|
+
return { kind: "stale", path: serverPath };
|
|
76
|
+
}
|
|
77
|
+
return { kind: "legacy-valid", path: serverPath };
|
|
78
|
+
}
|
|
79
|
+
return { kind: "none" };
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// src/lib/mcp-health.ts
|
|
83
|
+
import chalk2 from "chalk";
|
|
84
|
+
function printHealthSection(result) {
|
|
85
|
+
console.log(chalk2.dim("\u2500\u2500\u2500 Health Check \u2500\u2500\u2500"));
|
|
86
|
+
for (const check of result.checks) {
|
|
87
|
+
if (check.ok) {
|
|
88
|
+
const detail = check.detail ? `: ${check.detail}` : "";
|
|
89
|
+
console.log(chalk2.green(` \u2713 ${check.label}${detail}`));
|
|
90
|
+
} else if (check.warn) {
|
|
91
|
+
const detail = check.detail ? `: ${check.detail}` : "";
|
|
92
|
+
console.log(chalk2.yellow(` \u26A0 ${check.label}${detail}`));
|
|
93
|
+
} else {
|
|
94
|
+
const detail = check.detail ? `: ${check.detail}` : "";
|
|
95
|
+
console.log(chalk2.red(` \u2717 ${check.label}${detail}`));
|
|
96
|
+
if (check.fix) {
|
|
97
|
+
console.log(chalk2.dim(` \u2192 ${check.fix}`));
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
console.log();
|
|
102
|
+
}
|
|
103
|
+
async function checkMemoryMcpHealth(deps) {
|
|
104
|
+
const checks = [];
|
|
105
|
+
const mcpJson = deps.readMcpJson();
|
|
106
|
+
const state = detectMemoryMcpState(mcpJson, deps.pathExists);
|
|
107
|
+
if (state.kind === "npx") {
|
|
108
|
+
checks.push({ label: "Config (.mcp.json)", ok: true });
|
|
109
|
+
} else if (state.kind === "none") {
|
|
110
|
+
checks.push({
|
|
111
|
+
label: "Config (.mcp.json)",
|
|
112
|
+
ok: false,
|
|
113
|
+
detail: "coding-friend-memory not configured",
|
|
114
|
+
fix: 'Run "cf memory mcp" to add the MCP entry'
|
|
115
|
+
});
|
|
116
|
+
} else {
|
|
117
|
+
const detail = state.kind === "stale" ? `Stale path: ${state.path}` : `Absolute path (legacy): ${state.path}`;
|
|
118
|
+
checks.push({
|
|
119
|
+
label: "Config (.mcp.json)",
|
|
120
|
+
ok: false,
|
|
121
|
+
detail,
|
|
122
|
+
fix: 'Run "cf memory mcp" to update to the npx format'
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
const distExists = deps.pathExists(deps.memoryDistPath);
|
|
126
|
+
checks.push({
|
|
127
|
+
label: "Package built",
|
|
128
|
+
ok: distExists,
|
|
129
|
+
...distExists ? {} : {
|
|
130
|
+
detail: "cf-memory not built",
|
|
131
|
+
fix: 'Run "cf memory init" to build'
|
|
132
|
+
}
|
|
133
|
+
});
|
|
134
|
+
let daemonRunning = false;
|
|
135
|
+
let daemonCheckError;
|
|
136
|
+
try {
|
|
137
|
+
daemonRunning = await deps.isDaemonRunning();
|
|
138
|
+
} catch (err) {
|
|
139
|
+
daemonCheckError = err instanceof Error ? err.message : String(err);
|
|
140
|
+
}
|
|
141
|
+
checks.push({
|
|
142
|
+
label: "Daemon",
|
|
143
|
+
ok: daemonRunning,
|
|
144
|
+
detail: daemonRunning ? "running" : daemonCheckError ? `check failed: ${daemonCheckError}` : "stopped (starts automatically on MCP connect)",
|
|
145
|
+
...!daemonRunning ? { warn: true } : {}
|
|
146
|
+
});
|
|
147
|
+
const ok = checks.every((c) => c.ok || c.warn === true);
|
|
148
|
+
return { checks, ok };
|
|
149
|
+
}
|
|
150
|
+
async function checkLearnMcpHealth(deps) {
|
|
151
|
+
const checks = [];
|
|
152
|
+
const isRegistered = deps.checkRegistered();
|
|
153
|
+
checks.push({
|
|
154
|
+
label: "Registered (claude mcp)",
|
|
155
|
+
ok: isRegistered,
|
|
156
|
+
...isRegistered ? {} : {
|
|
157
|
+
detail: "coding-friend-learn not registered",
|
|
158
|
+
fix: 'Run "cf mcp" to register'
|
|
159
|
+
}
|
|
160
|
+
});
|
|
161
|
+
const distExists = deps.pathExists(deps.learnMcpDistPath);
|
|
162
|
+
checks.push({
|
|
163
|
+
label: "Package built",
|
|
164
|
+
ok: distExists,
|
|
165
|
+
...distExists ? {} : {
|
|
166
|
+
detail: "learn-mcp not built",
|
|
167
|
+
fix: 'Run "cf mcp" to install and build'
|
|
168
|
+
}
|
|
169
|
+
});
|
|
170
|
+
const mdFiles = deps.listMdFiles(deps.docsDir);
|
|
171
|
+
const hasDocs = mdFiles.length > 0;
|
|
172
|
+
checks.push({
|
|
173
|
+
label: "Docs directory",
|
|
174
|
+
ok: hasDocs,
|
|
175
|
+
...hasDocs ? {} : {
|
|
176
|
+
detail: `No .md files found in ${deps.docsDir}`,
|
|
177
|
+
fix: 'Run "/cf-learn" to generate docs'
|
|
178
|
+
}
|
|
179
|
+
});
|
|
180
|
+
const ok = checks.every((c) => c.ok || c.warn === true);
|
|
181
|
+
return { checks, ok };
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
export {
|
|
185
|
+
warnStaleMcpJson,
|
|
186
|
+
detectMemoryMcpState,
|
|
187
|
+
printHealthSection,
|
|
188
|
+
checkMemoryMcpHealth,
|
|
189
|
+
checkLearnMcpHealth
|
|
190
|
+
};
|
|
@@ -1,22 +1,22 @@
|
|
|
1
1
|
import {
|
|
2
2
|
ensureStatusline,
|
|
3
3
|
getInstalledVersion
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-WNTMKIKC.js";
|
|
5
5
|
import {
|
|
6
6
|
ensureShellCompletion
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-RLUNNJWJ.js";
|
|
8
8
|
import {
|
|
9
9
|
resolveScope
|
|
10
|
-
} from "./chunk-
|
|
11
|
-
import {
|
|
12
|
-
claudeSettingsPath
|
|
13
|
-
} from "./chunk-TWKNGPBO.js";
|
|
10
|
+
} from "./chunk-HPNRQYLM.js";
|
|
14
11
|
import {
|
|
15
12
|
commandExists,
|
|
16
13
|
run,
|
|
17
14
|
runWithStderr,
|
|
18
15
|
sleepSync
|
|
19
16
|
} from "./chunk-EVGXUDX4.js";
|
|
17
|
+
import {
|
|
18
|
+
claudeSettingsPath
|
|
19
|
+
} from "./chunk-TWKNGPBO.js";
|
|
20
20
|
import {
|
|
21
21
|
log,
|
|
22
22
|
printBanner
|
|
@@ -1,38 +1,37 @@
|
|
|
1
1
|
import {
|
|
2
|
+
checkMemoryMcpHealth,
|
|
3
|
+
printHealthSection,
|
|
2
4
|
warnStaleMcpJson
|
|
3
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-E5J24GD5.js";
|
|
4
6
|
import {
|
|
5
|
-
editMemoryAutoCapture,
|
|
6
|
-
editMemoryAutoStart,
|
|
7
|
-
editMemoryEmbedding,
|
|
8
|
-
editMemoryTier,
|
|
9
7
|
getMemoryMcpStatus,
|
|
10
8
|
memoryConfigMenu,
|
|
11
9
|
writeMemoryMcpEntry
|
|
12
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-C5YWESSF.js";
|
|
13
11
|
import {
|
|
14
12
|
loadConfig,
|
|
15
13
|
resolveMemoryDir
|
|
16
|
-
} from "./chunk-
|
|
14
|
+
} from "./chunk-B2NFXRFP.js";
|
|
17
15
|
import {
|
|
18
16
|
getLibPath
|
|
19
17
|
} from "./chunk-RZRT7NGT.js";
|
|
20
18
|
import {
|
|
21
19
|
showConfigHint
|
|
22
|
-
} from "./chunk-
|
|
23
|
-
import {
|
|
24
|
-
globalConfigPath,
|
|
25
|
-
localConfigPath
|
|
26
|
-
} from "./chunk-TWKNGPBO.js";
|
|
20
|
+
} from "./chunk-HPNRQYLM.js";
|
|
27
21
|
import {
|
|
28
22
|
run,
|
|
29
23
|
runWithStderr
|
|
30
24
|
} from "./chunk-EVGXUDX4.js";
|
|
25
|
+
import {
|
|
26
|
+
globalConfigPath,
|
|
27
|
+
localConfigPath
|
|
28
|
+
} from "./chunk-TWKNGPBO.js";
|
|
31
29
|
import {
|
|
32
30
|
log,
|
|
33
31
|
printBanner
|
|
34
32
|
} from "./chunk-NREZK463.js";
|
|
35
33
|
import {
|
|
34
|
+
mergeJson,
|
|
36
35
|
readJson
|
|
37
36
|
} from "./chunk-5UVDWG5L.js";
|
|
38
37
|
|
|
@@ -336,19 +335,38 @@ async function memoryRebuildCommand() {
|
|
|
336
335
|
const config = loadConfig();
|
|
337
336
|
const embedding = config.memory?.embedding;
|
|
338
337
|
const opts = embedding ? { embedding, skipVec: false } : { skipVec: false };
|
|
339
|
-
const
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
338
|
+
const runRebuild = async () => {
|
|
339
|
+
const backend = new SqliteBackend(memoryDir, opts);
|
|
340
|
+
try {
|
|
341
|
+
await backend.rebuild();
|
|
342
|
+
const stats = await backend.stats();
|
|
343
|
+
log.success(`Rebuilt: ${stats.total} memories indexed.`);
|
|
344
|
+
if (backend.isVecEnabled()) {
|
|
345
|
+
log.info(`Vector search: ${chalk.green("enabled")}`);
|
|
346
|
+
}
|
|
347
|
+
if (!backend.isRebuildNeeded()) {
|
|
348
|
+
log.info("Embedding dimensions: up to date");
|
|
349
|
+
}
|
|
350
|
+
} finally {
|
|
351
|
+
await backend.close();
|
|
346
352
|
}
|
|
347
|
-
|
|
348
|
-
|
|
353
|
+
};
|
|
354
|
+
try {
|
|
355
|
+
await runRebuild();
|
|
356
|
+
} catch (err) {
|
|
357
|
+
const isCorrupt = err instanceof Error && (err.message.includes("database disk image is malformed") || err.code === "SQLITE_CORRUPT" || err.code === "SQLITE_IOERR_SHORT_READ");
|
|
358
|
+
if (isCorrupt) {
|
|
359
|
+
const dbPath = getDbPath(memoryDir);
|
|
360
|
+
if (dbPath) {
|
|
361
|
+
log.warn("SQLite database is corrupt. Deleting and recreating...");
|
|
362
|
+
for (const suffix of ["", "-shm", "-wal"]) {
|
|
363
|
+
rmSync(dbPath + suffix, { force: true });
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
await runRebuild();
|
|
367
|
+
} else {
|
|
368
|
+
throw err;
|
|
349
369
|
}
|
|
350
|
-
} finally {
|
|
351
|
-
await backend.close();
|
|
352
370
|
}
|
|
353
371
|
return;
|
|
354
372
|
}
|
|
@@ -422,69 +440,80 @@ function isMemoryInitialized() {
|
|
|
422
440
|
return getDbPath(memoryDir) !== null;
|
|
423
441
|
}
|
|
424
442
|
async function memoryInitWizard(memoryDir, mcpDir) {
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
443
|
+
const globalCfg = readJson(globalConfigPath());
|
|
444
|
+
const globalMem = globalCfg?.memory ?? {};
|
|
445
|
+
const tier = globalMem.tier ?? "auto";
|
|
446
|
+
const embedding = globalMem.embedding ?? { provider: "transformers" };
|
|
447
|
+
const autoCapture = globalMem.autoCapture ?? true;
|
|
448
|
+
const autoStart = globalMem.autoStart ?? true;
|
|
449
|
+
const existingLocalCfg = readJson(localConfigPath());
|
|
450
|
+
const existingMem = existingLocalCfg?.memory ?? {};
|
|
451
|
+
mergeJson(localConfigPath(), {
|
|
452
|
+
memory: { ...existingMem, tier, embedding, autoCapture, autoStart }
|
|
453
|
+
});
|
|
454
|
+
const inherited = chalk.dim("\u2190 global config");
|
|
455
|
+
const defaulted = chalk.dim("\u2190 default");
|
|
456
|
+
const src = (field) => field !== void 0 ? inherited : defaulted;
|
|
430
457
|
console.log();
|
|
431
|
-
log.
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
readJson(localConfigPath())
|
|
458
|
+
log.info(`Search tier: ${chalk.cyan(String(tier))} ${src(globalMem.tier)}`);
|
|
459
|
+
log.dim(
|
|
460
|
+
" Auto-detects best backend: SQLite (Tier 1) \u2192 MiniSearch daemon (Tier 2) \u2192 Grep (Tier 3)."
|
|
435
461
|
);
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
await editMemoryAutoCapture(
|
|
439
|
-
readJson(globalConfigPath()),
|
|
440
|
-
readJson(localConfigPath())
|
|
462
|
+
log.info(
|
|
463
|
+
`Embedding: ${chalk.cyan(String(embedding.provider ?? "transformers"))} ${src(globalMem.embedding)}`
|
|
441
464
|
);
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
465
|
+
log.dim(
|
|
466
|
+
" Powers semantic search in Tier 1. Runs locally, no external service needed."
|
|
467
|
+
);
|
|
468
|
+
log.info(
|
|
469
|
+
`Auto-capture: ${autoCapture ? chalk.green("on") : chalk.dim("off")} ${src(globalMem.autoCapture)}`
|
|
470
|
+
);
|
|
471
|
+
log.dim(
|
|
472
|
+
" Saves session summaries to memory when Claude Code compacts context."
|
|
473
|
+
);
|
|
474
|
+
log.info(
|
|
475
|
+
`Auto-start daemon: ${autoStart ? chalk.green("on") : chalk.dim("off")} ${src(globalMem.autoStart)}`
|
|
476
|
+
);
|
|
477
|
+
log.dim(
|
|
478
|
+
" Background process watches docs/memory/ and auto-rebuilds the search index on file changes."
|
|
447
479
|
);
|
|
448
480
|
console.log();
|
|
449
481
|
const config = loadConfig();
|
|
450
|
-
const
|
|
451
|
-
if (
|
|
482
|
+
const resolvedTier = config.memory?.tier ?? "auto";
|
|
483
|
+
if (resolvedTier === "markdown") {
|
|
452
484
|
await setupMemoryMcp(memoryDir, mcpDir);
|
|
453
|
-
log.
|
|
454
|
-
|
|
455
|
-
);
|
|
485
|
+
console.log('\u{1F389} Memory initialized! Run "cf memory status" to verify.');
|
|
486
|
+
printInitDone();
|
|
456
487
|
return;
|
|
457
488
|
}
|
|
458
|
-
if (
|
|
489
|
+
if (resolvedTier === "lite") {
|
|
459
490
|
await setupMemoryMcp(memoryDir, mcpDir);
|
|
460
|
-
log.
|
|
461
|
-
|
|
462
|
-
);
|
|
491
|
+
console.log('\u{1F389} Memory initialized! Run "cf memory status" to verify.');
|
|
492
|
+
printInitDone();
|
|
463
493
|
return;
|
|
464
494
|
}
|
|
465
495
|
const depsOk = await ensureSqliteDepsIfNeeded(mcpDir);
|
|
466
496
|
if (!depsOk) return;
|
|
467
497
|
if (!existsSync(memoryDir)) {
|
|
468
|
-
log.info(
|
|
469
|
-
"No memory directory found. Memories will be indexed as they're created."
|
|
470
|
-
);
|
|
471
498
|
await setupMemoryMcp(memoryDir, mcpDir);
|
|
472
|
-
log
|
|
499
|
+
console.log('\u{1F389} Memory initialized! Run "cf memory status" to verify.');
|
|
500
|
+
printInitDone();
|
|
473
501
|
return;
|
|
474
502
|
}
|
|
475
503
|
const docCount = countMdFiles(memoryDir);
|
|
476
504
|
if (docCount === 0) {
|
|
477
505
|
log.info("No existing memories to import.");
|
|
478
506
|
await setupMemoryMcp(memoryDir, mcpDir);
|
|
479
|
-
log
|
|
507
|
+
console.log('\u{1F389} Memory initialized! Run "cf memory status" to verify.');
|
|
508
|
+
printInitDone();
|
|
480
509
|
return;
|
|
481
510
|
}
|
|
482
511
|
log.step(`Importing ${docCount} existing memories into SQLite...`);
|
|
483
512
|
const { SqliteBackend } = await import(join(mcpDir, "dist/backends/sqlite/index.js"));
|
|
484
|
-
const
|
|
513
|
+
const configEmbedding = config.memory?.embedding;
|
|
485
514
|
const backend = new SqliteBackend(memoryDir, {
|
|
486
515
|
skipVec: false,
|
|
487
|
-
...
|
|
516
|
+
...configEmbedding && { embedding: configEmbedding }
|
|
488
517
|
});
|
|
489
518
|
try {
|
|
490
519
|
await backend.rebuild();
|
|
@@ -498,10 +527,20 @@ async function memoryInitWizard(memoryDir, mcpDir) {
|
|
|
498
527
|
}
|
|
499
528
|
await setupMemoryMcp(memoryDir, mcpDir);
|
|
500
529
|
console.log();
|
|
501
|
-
log
|
|
530
|
+
console.log('\u{1F389} Memory initialized! Run "cf memory status" to verify.');
|
|
502
531
|
log.info(
|
|
503
532
|
`Tip: Run ${chalk.cyan("/cf-scan")} in Claude Code to populate memory with project knowledge.`
|
|
504
533
|
);
|
|
534
|
+
log.dim(` Change settings anytime: ${chalk.cyan("cf memory config")}`);
|
|
535
|
+
log.dim(
|
|
536
|
+
` Documentation: ${chalk.cyan("https://cf.dinhanhthi.com/docs/cli/cf-memory/")}`
|
|
537
|
+
);
|
|
538
|
+
}
|
|
539
|
+
function printInitDone() {
|
|
540
|
+
log.dim(` Change settings anytime: ${chalk.cyan("cf memory config")}`);
|
|
541
|
+
log.dim(
|
|
542
|
+
` Documentation: ${chalk.cyan("https://cf.dinhanhthi.com/docs/cli/cf-memory/")}`
|
|
543
|
+
);
|
|
505
544
|
}
|
|
506
545
|
async function setupMemoryMcp(memoryDir, _mcpDir) {
|
|
507
546
|
const mcpStatus = getMemoryMcpStatus();
|
|
@@ -509,20 +548,10 @@ async function setupMemoryMcp(memoryDir, _mcpDir) {
|
|
|
509
548
|
log.info(`MCP: ${chalk.green("already configured")} in .mcp.json`);
|
|
510
549
|
return;
|
|
511
550
|
}
|
|
512
|
-
|
|
513
|
-
log.step("MCP setup");
|
|
551
|
+
writeMemoryMcpEntry(memoryDir);
|
|
514
552
|
log.dim(
|
|
515
|
-
"
|
|
553
|
+
" Connects Claude Code to memory so skills can store and retrieve knowledge."
|
|
516
554
|
);
|
|
517
|
-
const addMcp = await confirm({
|
|
518
|
-
message: "Add coding-friend-memory to .mcp.json?",
|
|
519
|
-
default: true
|
|
520
|
-
});
|
|
521
|
-
if (!addMcp) {
|
|
522
|
-
log.dim('Skipped. Run "cf memory mcp" anytime to get the config.');
|
|
523
|
-
return;
|
|
524
|
-
}
|
|
525
|
-
writeMemoryMcpEntry(memoryDir);
|
|
526
555
|
}
|
|
527
556
|
async function memoryInitCommand() {
|
|
528
557
|
const memoryDir = getMemoryDir();
|
|
@@ -541,9 +570,8 @@ async function memoryInitCommand() {
|
|
|
541
570
|
return;
|
|
542
571
|
}
|
|
543
572
|
console.log();
|
|
544
|
-
printBanner("\u{1F9E0} Memory Setup");
|
|
573
|
+
printBanner("\u{1F9E0} CF Memory Setup");
|
|
545
574
|
console.log();
|
|
546
|
-
showConfigHint();
|
|
547
575
|
await memoryInitWizard(memoryDir, mcpDir);
|
|
548
576
|
}
|
|
549
577
|
async function memoryConfigCommand() {
|
|
@@ -808,6 +836,21 @@ async function memoryMcpCommand() {
|
|
|
808
836
|
ensureMemoryBuilt(mcpDir);
|
|
809
837
|
warnStaleMcpJson(memoryDir);
|
|
810
838
|
printMemoryMcpConfig(memoryDir);
|
|
839
|
+
const localMcpPath = join(process.cwd(), ".mcp.json");
|
|
840
|
+
const memoryDistPath = join(mcpDir, "dist", "index.js");
|
|
841
|
+
let isDaemonRunning = async () => false;
|
|
842
|
+
try {
|
|
843
|
+
const proc = await import(join(mcpDir, "dist/daemon/process.js"));
|
|
844
|
+
isDaemonRunning = proc.isDaemonRunning;
|
|
845
|
+
} catch {
|
|
846
|
+
}
|
|
847
|
+
const memoryHealth = await checkMemoryMcpHealth({
|
|
848
|
+
readMcpJson: () => readJson(localMcpPath),
|
|
849
|
+
pathExists: existsSync,
|
|
850
|
+
isDaemonRunning,
|
|
851
|
+
memoryDistPath
|
|
852
|
+
});
|
|
853
|
+
printHealthSection(memoryHealth);
|
|
811
854
|
}
|
|
812
855
|
|
|
813
856
|
export {
|