coding-friend-cli 1.37.0 → 1.38.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 +7 -2
- package/dist/{chunk-GZT3YPAP.js → chunk-32UAW3NX.js} +1 -1
- package/dist/chunk-5QCGEPQO.js +238 -0
- package/dist/{chunk-YIXAQ4R2.js → chunk-6NXZKAOV.js} +1 -1
- package/dist/{chunk-676YCW6T.js → chunk-776ZY62N.js} +99 -7
- package/dist/{chunk-DP6DFQ4G.js → chunk-BXSCYGWE.js} +2 -2
- package/dist/{chunk-W6D3J5DX.js → chunk-CGEMETKQ.js} +8 -6
- package/dist/{chunk-XEKQW4RN.js → chunk-ETTZRJ55.js} +1 -1
- package/dist/{chunk-IKLMHJH6.js → chunk-GIH6E7GJ.js} +62 -1
- package/dist/{chunk-6S7352JG.js → chunk-ONG4KRYP.js} +1 -1
- package/dist/{chunk-X2H7JJMR.js → chunk-PLSJW7QJ.js} +29 -1
- package/dist/{chunk-O5ZKLFSL.js → chunk-RCE4OWNX.js} +1 -1
- package/dist/{chunk-PI4P34KF.js → chunk-RFNKPAJW.js} +3 -1
- package/dist/{clean-WEYQZB34.js → clean-YWEMYDE5.js} +2 -2
- package/dist/{config-ZMR2AOBB.js → config-NG7SICM4.js} +12 -24
- package/dist/{dev-IGHMMEP4.js → dev-PTBNMKZT.js} +3 -3
- package/dist/{disable-HL5OEURT.js → disable-MO7AXYYF.js} +20 -3
- package/dist/{enable-CZIOCKKJ.js → enable-4GXLQEHD.js} +20 -3
- package/dist/{guide-CO2V4ZLZ.js → guide-HCR43LJN.js} +1 -1
- package/dist/{host-SSRI4JZV.js → host-EV6QIVTI.js} +2 -2
- package/dist/index.js +59 -47
- package/dist/{init-6MFQWPOP.js → init-KYE5CJMW.js} +115 -12
- package/dist/{install-37NN4CQH.js → install-XAFQY5YF.js} +79 -6
- package/dist/{learn-OPH3CI67.js → learn-MC4VDHZH.js} +2 -2
- package/dist/{mcp-DWKVMNC7.js → mcp-FV6YX3ZT.js} +7 -7
- package/dist/{mcp-serve-5GODJ4DJ.js → mcp-serve-PXREYQRZ.js} +2 -2
- package/dist/{mcp-serve-learn-OF3ZJUJC.js → mcp-serve-learn-UVXIDOEZ.js} +1 -1
- package/dist/{memory-TRDLADUM.js → memory-DF7OUOTV.js} +6 -6
- package/dist/{permission-I3OFJUYA.js → permission-VISPGMUK.js} +38 -4
- package/dist/{permissions-J46VP6US.js → permissions-2SBACQHV.js} +2 -2
- package/dist/{session-S4WKA4P5.js → session-QK6OZO3X.js} +2 -2
- package/dist/{status-YZEOUX46.js → status-FVFTJ6GS.js} +11 -10
- package/dist/{statusline-KKFJPTI7.js → statusline-AOZHEQSV.js} +2 -2
- package/dist/{uninstall-AVA3J554.js → uninstall-MF6FRUBO.js} +51 -4
- package/dist/{update-Q3ACCK4Z.js → update-QNUCSDSA.js} +6 -5
- package/dist/{update-check-25SCLHZO.js → update-check-7LKJLJPM.js} +1 -1
- package/lib/cf-memory/src/lib/claude-md.ts +82 -70
- package/lib/cf-memory/src/lib/status-frame.ts +2 -2
- package/lib/cf-memory/src/tools/delete.ts +1 -1
- package/lib/cf-memory/src/tools/store.ts +6 -6
- package/lib/cf-memory/src/tools/update.ts +5 -11
- package/package.json +32 -4
- package/lib/cf-memory/README.md +0 -317
- package/lib/cf-memory/src/__tests__/claude-md.test.ts +0 -343
- package/lib/cf-memory/src/__tests__/daemon-client.test.ts +0 -146
- package/lib/cf-memory/src/__tests__/daemon-e2e.test.ts +0 -223
- package/lib/cf-memory/src/__tests__/daemon.test.ts +0 -407
- package/lib/cf-memory/src/__tests__/dedup.test.ts +0 -103
- package/lib/cf-memory/src/__tests__/embeddings.test.ts +0 -292
- package/lib/cf-memory/src/__tests__/lazy-install.test.ts +0 -210
- package/lib/cf-memory/src/__tests__/markdown-backend.test.ts +0 -450
- package/lib/cf-memory/src/__tests__/migration.test.ts +0 -255
- package/lib/cf-memory/src/__tests__/migrations.test.ts +0 -288
- package/lib/cf-memory/src/__tests__/minisearch-backend.test.ts +0 -262
- package/lib/cf-memory/src/__tests__/ollama.test.ts +0 -48
- package/lib/cf-memory/src/__tests__/project-id.test.ts +0 -43
- package/lib/cf-memory/src/__tests__/schema.test.ts +0 -128
- package/lib/cf-memory/src/__tests__/search.test.ts +0 -115
- package/lib/cf-memory/src/__tests__/sqlite-lazy-db.test.ts +0 -213
- package/lib/cf-memory/src/__tests__/status-frame.test.ts +0 -136
- package/lib/cf-memory/src/__tests__/temporal-decay.test.ts +0 -54
- package/lib/cf-memory/src/__tests__/tier.test.ts +0 -388
- package/lib/cf-memory/src/__tests__/tools.test.ts +0 -83
- package/lib/cf-memory/vitest.config.ts +0 -7
- package/lib/learn-host/.prettierignore +0 -4
- package/lib/learn-host/.prettierrc +0 -8
- package/lib/learn-host/README.md +0 -113
- package/lib/learn-host/eslint.config.mjs +0 -6
- package/lib/learn-mcp/README.md +0 -187
- package/lib/learn-mcp/src/__tests__/docs.test.ts +0 -117
- package/lib/learn-mcp/vitest.config.ts +0 -8
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# coding-friend-cli
|
|
2
2
|
|
|
3
|
-
CLI companion for the [coding-friend](https://github.com/dinhanhthi/coding-friend) plugin — a lean toolkit for disciplined engineering workflows with Claude Code.
|
|
3
|
+
CLI companion for the [coding-friend](https://github.com/dinhanhthi/coding-friend) plugin — a lean toolkit for disciplined engineering workflows with Claude Code and Codex CLI.
|
|
4
4
|
|
|
5
5
|
## Plugin without the CLI?
|
|
6
6
|
|
|
@@ -23,7 +23,7 @@ Everything else (every skill, every agent, every hook except the three OPTIONAL
|
|
|
23
23
|
|
|
24
24
|
- Node.js >= 20
|
|
25
25
|
- npm (included with Node.js, but on some Linux distros you may need to install it separately)
|
|
26
|
-
- The [coding-friend plugin](https://github.com/dinhanhthi/coding-friend) installed in Claude Code
|
|
26
|
+
- The [coding-friend plugin](https://github.com/dinhanhthi/coding-friend) installed in Claude Code or Codex CLI
|
|
27
27
|
|
|
28
28
|
### Additional requirements for `cf memory init`
|
|
29
29
|
|
|
@@ -56,12 +56,14 @@ cf install --user # Install at user scope (all projects)
|
|
|
56
56
|
cf install --global # Same as --user
|
|
57
57
|
cf install --project # Install at project scope (shared via git)
|
|
58
58
|
cf install --local # Install at local scope (this machine only)
|
|
59
|
+
cf install --agent codex # Register/install Coding Friend for Codex CLI
|
|
59
60
|
# 💡 Safe to run multiple times (idempotent).
|
|
60
61
|
cf uninstall # Uninstall plugin (interactive scope chooser)
|
|
61
62
|
cf uninstall --user # Uninstall from user scope (full cleanup)
|
|
62
63
|
cf uninstall --global # Same as --user
|
|
63
64
|
cf uninstall --project # Uninstall from project scope only
|
|
64
65
|
cf uninstall --local # Uninstall from local scope only
|
|
66
|
+
cf uninstall --agent codex # Disable Coding Friend in Codex CLI
|
|
65
67
|
# 💡 Interactive — asks for confirmation before acting.
|
|
66
68
|
cf disable # Disable plugin (interactive scope chooser)
|
|
67
69
|
cf disable --user # Disable at user scope (all projects)
|
|
@@ -75,6 +77,7 @@ cf enable --global # Same as --user
|
|
|
75
77
|
cf enable --project # Enable at project scope
|
|
76
78
|
cf enable --local # Enable at local scope
|
|
77
79
|
cf init # Initialize workspace (interactive)
|
|
80
|
+
cf init --agent codex # Initialize workspace for Codex CLI
|
|
78
81
|
# 💡 You can run this anywhere, anytime.
|
|
79
82
|
cf config # Manage Coding Friend configuration (interactive menu)
|
|
80
83
|
# 💡 Edit docsDir, language, learn settings, and more.
|
|
@@ -89,6 +92,7 @@ cf permission # Manage Claude Code permission rules (interactive)
|
|
|
89
92
|
cf permission --all # Apply all recommended permissions without prompts
|
|
90
93
|
cf permission --user # Save to user-level settings (~/.claude/settings.json)
|
|
91
94
|
cf permission --project # Save to project-level settings (.claude/settings.local.json)
|
|
95
|
+
cf permission --agent codex --enable-auto-approve
|
|
92
96
|
cf permission --all --user # Apply all recommended permissions to user settings
|
|
93
97
|
cf statusline # Setup coding-friend statusline
|
|
94
98
|
cf update # Update plugin + CLI + statusline
|
|
@@ -97,6 +101,7 @@ cf update # Update plugin + CLI + statusline
|
|
|
97
101
|
cf update --cli # Update only the CLI (npm package)
|
|
98
102
|
cf update --plugin # Update only the Claude Code plugin
|
|
99
103
|
cf update --statusline # Update only the statusline
|
|
104
|
+
cf update --agent codex # Update Codex marketplace/plugin cache
|
|
100
105
|
cf update --project # Update plugin at project scope
|
|
101
106
|
cf update --local # Update plugin at local scope
|
|
102
107
|
cf dev on [path] # Switch to local plugin source for development
|
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
import {
|
|
2
|
+
codexAgentsDir,
|
|
3
|
+
codexConfigTomlPath,
|
|
4
|
+
codexInstalledPluginsPath
|
|
5
|
+
} from "./chunk-PLSJW7QJ.js";
|
|
6
|
+
|
|
7
|
+
// src/lib/codex-config.ts
|
|
8
|
+
import {
|
|
9
|
+
copyFileSync,
|
|
10
|
+
existsSync,
|
|
11
|
+
mkdirSync,
|
|
12
|
+
readdirSync,
|
|
13
|
+
readFileSync,
|
|
14
|
+
rmSync,
|
|
15
|
+
writeFileSync
|
|
16
|
+
} from "fs";
|
|
17
|
+
import { basename, dirname, join, resolve } from "path";
|
|
18
|
+
var CODEX_MARKETPLACE_NAME = "coding-friend-marketplace";
|
|
19
|
+
var CODEX_PLUGIN_NAME = "coding-friend";
|
|
20
|
+
var CODEX_PLUGIN_ID = `${CODEX_PLUGIN_NAME}@${CODEX_MARKETPLACE_NAME}`;
|
|
21
|
+
var CODEX_PLUGIN_TABLE = `plugins."${CODEX_PLUGIN_ID}"`;
|
|
22
|
+
var CODEX_MEMORY_MCP_TABLE = "mcp_servers.coding-friend-memory";
|
|
23
|
+
function readToml(filePath) {
|
|
24
|
+
try {
|
|
25
|
+
return readFileSync(filePath, "utf8");
|
|
26
|
+
} catch {
|
|
27
|
+
return "";
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
function writeToml(filePath, content) {
|
|
31
|
+
mkdirSync(dirname(filePath), { recursive: true });
|
|
32
|
+
writeFileSync(filePath, content.endsWith("\n") ? content : `${content}
|
|
33
|
+
`);
|
|
34
|
+
}
|
|
35
|
+
function tomlString(value) {
|
|
36
|
+
return JSON.stringify(value);
|
|
37
|
+
}
|
|
38
|
+
function tableHeader(tableName) {
|
|
39
|
+
return `[${tableName}]`;
|
|
40
|
+
}
|
|
41
|
+
function findTableRange(lines, header) {
|
|
42
|
+
const start = lines.findIndex((line) => line.trim() === header);
|
|
43
|
+
if (start === -1) return null;
|
|
44
|
+
let end = lines.length;
|
|
45
|
+
for (let index = start + 1; index < lines.length; index += 1) {
|
|
46
|
+
if (/^\s*\[[^\]]+\]\s*$/.test(lines[index])) {
|
|
47
|
+
end = index;
|
|
48
|
+
break;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
return { start, end };
|
|
52
|
+
}
|
|
53
|
+
function replaceTable(content, tableName, body) {
|
|
54
|
+
const header = tableHeader(tableName);
|
|
55
|
+
const lines = content.split(/\n/);
|
|
56
|
+
const range = findTableRange(lines, header);
|
|
57
|
+
const nextBlock = [header, ...body];
|
|
58
|
+
if (range) {
|
|
59
|
+
lines.splice(range.start, range.end - range.start, ...nextBlock);
|
|
60
|
+
return `${lines.join("\n").replace(/\n*$/, "")}
|
|
61
|
+
`;
|
|
62
|
+
}
|
|
63
|
+
const prefix = content.replace(/\n*$/, "");
|
|
64
|
+
return `${prefix}${prefix ? "\n\n" : ""}${nextBlock.join("\n")}
|
|
65
|
+
`;
|
|
66
|
+
}
|
|
67
|
+
function upsertTableKey(content, tableName, key, valueLine) {
|
|
68
|
+
const header = tableHeader(tableName);
|
|
69
|
+
const lines = content.split(/\n/);
|
|
70
|
+
const range = findTableRange(lines, header);
|
|
71
|
+
if (!range) {
|
|
72
|
+
return replaceTable(content, tableName, [valueLine]);
|
|
73
|
+
}
|
|
74
|
+
const keyPattern = new RegExp(`^\\s*${key}\\s*=`);
|
|
75
|
+
let replaced = false;
|
|
76
|
+
for (let index = range.start + 1; index < range.end; index += 1) {
|
|
77
|
+
if (keyPattern.test(lines[index])) {
|
|
78
|
+
lines[index] = valueLine;
|
|
79
|
+
replaced = true;
|
|
80
|
+
break;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
if (!replaced) {
|
|
84
|
+
lines.splice(range.end, 0, valueLine);
|
|
85
|
+
}
|
|
86
|
+
return `${lines.join("\n").replace(/\n*$/, "")}
|
|
87
|
+
`;
|
|
88
|
+
}
|
|
89
|
+
function getTable(content, tableName) {
|
|
90
|
+
const lines = content.split(/\n/);
|
|
91
|
+
const range = findTableRange(lines, tableHeader(tableName));
|
|
92
|
+
if (!range) return [];
|
|
93
|
+
return lines.slice(range.start + 1, range.end);
|
|
94
|
+
}
|
|
95
|
+
function isCodexMarketplaceRegistered(configPath = codexConfigTomlPath()) {
|
|
96
|
+
return readToml(configPath).split(/\n/).some(
|
|
97
|
+
(line) => line.trim() === tableHeader(`marketplaces.${CODEX_MARKETPLACE_NAME}`)
|
|
98
|
+
);
|
|
99
|
+
}
|
|
100
|
+
function setCodexPluginEnabled(enabled, configPath = codexConfigTomlPath()) {
|
|
101
|
+
const content = readToml(configPath);
|
|
102
|
+
writeToml(
|
|
103
|
+
configPath,
|
|
104
|
+
upsertTableKey(
|
|
105
|
+
content,
|
|
106
|
+
CODEX_PLUGIN_TABLE,
|
|
107
|
+
"enabled",
|
|
108
|
+
`enabled = ${enabled ? "true" : "false"}`
|
|
109
|
+
)
|
|
110
|
+
);
|
|
111
|
+
}
|
|
112
|
+
function isCodexPluginDisabled(configPath = codexConfigTomlPath()) {
|
|
113
|
+
const table = getTable(readToml(configPath), CODEX_PLUGIN_TABLE);
|
|
114
|
+
return table.some((line) => /^\s*enabled\s*=\s*false\s*(?:#.*)?$/.test(line));
|
|
115
|
+
}
|
|
116
|
+
function removeTable(content, tableName) {
|
|
117
|
+
const lines = content.split(/\n/);
|
|
118
|
+
const range = findTableRange(lines, tableHeader(tableName));
|
|
119
|
+
if (!range) return content;
|
|
120
|
+
let start = range.start;
|
|
121
|
+
while (start > 0 && lines[start - 1].trim() === "") start -= 1;
|
|
122
|
+
lines.splice(start, range.end - start);
|
|
123
|
+
return `${lines.join("\n").replace(/\n*$/, "")}
|
|
124
|
+
`;
|
|
125
|
+
}
|
|
126
|
+
function removeCodexMemoryMcpConfig(configPath = codexConfigTomlPath()) {
|
|
127
|
+
const content = readToml(configPath);
|
|
128
|
+
const next = removeTable(content, CODEX_MEMORY_MCP_TABLE);
|
|
129
|
+
if (next === content) return false;
|
|
130
|
+
writeToml(configPath, next);
|
|
131
|
+
return true;
|
|
132
|
+
}
|
|
133
|
+
function removeDeployedCodexAgents(targetDir = codexAgentsDir()) {
|
|
134
|
+
if (!existsSync(targetDir)) return 0;
|
|
135
|
+
let removed = 0;
|
|
136
|
+
for (const entry of readdirSync(targetDir, { withFileTypes: true })) {
|
|
137
|
+
if (!entry.isFile() || !/^cf-.*\.toml$/.test(entry.name)) continue;
|
|
138
|
+
rmSync(join(targetDir, entry.name));
|
|
139
|
+
removed += 1;
|
|
140
|
+
}
|
|
141
|
+
return removed;
|
|
142
|
+
}
|
|
143
|
+
function writeCodexMemoryMcpConfig(memoryDir, configPath = codexConfigTomlPath()) {
|
|
144
|
+
const content = readToml(configPath);
|
|
145
|
+
writeToml(
|
|
146
|
+
configPath,
|
|
147
|
+
replaceTable(content, CODEX_MEMORY_MCP_TABLE, [
|
|
148
|
+
'command = "npx"',
|
|
149
|
+
`args = ["-y", "coding-friend-cli", "mcp-serve", ${tomlString(memoryDir)}]`
|
|
150
|
+
])
|
|
151
|
+
);
|
|
152
|
+
}
|
|
153
|
+
function writeCodexAgentLimits(configPath = codexConfigTomlPath()) {
|
|
154
|
+
const content = readToml(configPath);
|
|
155
|
+
writeToml(
|
|
156
|
+
configPath,
|
|
157
|
+
upsertTableKey(content, "agents", "max_depth", "max_depth = 2")
|
|
158
|
+
);
|
|
159
|
+
}
|
|
160
|
+
function trustCodexProject(projectPath = process.cwd(), configPath = codexConfigTomlPath()) {
|
|
161
|
+
const tableName = `projects.${tomlString(projectPath)}`;
|
|
162
|
+
const content = readToml(configPath);
|
|
163
|
+
writeToml(
|
|
164
|
+
configPath,
|
|
165
|
+
upsertTableKey(
|
|
166
|
+
content,
|
|
167
|
+
tableName,
|
|
168
|
+
"trust_level",
|
|
169
|
+
'trust_level = "trusted"'
|
|
170
|
+
)
|
|
171
|
+
);
|
|
172
|
+
}
|
|
173
|
+
function generatedCodexAgentsDir(repoRoot = process.cwd()) {
|
|
174
|
+
return resolve(repoRoot, "plugin-codex", "agents");
|
|
175
|
+
}
|
|
176
|
+
function compareVersions(a, b) {
|
|
177
|
+
const left = a.split(".").map((part) => Number.parseInt(part, 10));
|
|
178
|
+
const right = b.split(".").map((part) => Number.parseInt(part, 10));
|
|
179
|
+
for (let index = 0; index < 3; index += 1) {
|
|
180
|
+
const diff = (left[index] || 0) - (right[index] || 0);
|
|
181
|
+
if (diff !== 0) return diff > 0 ? 1 : -1;
|
|
182
|
+
}
|
|
183
|
+
return 0;
|
|
184
|
+
}
|
|
185
|
+
function latestCodexInstalledPluginDir(pluginRoot = codexInstalledPluginsPath()) {
|
|
186
|
+
const directManifest = join(pluginRoot, ".codex-plugin", "plugin.json");
|
|
187
|
+
if (existsSync(directManifest)) return pluginRoot;
|
|
188
|
+
if (!existsSync(pluginRoot)) return null;
|
|
189
|
+
const versions = readdirSync(pluginRoot, { withFileTypes: true }).filter((entry) => entry.isDirectory()).map((entry) => entry.name).filter(
|
|
190
|
+
(version) => existsSync(join(pluginRoot, version, ".codex-plugin", "plugin.json"))
|
|
191
|
+
).sort(compareVersions).reverse();
|
|
192
|
+
return versions[0] ? join(pluginRoot, versions[0]) : null;
|
|
193
|
+
}
|
|
194
|
+
function getCodexInstalledVersion(pluginRoot = codexInstalledPluginsPath()) {
|
|
195
|
+
const pluginDir = latestCodexInstalledPluginDir(pluginRoot);
|
|
196
|
+
if (!pluginDir) return null;
|
|
197
|
+
try {
|
|
198
|
+
const manifest = JSON.parse(
|
|
199
|
+
readFileSync(join(pluginDir, ".codex-plugin", "plugin.json"), "utf8")
|
|
200
|
+
);
|
|
201
|
+
return manifest.version ?? basename(pluginDir);
|
|
202
|
+
} catch {
|
|
203
|
+
return basename(pluginDir);
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
function findCodexAgentSourceDir(repoRoot = process.cwd()) {
|
|
207
|
+
const generated = generatedCodexAgentsDir(repoRoot);
|
|
208
|
+
if (existsSync(generated)) return generated;
|
|
209
|
+
const installedPluginDir = latestCodexInstalledPluginDir();
|
|
210
|
+
const installedAgents = installedPluginDir ? join(installedPluginDir, "agents") : null;
|
|
211
|
+
return installedAgents && existsSync(installedAgents) ? installedAgents : null;
|
|
212
|
+
}
|
|
213
|
+
function deployCodexAgents(sourceDir, targetDir = codexAgentsDir()) {
|
|
214
|
+
if (!existsSync(sourceDir)) return 0;
|
|
215
|
+
mkdirSync(targetDir, { recursive: true });
|
|
216
|
+
let copied = 0;
|
|
217
|
+
for (const entry of readdirSync(sourceDir, { withFileTypes: true })) {
|
|
218
|
+
if (!entry.isFile() || !entry.name.endsWith(".toml")) continue;
|
|
219
|
+
copyFileSync(join(sourceDir, entry.name), join(targetDir, entry.name));
|
|
220
|
+
copied += 1;
|
|
221
|
+
}
|
|
222
|
+
return copied;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
export {
|
|
226
|
+
CODEX_MARKETPLACE_NAME,
|
|
227
|
+
isCodexMarketplaceRegistered,
|
|
228
|
+
setCodexPluginEnabled,
|
|
229
|
+
isCodexPluginDisabled,
|
|
230
|
+
removeCodexMemoryMcpConfig,
|
|
231
|
+
removeDeployedCodexAgents,
|
|
232
|
+
writeCodexMemoryMcpConfig,
|
|
233
|
+
writeCodexAgentLimits,
|
|
234
|
+
trustCodexProject,
|
|
235
|
+
getCodexInstalledVersion,
|
|
236
|
+
findCodexAgentSourceDir,
|
|
237
|
+
deployCodexAgents
|
|
238
|
+
};
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
2
|
ensureStatusline,
|
|
3
3
|
getInstalledVersion
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-ETTZRJ55.js";
|
|
5
5
|
import {
|
|
6
6
|
ensureShellCompletion
|
|
7
7
|
} from "./chunk-667NMPN4.js";
|
|
@@ -9,10 +9,15 @@ import {
|
|
|
9
9
|
isMemoryMcpRegistered,
|
|
10
10
|
registerMemoryMcp,
|
|
11
11
|
removeMemoryMcpEntry
|
|
12
|
-
} from "./chunk-
|
|
12
|
+
} from "./chunk-BXSCYGWE.js";
|
|
13
13
|
import {
|
|
14
|
+
CODEX_MARKETPLACE_NAME,
|
|
15
|
+
getCodexInstalledVersion
|
|
16
|
+
} from "./chunk-5QCGEPQO.js";
|
|
17
|
+
import {
|
|
18
|
+
resolveHostFlags,
|
|
14
19
|
resolveScope
|
|
15
|
-
} from "./chunk-
|
|
20
|
+
} from "./chunk-GIH6E7GJ.js";
|
|
16
21
|
import {
|
|
17
22
|
commandExists,
|
|
18
23
|
run,
|
|
@@ -24,7 +29,7 @@ import {
|
|
|
24
29
|
} from "./chunk-5UVDWG5L.js";
|
|
25
30
|
import {
|
|
26
31
|
claudeSettingsPath
|
|
27
|
-
} from "./chunk-
|
|
32
|
+
} from "./chunk-PLSJW7QJ.js";
|
|
28
33
|
import {
|
|
29
34
|
log,
|
|
30
35
|
printBanner
|
|
@@ -94,6 +99,11 @@ function getStatuslineVersion() {
|
|
|
94
99
|
return match?.[1]?.replace(/^v/, "") ?? null;
|
|
95
100
|
}
|
|
96
101
|
async function updateCommand(opts) {
|
|
102
|
+
const { host } = resolveHostFlags(opts);
|
|
103
|
+
if (host === "codex") {
|
|
104
|
+
await updateCodexCommand(opts);
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
97
107
|
const updateAll = !opts.cli && !opts.plugin && !opts.statusline;
|
|
98
108
|
const doCli = updateAll || !!opts.cli;
|
|
99
109
|
const doPlugin = updateAll || !!opts.plugin;
|
|
@@ -254,7 +264,7 @@ async function updateCommand(opts) {
|
|
|
254
264
|
}
|
|
255
265
|
}
|
|
256
266
|
if (doCli) {
|
|
257
|
-
const { refreshMemoryAfterUpdate } = await import("./memory-
|
|
267
|
+
const { refreshMemoryAfterUpdate } = await import("./memory-DF7OUOTV.js");
|
|
258
268
|
await refreshMemoryAfterUpdate();
|
|
259
269
|
}
|
|
260
270
|
if (!isMemoryMcpRegistered()) {
|
|
@@ -263,9 +273,13 @@ async function updateCommand(opts) {
|
|
|
263
273
|
const migrationResult = removeMemoryMcpEntry();
|
|
264
274
|
if (migrationResult.removed) {
|
|
265
275
|
if (migrationResult.fileDeleted) {
|
|
266
|
-
log.success(
|
|
276
|
+
log.success(
|
|
277
|
+
"Removed legacy project-scope coding-friend-memory from .mcp.json (file deleted \u2014 was empty)."
|
|
278
|
+
);
|
|
267
279
|
} else {
|
|
268
|
-
log.success(
|
|
280
|
+
log.success(
|
|
281
|
+
"Removed legacy project-scope coding-friend-memory from .mcp.json"
|
|
282
|
+
);
|
|
269
283
|
}
|
|
270
284
|
}
|
|
271
285
|
if (doStatusline) {
|
|
@@ -278,6 +292,84 @@ async function updateCommand(opts) {
|
|
|
278
292
|
console.log();
|
|
279
293
|
log.dim("Restart Claude Code (or start a new session) to see changes.");
|
|
280
294
|
}
|
|
295
|
+
async function updateCodexCommand(opts) {
|
|
296
|
+
const updateAll = !opts.cli && !opts.plugin && !opts.statusline;
|
|
297
|
+
const doCli = updateAll || !!opts.cli;
|
|
298
|
+
const doPlugin = updateAll || !!opts.plugin;
|
|
299
|
+
printBanner("\u2728 Coding Friend Codex Update \u2728");
|
|
300
|
+
console.log();
|
|
301
|
+
if (doPlugin) {
|
|
302
|
+
const beforeVersion = getCodexInstalledVersion();
|
|
303
|
+
console.log(
|
|
304
|
+
` Plugin ${beforeVersion ? beforeVersion : chalk.dim("not installed")}`
|
|
305
|
+
);
|
|
306
|
+
if (!commandExists("codex")) {
|
|
307
|
+
log.error("Codex CLI not found. Cannot update Codex plugin.");
|
|
308
|
+
} else {
|
|
309
|
+
log.step("Updating Codex marketplace...");
|
|
310
|
+
const result = runWithStderr("codex", [
|
|
311
|
+
"plugin",
|
|
312
|
+
"marketplace",
|
|
313
|
+
"upgrade",
|
|
314
|
+
CODEX_MARKETPLACE_NAME
|
|
315
|
+
]);
|
|
316
|
+
if (result.exitCode !== 0) {
|
|
317
|
+
log.error(
|
|
318
|
+
`Codex marketplace upgrade failed. Try manually: codex plugin marketplace upgrade ${CODEX_MARKETPLACE_NAME}`
|
|
319
|
+
);
|
|
320
|
+
if (result.stderr) log.dim(`stderr: ${result.stderr}`);
|
|
321
|
+
} else {
|
|
322
|
+
const afterVersion = getCodexInstalledVersion();
|
|
323
|
+
if (afterVersion && afterVersion !== beforeVersion) {
|
|
324
|
+
log.success(
|
|
325
|
+
`Codex plugin updated to ${chalk.green(`v${afterVersion}`)}.`
|
|
326
|
+
);
|
|
327
|
+
} else if (afterVersion) {
|
|
328
|
+
log.success(
|
|
329
|
+
`Codex plugin is installed at ${chalk.green(`v${afterVersion}`)}.`
|
|
330
|
+
);
|
|
331
|
+
} else {
|
|
332
|
+
log.warn(
|
|
333
|
+
"Marketplace updated, but coding-friend is not installed yet. Open Codex and run /plugins to install it."
|
|
334
|
+
);
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
if (doCli) {
|
|
340
|
+
const cliVersion = getCliVersion();
|
|
341
|
+
const latestCliVersion = getLatestCliVersion();
|
|
342
|
+
if (!latestCliVersion) {
|
|
343
|
+
log.warn("Cannot check latest CLI version from npm.");
|
|
344
|
+
} else {
|
|
345
|
+
const cmp = semverCompare(cliVersion, latestCliVersion);
|
|
346
|
+
if (cmp < 0) {
|
|
347
|
+
log.step(
|
|
348
|
+
`CLI update available: ${chalk.yellow(`v${cliVersion}`)} \u2192 ${chalk.green(`v${latestCliVersion}`)}`
|
|
349
|
+
);
|
|
350
|
+
const result = run("npm", [
|
|
351
|
+
"install",
|
|
352
|
+
"-g",
|
|
353
|
+
"coding-friend-cli@latest"
|
|
354
|
+
]);
|
|
355
|
+
if (result === null) {
|
|
356
|
+
log.error(
|
|
357
|
+
"CLI update failed. Try manually: npm install -g coding-friend-cli@latest"
|
|
358
|
+
);
|
|
359
|
+
} else {
|
|
360
|
+
log.success(`CLI updated to ${chalk.green(`v${latestCliVersion}`)}`);
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
if (opts.statusline) {
|
|
366
|
+
log.warn(
|
|
367
|
+
"Codex owns its TUI footer. Open Codex and run /statusline to configure it."
|
|
368
|
+
);
|
|
369
|
+
}
|
|
370
|
+
console.log();
|
|
371
|
+
log.dim("Restart Codex CLI (or start a new session) to see changes.");
|
|
372
|
+
}
|
|
281
373
|
|
|
282
374
|
export {
|
|
283
375
|
semverCompare,
|
|
@@ -6,7 +6,7 @@ import {
|
|
|
6
6
|
askScope,
|
|
7
7
|
formatScopeLabel,
|
|
8
8
|
injectBackChoice
|
|
9
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-GIH6E7GJ.js";
|
|
10
10
|
import {
|
|
11
11
|
runWithStderr
|
|
12
12
|
} from "./chunk-EVGXUDX4.js";
|
|
@@ -19,7 +19,7 @@ import {
|
|
|
19
19
|
claudeConfigDir,
|
|
20
20
|
globalConfigPath,
|
|
21
21
|
localConfigPath
|
|
22
|
-
} from "./chunk-
|
|
22
|
+
} from "./chunk-PLSJW7QJ.js";
|
|
23
23
|
import {
|
|
24
24
|
log
|
|
25
25
|
} from "./chunk-NREZK463.js";
|
|
@@ -2,23 +2,23 @@ import {
|
|
|
2
2
|
checkMemoryMcpHealth,
|
|
3
3
|
printHealthSection,
|
|
4
4
|
warnStaleMcpJson
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-ONG4KRYP.js";
|
|
6
6
|
import {
|
|
7
7
|
loadConfig,
|
|
8
8
|
resolveMemoryDir
|
|
9
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-RFNKPAJW.js";
|
|
10
10
|
import {
|
|
11
11
|
getMemoryMcpStatus,
|
|
12
12
|
isMemoryMcpRegistered,
|
|
13
13
|
memoryConfigMenu,
|
|
14
14
|
registerMemoryMcp
|
|
15
|
-
} from "./chunk-
|
|
15
|
+
} from "./chunk-BXSCYGWE.js";
|
|
16
16
|
import {
|
|
17
17
|
getLibPath
|
|
18
18
|
} from "./chunk-RZRT7NGT.js";
|
|
19
19
|
import {
|
|
20
20
|
showConfigHint
|
|
21
|
-
} from "./chunk-
|
|
21
|
+
} from "./chunk-GIH6E7GJ.js";
|
|
22
22
|
import {
|
|
23
23
|
run,
|
|
24
24
|
runWithStderr
|
|
@@ -30,7 +30,7 @@ import {
|
|
|
30
30
|
import {
|
|
31
31
|
globalConfigPath,
|
|
32
32
|
localConfigPath
|
|
33
|
-
} from "./chunk-
|
|
33
|
+
} from "./chunk-PLSJW7QJ.js";
|
|
34
34
|
import {
|
|
35
35
|
log,
|
|
36
36
|
printBanner
|
|
@@ -104,7 +104,9 @@ async function refreshMemoryAfterUpdate() {
|
|
|
104
104
|
log.warn('Memory server build failed \u2014 run "cf memory status" to retry.');
|
|
105
105
|
}
|
|
106
106
|
} catch {
|
|
107
|
-
log.warn(
|
|
107
|
+
log.warn(
|
|
108
|
+
'Could not refresh memory server \u2014 run "cf memory status" to retry.'
|
|
109
|
+
);
|
|
108
110
|
}
|
|
109
111
|
}
|
|
110
112
|
function printMemoryMcpConfig(memoryDir) {
|
|
@@ -7,11 +7,62 @@ import {
|
|
|
7
7
|
import {
|
|
8
8
|
globalConfigPath,
|
|
9
9
|
localConfigPath
|
|
10
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-PLSJW7QJ.js";
|
|
11
11
|
import {
|
|
12
12
|
log
|
|
13
13
|
} from "./chunk-NREZK463.js";
|
|
14
14
|
|
|
15
|
+
// src/lib/host.ts
|
|
16
|
+
var CODEX_MIN_VERSION = "0.130.0";
|
|
17
|
+
function resolveHost(opts = {}) {
|
|
18
|
+
const agent = opts.agent?.trim().toLowerCase();
|
|
19
|
+
const codexAlias = opts.codex === true;
|
|
20
|
+
if (agent && agent !== "claude" && agent !== "codex") {
|
|
21
|
+
throw new Error(
|
|
22
|
+
`Unsupported agent "${opts.agent}". Use "claude" or "codex".`
|
|
23
|
+
);
|
|
24
|
+
}
|
|
25
|
+
if (codexAlias && agent === "claude") {
|
|
26
|
+
throw new Error("Use either --agent claude or --codex, not both.");
|
|
27
|
+
}
|
|
28
|
+
if (codexAlias) return "codex";
|
|
29
|
+
if (agent === "codex") return "codex";
|
|
30
|
+
return "claude";
|
|
31
|
+
}
|
|
32
|
+
function getCodexMinVersion() {
|
|
33
|
+
return CODEX_MIN_VERSION;
|
|
34
|
+
}
|
|
35
|
+
function checkCodexVersion() {
|
|
36
|
+
const min = getCodexMinVersion();
|
|
37
|
+
const output = run("codex", ["--version"]);
|
|
38
|
+
const actual = output ? extractVersion(output) : void 0;
|
|
39
|
+
return {
|
|
40
|
+
ok: actual ? compareVersions(actual, min) >= 0 : false,
|
|
41
|
+
actual,
|
|
42
|
+
min
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
function extractVersion(input) {
|
|
46
|
+
return input.match(/\b(\d+\.\d+\.\d+)\b/)?.[1];
|
|
47
|
+
}
|
|
48
|
+
function compareVersions(a, b) {
|
|
49
|
+
const left = parseVersion(a);
|
|
50
|
+
const right = parseVersion(b);
|
|
51
|
+
for (let i = 0; i < 3; i += 1) {
|
|
52
|
+
if (left[i] > right[i]) return 1;
|
|
53
|
+
if (left[i] < right[i]) return -1;
|
|
54
|
+
}
|
|
55
|
+
return 0;
|
|
56
|
+
}
|
|
57
|
+
function parseVersion(version) {
|
|
58
|
+
const parts = version.split(".").map((part) => Number.parseInt(part, 10));
|
|
59
|
+
return [
|
|
60
|
+
Number.isFinite(parts[0]) ? parts[0] : 0,
|
|
61
|
+
Number.isFinite(parts[1]) ? parts[1] : 0,
|
|
62
|
+
Number.isFinite(parts[2]) ? parts[2] : 0
|
|
63
|
+
];
|
|
64
|
+
}
|
|
65
|
+
|
|
15
66
|
// src/lib/prompt-utils.ts
|
|
16
67
|
import { select, Separator } from "@inquirer/prompts";
|
|
17
68
|
import { existsSync } from "fs";
|
|
@@ -80,6 +131,14 @@ async function resolveScope(opts, message) {
|
|
|
80
131
|
}
|
|
81
132
|
return askPluginScope(message);
|
|
82
133
|
}
|
|
134
|
+
function resolveHostFlags(opts) {
|
|
135
|
+
try {
|
|
136
|
+
return { host: resolveHost(opts) };
|
|
137
|
+
} catch (err) {
|
|
138
|
+
log.error(err instanceof Error ? err.message : String(err));
|
|
139
|
+
process.exit(1);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
83
142
|
function showConfigHint() {
|
|
84
143
|
console.log(chalk.dim("Config files:"));
|
|
85
144
|
console.log(chalk.dim(" Global: ~/.coding-friend/config.json"));
|
|
@@ -173,10 +232,12 @@ function applyDocsDirChange(newValue, oldValue, scope, subfolders = []) {
|
|
|
173
232
|
}
|
|
174
233
|
|
|
175
234
|
export {
|
|
235
|
+
checkCodexVersion,
|
|
176
236
|
BACK,
|
|
177
237
|
injectBackChoice,
|
|
178
238
|
askScope,
|
|
179
239
|
resolveScope,
|
|
240
|
+
resolveHostFlags,
|
|
180
241
|
showConfigHint,
|
|
181
242
|
getScopeLabel,
|
|
182
243
|
formatScopeLabel,
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
// src/lib/paths.ts
|
|
2
2
|
import { homedir } from "os";
|
|
3
3
|
import { resolve, join, isAbsolute } from "path";
|
|
4
|
+
var CODEX_MARKETPLACE_NAME = "coding-friend-marketplace";
|
|
5
|
+
var CODEX_PLUGIN_NAME = "coding-friend";
|
|
4
6
|
function resolvePath(p, base = process.cwd()) {
|
|
5
7
|
if (isAbsolute(p)) return p;
|
|
6
8
|
if (p.startsWith("~/")) return join(homedir(), p.slice(2));
|
|
@@ -77,6 +79,29 @@ function claudeProjectsDir() {
|
|
|
77
79
|
function claudeSessionDir(encodedPath) {
|
|
78
80
|
return join(claudeProjectsDir(), encodedPath);
|
|
79
81
|
}
|
|
82
|
+
function codexConfigDir() {
|
|
83
|
+
const env = process.env.CODEX_HOME?.trim();
|
|
84
|
+
if (!env) return join(homedir(), ".codex");
|
|
85
|
+
if (env === "~") return homedir();
|
|
86
|
+
if (env.startsWith("~/")) return join(homedir(), env.slice(2));
|
|
87
|
+
return env;
|
|
88
|
+
}
|
|
89
|
+
function codexConfigTomlPath() {
|
|
90
|
+
return join(codexConfigDir(), "config.toml");
|
|
91
|
+
}
|
|
92
|
+
function codexPluginsCacheDir() {
|
|
93
|
+
return join(codexConfigDir(), "plugins", "cache");
|
|
94
|
+
}
|
|
95
|
+
function codexInstalledPluginsPath() {
|
|
96
|
+
return join(
|
|
97
|
+
codexPluginsCacheDir(),
|
|
98
|
+
CODEX_MARKETPLACE_NAME,
|
|
99
|
+
CODEX_PLUGIN_NAME
|
|
100
|
+
);
|
|
101
|
+
}
|
|
102
|
+
function codexAgentsDir() {
|
|
103
|
+
return join(codexConfigDir(), "agents");
|
|
104
|
+
}
|
|
80
105
|
|
|
81
106
|
export {
|
|
82
107
|
resolvePath,
|
|
@@ -95,5 +120,8 @@ export {
|
|
|
95
120
|
globalConfigDir,
|
|
96
121
|
memoryDepsDir,
|
|
97
122
|
encodeProjectPath,
|
|
98
|
-
claudeSessionDir
|
|
123
|
+
claudeSessionDir,
|
|
124
|
+
codexConfigTomlPath,
|
|
125
|
+
codexInstalledPluginsPath,
|
|
126
|
+
codexAgentsDir
|
|
99
127
|
};
|