coding-friend-cli 1.33.0 → 1.34.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/dist/{chunk-NZKIYVIK.js → chunk-3UIH5U3Y.js} +1 -1
- package/dist/{chunk-G2P43ZK2.js → chunk-5X3JZKYW.js} +78 -150
- package/dist/chunk-AYIREX73.js +85 -0
- package/dist/{config-TAYQIBPL.js → chunk-CXG6I6ZF.js} +76 -3
- package/dist/{chunk-LK6HFD35.js → chunk-K6EDQGDN.js} +17 -0
- package/dist/chunk-TVAY4OQL.js +118 -0
- package/dist/config-MCEQOLE5.js +22 -0
- package/dist/{host-Y4N4PI4N.js → host-Y3WIXDSS.js} +1 -1
- package/dist/index.js +17 -17
- package/dist/{init-QZG3RV5S.js → init-WDFYY5MM.js} +18 -4
- package/dist/{mcp-BD2K7OL6.js → mcp-J4SD646S.js} +67 -14
- package/dist/{memory-FZMACIGE.js → memory-RLZR7XQY.js} +4 -3
- package/dist/{session-RHXTVNRU.js → session-6WQGKIRG.js} +1 -1
- package/dist/{status-V2A7RSSM.js → status-BX2SVBBW.js} +81 -4
- package/package.json +1 -1
|
@@ -1,8 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
memoryConfigMenu
|
|
3
|
-
} from "./chunk-
|
|
4
|
-
import "./chunk-LK6HFD35.js";
|
|
5
|
-
import "./chunk-RZRT7NGT.js";
|
|
3
|
+
} from "./chunk-3UIH5U3Y.js";
|
|
6
4
|
import {
|
|
7
5
|
findStatuslineHookPath,
|
|
8
6
|
getCurrentAccountEmail,
|
|
@@ -413,6 +411,68 @@ async function editAutoApprove(globalCfg, localCfg) {
|
|
|
413
411
|
log.dim("Docs: https://cf.dinhanhthi.com/docs/reference/auto-approve/");
|
|
414
412
|
}
|
|
415
413
|
}
|
|
414
|
+
function buildCodexConfig(input2) {
|
|
415
|
+
return {
|
|
416
|
+
enabled: input2.enabled,
|
|
417
|
+
modes: input2.modes,
|
|
418
|
+
effort: input2.effort
|
|
419
|
+
};
|
|
420
|
+
}
|
|
421
|
+
async function editCodex(globalCfg, localCfg) {
|
|
422
|
+
const current = localCfg?.codex ?? globalCfg?.codex;
|
|
423
|
+
const defaults = DEFAULT_CONFIG.codex;
|
|
424
|
+
if (current !== void 0) {
|
|
425
|
+
log.dim(
|
|
426
|
+
`Current: enabled=${current.enabled}, modes=${(current.modes ?? []).join(",") || "(none)"}, effort=${current.effort}`
|
|
427
|
+
);
|
|
428
|
+
}
|
|
429
|
+
const enabled = await confirm({
|
|
430
|
+
message: "Enable Codex as 6th specialist in /cf-review? (requires `codex` CLI in PATH)",
|
|
431
|
+
default: current?.enabled ?? defaults.enabled
|
|
432
|
+
});
|
|
433
|
+
const modes = await checkbox({
|
|
434
|
+
message: "Which review modes should Codex run in?",
|
|
435
|
+
choices: [
|
|
436
|
+
{ name: "QUICK (\u22643 files, \u226450 lines)", value: "QUICK" },
|
|
437
|
+
{ name: "STANDARD (4\u201310 files or 51\u2013300 lines)", value: "STANDARD" },
|
|
438
|
+
{
|
|
439
|
+
name: "DEEP (>10 files, >300 lines, or sensitive paths)",
|
|
440
|
+
value: "DEEP"
|
|
441
|
+
}
|
|
442
|
+
],
|
|
443
|
+
default: current?.modes ?? defaults.modes
|
|
444
|
+
});
|
|
445
|
+
if (modes.length === 0) {
|
|
446
|
+
log.dim(
|
|
447
|
+
"Warning: no modes selected \u2014 Codex will never run. Select at least one mode."
|
|
448
|
+
);
|
|
449
|
+
}
|
|
450
|
+
const effort = await select({
|
|
451
|
+
message: "Codex reasoning effort level:",
|
|
452
|
+
choices: [
|
|
453
|
+
{ name: "minimal \u2014 fastest, least thorough", value: "minimal" },
|
|
454
|
+
{ name: "low", value: "low" },
|
|
455
|
+
{ name: "medium (default)", value: "medium" },
|
|
456
|
+
{ name: "high", value: "high" },
|
|
457
|
+
{ name: "xhigh \u2014 slowest, most thorough", value: "xhigh" }
|
|
458
|
+
],
|
|
459
|
+
default: current?.effort ?? defaults.effort
|
|
460
|
+
});
|
|
461
|
+
const scope = await askScope();
|
|
462
|
+
if (scope === "back") return;
|
|
463
|
+
const codexCfg = buildCodexConfig({ enabled, modes, effort });
|
|
464
|
+
writeToScope(scope, { codex: codexCfg });
|
|
465
|
+
if (enabled) {
|
|
466
|
+
log.dim(
|
|
467
|
+
"Tip: make sure `codex` CLI is installed \u2014 `npm install -g @openai/codex`"
|
|
468
|
+
);
|
|
469
|
+
log.dim(
|
|
470
|
+
"Docs: https://cf.dinhanhthi.com/docs/configuration/config-json/#codex"
|
|
471
|
+
);
|
|
472
|
+
} else {
|
|
473
|
+
log.dim("Codex disabled.");
|
|
474
|
+
}
|
|
475
|
+
}
|
|
416
476
|
async function editStatusline() {
|
|
417
477
|
const hookResult = findStatuslineHookPath();
|
|
418
478
|
if (!hookResult) {
|
|
@@ -626,6 +686,8 @@ async function configCommand() {
|
|
|
626
686
|
globalCfg,
|
|
627
687
|
localCfg
|
|
628
688
|
);
|
|
689
|
+
const codexScope = getScopeLabel("codex", globalCfg, localCfg);
|
|
690
|
+
const codexEnabled = getMergedValue("codex", globalCfg, localCfg)?.enabled;
|
|
629
691
|
const statuslineStatus = isStatuslineConfigured() ? chalk.green("configured") : chalk.yellow("not configured");
|
|
630
692
|
const completionStatus = hasShellCompletion() ? chalk.green("installed") : chalk.yellow("not installed");
|
|
631
693
|
const projectRules = getExistingRules(claudeLocalSettingsPath());
|
|
@@ -666,6 +728,11 @@ async function configCommand() {
|
|
|
666
728
|
value: "autoApprove",
|
|
667
729
|
description: " Auto-approve safe tool calls, block destructive ones, prompt for ambiguous"
|
|
668
730
|
},
|
|
731
|
+
{
|
|
732
|
+
name: `Codex cross-engine review ${formatScopeLabel(codexScope)}${codexEnabled !== void 0 ? ` (${codexEnabled ? "enabled" : "disabled"})` : ""}`,
|
|
733
|
+
value: "codex",
|
|
734
|
+
description: " Enable OpenAI Codex as 6th specialist in /cf-review (opt-in)"
|
|
735
|
+
},
|
|
669
736
|
{
|
|
670
737
|
name: `Statusline (${statuslineStatus})`,
|
|
671
738
|
value: "statusline",
|
|
@@ -709,6 +776,9 @@ async function configCommand() {
|
|
|
709
776
|
case "autoApprove":
|
|
710
777
|
await editAutoApprove(globalCfg, localCfg);
|
|
711
778
|
break;
|
|
779
|
+
case "codex":
|
|
780
|
+
await editCodex(globalCfg, localCfg);
|
|
781
|
+
break;
|
|
712
782
|
case "statusline":
|
|
713
783
|
await editStatusline();
|
|
714
784
|
break;
|
|
@@ -725,6 +795,9 @@ async function configCommand() {
|
|
|
725
795
|
console.log();
|
|
726
796
|
}
|
|
727
797
|
}
|
|
798
|
+
|
|
728
799
|
export {
|
|
800
|
+
buildCodexConfig,
|
|
801
|
+
editCodex,
|
|
729
802
|
configCommand
|
|
730
803
|
};
|
|
@@ -160,6 +160,22 @@ function stripPath(obj, path) {
|
|
|
160
160
|
}
|
|
161
161
|
delete current[String(path[path.length - 1])];
|
|
162
162
|
}
|
|
163
|
+
function sanitizeRawConfig(raw) {
|
|
164
|
+
const result = ConfigSchema.safeParse(raw);
|
|
165
|
+
if (result.success) {
|
|
166
|
+
return result.data;
|
|
167
|
+
}
|
|
168
|
+
const unknownKeys = [];
|
|
169
|
+
for (const issue of result.error.issues) {
|
|
170
|
+
if (issue.code === "unrecognized_keys") {
|
|
171
|
+
const keys = issue.keys ?? [];
|
|
172
|
+
unknownKeys.push(...keys);
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
const keyList = unknownKeys.length > 0 ? `: ${unknownKeys.join(", ")}` : "";
|
|
176
|
+
log.warn(`sanitizeRawConfig: unrecognized config keys${keyList} \u2014 returning raw config`);
|
|
177
|
+
return raw;
|
|
178
|
+
}
|
|
163
179
|
function loadConfig() {
|
|
164
180
|
const global = readJson(globalConfigPath());
|
|
165
181
|
const local = readJson(localConfigPath());
|
|
@@ -196,6 +212,7 @@ function resolveMemoryDir(explicitPath) {
|
|
|
196
212
|
}
|
|
197
213
|
|
|
198
214
|
export {
|
|
215
|
+
sanitizeRawConfig,
|
|
199
216
|
loadConfig,
|
|
200
217
|
resolveDocsDir,
|
|
201
218
|
resolveMemoryDir
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import {
|
|
2
|
+
detectMemoryMcpState
|
|
3
|
+
} from "./chunk-AYIREX73.js";
|
|
4
|
+
|
|
5
|
+
// src/lib/mcp-health.ts
|
|
6
|
+
async function checkMemoryMcpHealth(deps) {
|
|
7
|
+
const checks = [];
|
|
8
|
+
const mcpJson = deps.readMcpJson();
|
|
9
|
+
const state = detectMemoryMcpState(mcpJson, deps.pathExists);
|
|
10
|
+
if (state.kind === "npx") {
|
|
11
|
+
checks.push({ label: "Config (.mcp.json)", ok: true });
|
|
12
|
+
} else if (state.kind === "none") {
|
|
13
|
+
checks.push({
|
|
14
|
+
label: "Config (.mcp.json)",
|
|
15
|
+
ok: false,
|
|
16
|
+
detail: "coding-friend-memory not configured",
|
|
17
|
+
fix: 'Run "cf memory mcp" to add the MCP entry'
|
|
18
|
+
});
|
|
19
|
+
} else {
|
|
20
|
+
const detail = state.kind === "stale" ? `Stale path: ${state.path}` : `Absolute path (legacy): ${state.path}`;
|
|
21
|
+
checks.push({
|
|
22
|
+
label: "Config (.mcp.json)",
|
|
23
|
+
ok: false,
|
|
24
|
+
detail,
|
|
25
|
+
fix: 'Run "cf memory mcp" to update to the npx format'
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
const distExists = deps.pathExists(deps.memoryDistPath);
|
|
29
|
+
checks.push({
|
|
30
|
+
label: "Package built",
|
|
31
|
+
ok: distExists,
|
|
32
|
+
...distExists ? {} : {
|
|
33
|
+
detail: "cf-memory not built",
|
|
34
|
+
fix: 'Run "cf memory init" to build'
|
|
35
|
+
}
|
|
36
|
+
});
|
|
37
|
+
let daemonRunning = false;
|
|
38
|
+
let daemonCheckError;
|
|
39
|
+
try {
|
|
40
|
+
daemonRunning = await deps.isDaemonRunning();
|
|
41
|
+
} catch (err) {
|
|
42
|
+
daemonCheckError = err instanceof Error ? err.message : String(err);
|
|
43
|
+
}
|
|
44
|
+
checks.push({
|
|
45
|
+
label: "Daemon status",
|
|
46
|
+
ok: daemonRunning,
|
|
47
|
+
...daemonRunning ? {} : {
|
|
48
|
+
warn: true,
|
|
49
|
+
detail: daemonCheckError ? `check failed: ${daemonCheckError}` : "stopped (starts automatically on MCP connect)"
|
|
50
|
+
}
|
|
51
|
+
});
|
|
52
|
+
const ok = checks.every((c) => c.ok || c.warn === true);
|
|
53
|
+
return { checks, ok };
|
|
54
|
+
}
|
|
55
|
+
async function checkLearnMcpHealth(deps) {
|
|
56
|
+
const checks = [];
|
|
57
|
+
const mcpJson = deps.readMcpJson();
|
|
58
|
+
const servers = mcpJson?.mcpServers;
|
|
59
|
+
const hasEntry = servers != null && typeof servers === "object" && !Array.isArray(servers) && "coding-friend-learn" in servers;
|
|
60
|
+
checks.push({
|
|
61
|
+
label: "Config (.mcp.json)",
|
|
62
|
+
ok: hasEntry,
|
|
63
|
+
...hasEntry ? {} : {
|
|
64
|
+
detail: "coding-friend-learn not configured",
|
|
65
|
+
fix: 'Run "cf mcp" to print the config snippet and add it'
|
|
66
|
+
}
|
|
67
|
+
});
|
|
68
|
+
const distExists = deps.pathExists(deps.learnMcpDistPath);
|
|
69
|
+
checks.push({
|
|
70
|
+
label: "Package built",
|
|
71
|
+
ok: distExists,
|
|
72
|
+
...distExists ? {} : {
|
|
73
|
+
detail: "learn-mcp not built",
|
|
74
|
+
fix: 'Run "cf mcp" to install and build'
|
|
75
|
+
}
|
|
76
|
+
});
|
|
77
|
+
const mdFiles = deps.listMdFiles(deps.docsDir);
|
|
78
|
+
const hasDocs = mdFiles.length > 0;
|
|
79
|
+
checks.push({
|
|
80
|
+
label: "Docs directory",
|
|
81
|
+
ok: hasDocs,
|
|
82
|
+
...hasDocs ? {} : {
|
|
83
|
+
detail: `No .md files found in ${deps.docsDir}`,
|
|
84
|
+
fix: 'Run "/cf-learn" to generate docs'
|
|
85
|
+
}
|
|
86
|
+
});
|
|
87
|
+
const ok = checks.every((c) => c.ok || c.warn === true);
|
|
88
|
+
return { checks, ok };
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// src/lib/fs-utils.ts
|
|
92
|
+
import { readdirSync } from "fs";
|
|
93
|
+
import { join } from "path";
|
|
94
|
+
function listMdFilesRecursive(dir, maxDepth = 15) {
|
|
95
|
+
try {
|
|
96
|
+
const results = [];
|
|
97
|
+
for (const entry of readdirSync(dir, { withFileTypes: true })) {
|
|
98
|
+
if (entry.isDirectory()) {
|
|
99
|
+
if (maxDepth > 0) {
|
|
100
|
+
results.push(
|
|
101
|
+
...listMdFilesRecursive(join(dir, entry.name), maxDepth - 1)
|
|
102
|
+
);
|
|
103
|
+
}
|
|
104
|
+
} else if (entry.name.endsWith(".md") && entry.name !== "README.md") {
|
|
105
|
+
results.push(entry.name);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
return results;
|
|
109
|
+
} catch {
|
|
110
|
+
return [];
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
export {
|
|
115
|
+
checkMemoryMcpHealth,
|
|
116
|
+
checkLearnMcpHealth,
|
|
117
|
+
listMdFilesRecursive
|
|
118
|
+
};
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import {
|
|
2
|
+
buildCodexConfig,
|
|
3
|
+
configCommand,
|
|
4
|
+
editCodex
|
|
5
|
+
} from "./chunk-CXG6I6ZF.js";
|
|
6
|
+
import "./chunk-3UIH5U3Y.js";
|
|
7
|
+
import "./chunk-K6EDQGDN.js";
|
|
8
|
+
import "./chunk-RZRT7NGT.js";
|
|
9
|
+
import "./chunk-XOHLBLN2.js";
|
|
10
|
+
import "./chunk-OYPOADCX.js";
|
|
11
|
+
import "./chunk-DMJCO6LJ.js";
|
|
12
|
+
import "./chunk-Z5Q533UG.js";
|
|
13
|
+
import "./chunk-TWKNGPBO.js";
|
|
14
|
+
import "./chunk-EVGXUDX4.js";
|
|
15
|
+
import "./chunk-NREZK463.js";
|
|
16
|
+
import "./chunk-O27FVQFU.js";
|
|
17
|
+
import "./chunk-5UVDWG5L.js";
|
|
18
|
+
export {
|
|
19
|
+
buildCodexConfig,
|
|
20
|
+
configCommand,
|
|
21
|
+
editCodex
|
|
22
|
+
};
|
package/dist/index.js
CHANGED
|
@@ -30,19 +30,19 @@ 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-WDFYY5MM.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-MCEQOLE5.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-Y3WIXDSS.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-J4SD646S.js");
|
|
46
46
|
await mcpCommand(path);
|
|
47
47
|
});
|
|
48
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) => {
|
|
@@ -69,7 +69,7 @@ program.command("update").description("Update coding-friend plugin, CLI, and sta
|
|
|
69
69
|
await updateCommand(opts);
|
|
70
70
|
});
|
|
71
71
|
program.command("status").description("Show comprehensive Coding Friend status").action(async () => {
|
|
72
|
-
const { statusCommand } = await import("./status-
|
|
72
|
+
const { statusCommand } = await import("./status-BX2SVBBW.js");
|
|
73
73
|
await statusCommand();
|
|
74
74
|
});
|
|
75
75
|
var session = program.command("session").description("Save and load Claude Code sessions across machines");
|
|
@@ -84,11 +84,11 @@ session.command("save").description("Save current Claude Code session to sync fo
|
|
|
84
84
|
"-s, --session-id <id>",
|
|
85
85
|
"session UUID to save (default: auto-detect newest)"
|
|
86
86
|
).option("-l, --label <label>", "label for this session").action(async (opts) => {
|
|
87
|
-
const { sessionSaveCommand } = await import("./session-
|
|
87
|
+
const { sessionSaveCommand } = await import("./session-6WQGKIRG.js");
|
|
88
88
|
await sessionSaveCommand(opts);
|
|
89
89
|
});
|
|
90
90
|
session.command("load").description("Load a saved session from sync folder").action(async () => {
|
|
91
|
-
const { sessionLoadCommand } = await import("./session-
|
|
91
|
+
const { sessionLoadCommand } = await import("./session-6WQGKIRG.js");
|
|
92
92
|
await sessionLoadCommand();
|
|
93
93
|
});
|
|
94
94
|
var memory = program.command("memory").description("AI memory system \u2014 store and search project knowledge");
|
|
@@ -108,43 +108,43 @@ Memory subcommands:
|
|
|
108
108
|
memory mcp Show MCP server setup instructions`
|
|
109
109
|
);
|
|
110
110
|
memory.command("status").description("Show memory system status").action(async () => {
|
|
111
|
-
const { memoryStatusCommand } = await import("./memory-
|
|
111
|
+
const { memoryStatusCommand } = await import("./memory-RLZR7XQY.js");
|
|
112
112
|
await memoryStatusCommand();
|
|
113
113
|
});
|
|
114
114
|
memory.command("search").description("Search memories by query").argument("<query>", "search query").action(async (query) => {
|
|
115
|
-
const { memorySearchCommand } = await import("./memory-
|
|
115
|
+
const { memorySearchCommand } = await import("./memory-RLZR7XQY.js");
|
|
116
116
|
await memorySearchCommand(query);
|
|
117
117
|
});
|
|
118
118
|
memory.command("list").description(
|
|
119
119
|
"List memories in current project, or all projects with --projects"
|
|
120
120
|
).option("--projects", "List all project databases with size and metadata").action(async (opts) => {
|
|
121
|
-
const { memoryListCommand } = await import("./memory-
|
|
121
|
+
const { memoryListCommand } = await import("./memory-RLZR7XQY.js");
|
|
122
122
|
await memoryListCommand(opts);
|
|
123
123
|
});
|
|
124
124
|
memory.command("init").description(
|
|
125
125
|
"Initialize memory system \u2014 interactive wizard (first time) or config menu"
|
|
126
126
|
).action(async () => {
|
|
127
|
-
const { memoryInitCommand } = await import("./memory-
|
|
127
|
+
const { memoryInitCommand } = await import("./memory-RLZR7XQY.js");
|
|
128
128
|
await memoryInitCommand();
|
|
129
129
|
});
|
|
130
130
|
memory.command("config").description("Configure memory system settings").action(async () => {
|
|
131
|
-
const { memoryConfigCommand } = await import("./memory-
|
|
131
|
+
const { memoryConfigCommand } = await import("./memory-RLZR7XQY.js");
|
|
132
132
|
await memoryConfigCommand();
|
|
133
133
|
});
|
|
134
134
|
memory.command("start-daemon").description("Start the memory daemon (Tier 2 \u2014 MiniSearch)").action(async () => {
|
|
135
|
-
const { memoryStartDaemonCommand } = await import("./memory-
|
|
135
|
+
const { memoryStartDaemonCommand } = await import("./memory-RLZR7XQY.js");
|
|
136
136
|
await memoryStartDaemonCommand();
|
|
137
137
|
});
|
|
138
138
|
memory.command("stop-daemon").description("Stop the memory daemon").action(async () => {
|
|
139
|
-
const { memoryStopDaemonCommand } = await import("./memory-
|
|
139
|
+
const { memoryStopDaemonCommand } = await import("./memory-RLZR7XQY.js");
|
|
140
140
|
await memoryStopDaemonCommand();
|
|
141
141
|
});
|
|
142
142
|
memory.command("rebuild").description("Rebuild the daemon search index").action(async () => {
|
|
143
|
-
const { memoryRebuildCommand } = await import("./memory-
|
|
143
|
+
const { memoryRebuildCommand } = await import("./memory-RLZR7XQY.js");
|
|
144
144
|
await memoryRebuildCommand();
|
|
145
145
|
});
|
|
146
146
|
memory.command("mcp").description("Show MCP server setup instructions").action(async () => {
|
|
147
|
-
const { memoryMcpCommand } = await import("./memory-
|
|
147
|
+
const { memoryMcpCommand } = await import("./memory-RLZR7XQY.js");
|
|
148
148
|
await memoryMcpCommand();
|
|
149
149
|
});
|
|
150
150
|
memory.command("rm").description("Remove a project database").option("--project-id <id>", "Project ID to remove").option("--all", "Remove all project databases").option(
|
|
@@ -152,7 +152,7 @@ memory.command("rm").description("Remove a project database").option("--project-
|
|
|
152
152
|
"Remove orphaned projects (source dir missing or 0 memories)"
|
|
153
153
|
).action(
|
|
154
154
|
async (opts) => {
|
|
155
|
-
const { memoryRmCommand } = await import("./memory-
|
|
155
|
+
const { memoryRmCommand } = await import("./memory-RLZR7XQY.js");
|
|
156
156
|
await memoryRmCommand(opts);
|
|
157
157
|
}
|
|
158
158
|
);
|
|
@@ -1,12 +1,16 @@
|
|
|
1
|
+
import {
|
|
2
|
+
editCodex
|
|
3
|
+
} from "./chunk-CXG6I6ZF.js";
|
|
1
4
|
import {
|
|
2
5
|
ensureMemoryBuilt,
|
|
3
6
|
isMemoryInitialized,
|
|
4
7
|
memoryInitWizard
|
|
5
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-5X3JZKYW.js";
|
|
9
|
+
import "./chunk-AYIREX73.js";
|
|
6
10
|
import {
|
|
7
11
|
memoryConfigMenu
|
|
8
|
-
} from "./chunk-
|
|
9
|
-
import "./chunk-
|
|
12
|
+
} from "./chunk-3UIH5U3Y.js";
|
|
13
|
+
import "./chunk-K6EDQGDN.js";
|
|
10
14
|
import {
|
|
11
15
|
getLibPath
|
|
12
16
|
} from "./chunk-RZRT7NGT.js";
|
|
@@ -741,6 +745,8 @@ async function initMenu(gitAvailable) {
|
|
|
741
745
|
globalCfg,
|
|
742
746
|
localCfg
|
|
743
747
|
);
|
|
748
|
+
const codexScope = getScopeLabel("codex", globalCfg, localCfg);
|
|
749
|
+
const codexEnabled = getMergedValue("codex", globalCfg, localCfg)?.enabled;
|
|
744
750
|
const projectRules = getExistingRules(claudeLocalSettingsPath());
|
|
745
751
|
const userRules = getExistingRules(claudeSettingsPath());
|
|
746
752
|
const allRules = getAllRules();
|
|
@@ -792,6 +798,11 @@ async function initMenu(gitAvailable) {
|
|
|
792
798
|
value: "autoApprove",
|
|
793
799
|
description: " Auto-approve safe tool calls, block destructive ones, prompt for ambiguous"
|
|
794
800
|
},
|
|
801
|
+
{
|
|
802
|
+
name: `Codex cross-engine review ${formatScopeLabel(codexScope)}${codexEnabled !== void 0 ? ` (${codexEnabled ? "enabled" : "disabled"})` : ""}`,
|
|
803
|
+
value: "codex",
|
|
804
|
+
description: " Enable OpenAI Codex as 6th specialist in /cf-review (opt-in)"
|
|
805
|
+
},
|
|
795
806
|
{
|
|
796
807
|
name: `Permissions (${permissionStatus} rules)`,
|
|
797
808
|
value: "permissions",
|
|
@@ -860,6 +871,9 @@ async function initMenu(gitAvailable) {
|
|
|
860
871
|
}
|
|
861
872
|
break;
|
|
862
873
|
}
|
|
874
|
+
case "codex":
|
|
875
|
+
await editCodex(globalCfg, localCfg);
|
|
876
|
+
break;
|
|
863
877
|
case "permissions": {
|
|
864
878
|
const learnCfg = localCfg?.learn ?? globalCfg?.learn;
|
|
865
879
|
const learnOutputDir = learnCfg?.outputDir || `${docsDir}/learn`;
|
|
@@ -1000,7 +1014,7 @@ async function initCommand() {
|
|
|
1000
1014
|
console.log();
|
|
1001
1015
|
log.congrats("Setup complete!");
|
|
1002
1016
|
log.dim(
|
|
1003
|
-
"Available commands: /cf-ask, /cf-plan, /cf-fix, /cf-commit, /cf-review, /cf-review-out, /cf-review-in, /cf-ship, /cf-optimize, /cf-scan, /cf-remember, /cf-learn, /cf-teach, /cf-research, /cf-session, /cf-warm, /cf-help"
|
|
1017
|
+
"Available commands: /cf-ask, /cf-plan, /cf-fix, /cf-commit, /cf-review, /cf-review-codex, /cf-review-out, /cf-review-in, /cf-ship, /cf-optimize, /cf-scan, /cf-remember, /cf-learn, /cf-teach, /cf-research, /cf-session, /cf-warm, /cf-help"
|
|
1004
1018
|
);
|
|
1005
1019
|
}
|
|
1006
1020
|
export {
|
|
@@ -1,14 +1,21 @@
|
|
|
1
1
|
import {
|
|
2
|
-
|
|
2
|
+
checkLearnMcpHealth,
|
|
3
|
+
checkMemoryMcpHealth,
|
|
4
|
+
listMdFilesRecursive
|
|
5
|
+
} from "./chunk-TVAY4OQL.js";
|
|
6
|
+
import {
|
|
3
7
|
ensureMemoryBuilt,
|
|
4
|
-
printMemoryMcpConfig
|
|
8
|
+
printMemoryMcpConfig
|
|
9
|
+
} from "./chunk-5X3JZKYW.js";
|
|
10
|
+
import {
|
|
11
|
+
detectMemoryMcpState,
|
|
5
12
|
warnStaleMcpJson
|
|
6
|
-
} from "./chunk-
|
|
7
|
-
import "./chunk-
|
|
13
|
+
} from "./chunk-AYIREX73.js";
|
|
14
|
+
import "./chunk-3UIH5U3Y.js";
|
|
8
15
|
import {
|
|
9
16
|
resolveDocsDir,
|
|
10
17
|
resolveMemoryDir
|
|
11
|
-
} from "./chunk-
|
|
18
|
+
} from "./chunk-K6EDQGDN.js";
|
|
12
19
|
import {
|
|
13
20
|
getLibPath
|
|
14
21
|
} from "./chunk-RZRT7NGT.js";
|
|
@@ -22,7 +29,9 @@ import {
|
|
|
22
29
|
log,
|
|
23
30
|
printBanner
|
|
24
31
|
} from "./chunk-NREZK463.js";
|
|
25
|
-
import
|
|
32
|
+
import {
|
|
33
|
+
readJson
|
|
34
|
+
} from "./chunk-5UVDWG5L.js";
|
|
26
35
|
|
|
27
36
|
// src/commands/mcp.ts
|
|
28
37
|
import { existsSync, readdirSync } from "fs";
|
|
@@ -39,10 +48,28 @@ function countMdFiles(dir) {
|
|
|
39
48
|
}
|
|
40
49
|
return count;
|
|
41
50
|
}
|
|
51
|
+
function printHealthSection(result) {
|
|
52
|
+
console.log(chalk.dim("\u2500\u2500\u2500 Health Check \u2500\u2500\u2500"));
|
|
53
|
+
for (const check of result.checks) {
|
|
54
|
+
if (check.ok) {
|
|
55
|
+
console.log(chalk.green(` \u2713 ${check.label}`));
|
|
56
|
+
} else if (check.warn) {
|
|
57
|
+
const detail = check.detail ? `: ${check.detail}` : "";
|
|
58
|
+
console.log(chalk.yellow(` \u26A0 ${check.label}${detail}`));
|
|
59
|
+
} else {
|
|
60
|
+
const detail = check.detail ? `: ${check.detail}` : "";
|
|
61
|
+
console.log(chalk.red(` \u2717 ${check.label}${detail}`));
|
|
62
|
+
if (check.fix) {
|
|
63
|
+
console.log(chalk.dim(` \u2192 ${check.fix}`));
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
console.log();
|
|
68
|
+
}
|
|
42
69
|
async function mcpCommand(path) {
|
|
43
70
|
warnStaleMcpJson(resolveMemoryDir());
|
|
44
71
|
const docsDir = resolveDocsDir(path);
|
|
45
|
-
const
|
|
72
|
+
const learnMcpDir = getLibPath("learn-mcp");
|
|
46
73
|
if (!existsSync(docsDir)) {
|
|
47
74
|
log.error(`Docs folder not found: ${docsDir}`);
|
|
48
75
|
log.dim("Run /cf-learn first to generate some docs.");
|
|
@@ -57,18 +84,18 @@ async function mcpCommand(path) {
|
|
|
57
84
|
printBanner("\u{1F4DA} Learn MCP");
|
|
58
85
|
log.info(`Docs folder: ${chalk.cyan(docsDir)}`);
|
|
59
86
|
log.info(`Found: ${chalk.green(docCount)} docs`);
|
|
60
|
-
if (!existsSync(join(
|
|
87
|
+
if (!existsSync(join(learnMcpDir, "node_modules"))) {
|
|
61
88
|
log.step("Installing MCP server dependencies (one-time setup)...");
|
|
62
|
-
const result = run("npm", ["install", "--silent"], { cwd:
|
|
89
|
+
const result = run("npm", ["install", "--silent"], { cwd: learnMcpDir });
|
|
63
90
|
if (result === null) {
|
|
64
91
|
log.error("Failed to install dependencies");
|
|
65
92
|
process.exit(1);
|
|
66
93
|
}
|
|
67
94
|
log.success("Done.");
|
|
68
95
|
}
|
|
69
|
-
if (!existsSync(join(
|
|
96
|
+
if (!existsSync(join(learnMcpDir, "dist"))) {
|
|
70
97
|
log.step("Building MCP server...");
|
|
71
|
-
const result = run("npm", ["run", "build", "--silent"], { cwd:
|
|
98
|
+
const result = run("npm", ["run", "build", "--silent"], { cwd: learnMcpDir });
|
|
72
99
|
if (result === null) {
|
|
73
100
|
log.error("Failed to build MCP server");
|
|
74
101
|
process.exit(1);
|
|
@@ -96,9 +123,19 @@ async function mcpCommand(path) {
|
|
|
96
123
|
"Available tools & resources: https://cf.dinhanhthi.com/docs/cli/cf-mcp/"
|
|
97
124
|
);
|
|
98
125
|
console.log();
|
|
99
|
-
|
|
126
|
+
const localMcpPath = join(process.cwd(), ".mcp.json");
|
|
127
|
+
const learnMcpDistPath = join(learnMcpDir, "dist", "index.js");
|
|
128
|
+
const learnHealth = await checkLearnMcpHealth({
|
|
129
|
+
readMcpJson: () => readJson(localMcpPath),
|
|
130
|
+
pathExists: existsSync,
|
|
131
|
+
listMdFiles: listMdFilesRecursive,
|
|
132
|
+
docsDir,
|
|
133
|
+
learnMcpDistPath
|
|
134
|
+
});
|
|
135
|
+
printHealthSection(learnHealth);
|
|
136
|
+
await printMemoryMcp();
|
|
100
137
|
}
|
|
101
|
-
function printMemoryMcp() {
|
|
138
|
+
async function printMemoryMcp() {
|
|
102
139
|
const memoryDir = resolveMemoryDir();
|
|
103
140
|
let mcpDir;
|
|
104
141
|
try {
|
|
@@ -115,8 +152,24 @@ function printMemoryMcp() {
|
|
|
115
152
|
log.info(`Memory dir: ${chalk.cyan(memoryDir)}`);
|
|
116
153
|
console.log();
|
|
117
154
|
printMemoryMcpConfig(memoryDir);
|
|
155
|
+
const localMcpPath = join(process.cwd(), ".mcp.json");
|
|
156
|
+
const memoryDistPath = join(mcpDir, "dist", "index.js");
|
|
157
|
+
let isDaemonRunning = async () => false;
|
|
158
|
+
try {
|
|
159
|
+
const proc = await import(join(mcpDir, "dist/daemon/process.js"));
|
|
160
|
+
isDaemonRunning = proc.isDaemonRunning;
|
|
161
|
+
} catch {
|
|
162
|
+
}
|
|
163
|
+
const memoryHealth = await checkMemoryMcpHealth({
|
|
164
|
+
readMcpJson: () => readJson(localMcpPath),
|
|
165
|
+
pathExists: existsSync,
|
|
166
|
+
isDaemonRunning,
|
|
167
|
+
memoryDistPath
|
|
168
|
+
});
|
|
169
|
+
printHealthSection(memoryHealth);
|
|
118
170
|
}
|
|
119
171
|
export {
|
|
120
172
|
detectMemoryMcpState,
|
|
121
|
-
mcpCommand
|
|
173
|
+
mcpCommand,
|
|
174
|
+
printHealthSection
|
|
122
175
|
};
|
|
@@ -13,9 +13,10 @@ import {
|
|
|
13
13
|
memoryStatusCommand,
|
|
14
14
|
memoryStopDaemonCommand,
|
|
15
15
|
printMemoryMcpConfig
|
|
16
|
-
} from "./chunk-
|
|
17
|
-
import "./chunk-
|
|
18
|
-
import "./chunk-
|
|
16
|
+
} from "./chunk-5X3JZKYW.js";
|
|
17
|
+
import "./chunk-AYIREX73.js";
|
|
18
|
+
import "./chunk-3UIH5U3Y.js";
|
|
19
|
+
import "./chunk-K6EDQGDN.js";
|
|
19
20
|
import "./chunk-RZRT7NGT.js";
|
|
20
21
|
import "./chunk-OYPOADCX.js";
|
|
21
22
|
import "./chunk-Z5Q533UG.js";
|