@curdx/flow 2.0.17 → 2.0.18
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/.claude-plugin/marketplace.json +1 -1
- package/.claude-plugin/plugin.json +1 -1
- package/cli/doctor.js +71 -188
- package/cli/install-companions.js +37 -52
- package/cli/install-curdx-plugin.js +23 -47
- package/cli/lib/claude-ops.js +47 -0
- package/cli/lib/doctor-report.js +189 -0
- package/cli/uninstall.js +233 -213
- package/cli/upgrade.js +6 -10
- package/package.json +1 -1
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
},
|
|
7
7
|
"metadata": {
|
|
8
8
|
"description": "Claude Code Discipline Layer — spec-driven workflow + goal-backward verification + Karpathy 4 principles enforced via gates. Stops Claude from faking \"done\" on non-trivial features.",
|
|
9
|
-
"version": "2.0.
|
|
9
|
+
"version": "2.0.18"
|
|
10
10
|
},
|
|
11
11
|
"plugins": [
|
|
12
12
|
{
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "curdx-flow",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.18",
|
|
4
4
|
"description": "Claude Code Discipline Layer — spec-driven workflow + goal-backward verification + Karpathy 4 principles enforced via gates. Stops Claude from faking \"done\" on non-trivial features.",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "wdx",
|
package/cli/doctor.js
CHANGED
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
import fs from "node:fs/promises";
|
|
6
6
|
import path from "node:path";
|
|
7
|
+
|
|
7
8
|
import {
|
|
8
9
|
color,
|
|
9
10
|
log,
|
|
@@ -14,220 +15,102 @@ import {
|
|
|
14
15
|
listMcps,
|
|
15
16
|
ensureClaudeMemRuntimes,
|
|
16
17
|
readUserMcpConfig,
|
|
17
|
-
findDuplicateMcps,
|
|
18
|
-
findPluginByRegistryEntry,
|
|
19
|
-
hasMarketplace,
|
|
20
18
|
} from "./utils.js";
|
|
21
|
-
import {
|
|
19
|
+
import { buildDoctorReport } from "./lib/doctor-report.js";
|
|
22
20
|
|
|
23
21
|
export async function doctor(args = []) {
|
|
24
22
|
const verbose = args.includes("--verbose") || args.includes("-v");
|
|
23
|
+
const claudeVersionValue = claudeVersion();
|
|
24
|
+
const plugins = claudeVersionValue ? listPlugins() : [];
|
|
25
|
+
const marketplaces = claudeVersionValue ? listPluginMarketplaces() : [];
|
|
26
|
+
const mcps = claudeVersionValue ? listMcps() : [];
|
|
27
|
+
const userMcpConfig = claudeVersionValue ? readUserMcpConfig() : new Map();
|
|
28
|
+
const runtimeStatus = plugins.some((plugin) => plugin.name === "claude-mem" && plugin.status === "enabled")
|
|
29
|
+
? ensureClaudeMemRuntimes()
|
|
30
|
+
: null;
|
|
25
31
|
|
|
26
32
|
log.title("🏥 CurdX-Flow Health Check");
|
|
27
33
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
34
|
+
const report = buildDoctorReport({
|
|
35
|
+
claudeVersionValue,
|
|
36
|
+
nodeVersion: process.version,
|
|
37
|
+
plugins,
|
|
38
|
+
marketplaces,
|
|
39
|
+
mcps,
|
|
40
|
+
userMcpConfig,
|
|
41
|
+
runtimeStatus,
|
|
42
|
+
cwd: process.cwd(),
|
|
43
|
+
projectState: await readProjectState(),
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
renderLines(report.lines);
|
|
47
|
+
for (const section of report.sections) {
|
|
48
|
+
console.log(`\n${color.bold(section.title)}`);
|
|
49
|
+
renderLines(section.lines);
|
|
38
50
|
}
|
|
39
51
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
// ---------- curdx-flow plugin ----------
|
|
44
|
-
const plugins = cv ? listPlugins() : [];
|
|
45
|
-
const curdx = plugins.find((p) => p.name === "curdx-flow");
|
|
46
|
-
if (curdx) {
|
|
47
|
-
if (curdx.status === "enabled") {
|
|
48
|
-
log.ok(`curdx-flow ${color.dim(`v${curdx.version} (enabled)`)}`);
|
|
49
|
-
} else {
|
|
50
|
-
log.err(`curdx-flow v${curdx.version} (${curdx.status})`);
|
|
51
|
-
errors++;
|
|
52
|
-
}
|
|
53
|
-
} else {
|
|
54
|
-
log.warn("curdx-flow not installed → run curdx-flow install");
|
|
55
|
-
warnings++;
|
|
52
|
+
printSummary(report);
|
|
53
|
+
if (verbose && claudeVersionValue) {
|
|
54
|
+
printVerboseDetails();
|
|
56
55
|
}
|
|
56
|
+
}
|
|
57
57
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
log.warn(
|
|
66
|
-
`${r.marketplaceId.padEnd(22)} marketplace missing ${color.dim(`(run: claude plugin marketplace add --scope ${r.scope} ${r.marketplaceSource})`)}`
|
|
67
|
-
);
|
|
68
|
-
warnings++;
|
|
69
|
-
}
|
|
70
|
-
if (p && p.status === "enabled") {
|
|
71
|
-
log.ok(`${r.name.padEnd(22)} ${color.dim(`v${p.version || "unknown"}`)}`);
|
|
72
|
-
} else if (p && p.status === "failed") {
|
|
73
|
-
log.err(`${r.name.padEnd(22)} load failed`);
|
|
74
|
-
errors++;
|
|
75
|
-
} else {
|
|
76
|
-
log.warn(
|
|
77
|
-
`${r.name.padEnd(22)} not installed ${color.dim(`(run: claude plugin install --scope ${r.scope} ${r.installSpec})`)}`
|
|
78
|
-
);
|
|
79
|
-
warnings++;
|
|
58
|
+
async function readProjectState() {
|
|
59
|
+
const cwd = process.cwd();
|
|
60
|
+
const flowDir = path.join(cwd, ".flow");
|
|
61
|
+
try {
|
|
62
|
+
const stat = await fs.stat(flowDir);
|
|
63
|
+
if (!stat.isDirectory()) {
|
|
64
|
+
return { exists: false, activeSpec: null };
|
|
80
65
|
}
|
|
81
|
-
}
|
|
82
66
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
// Beta.12 onward: the required MCPs are registered at user-level
|
|
89
|
-
// (via `claude mcp add`), NOT plugin-bundled. Both still show up in
|
|
90
|
-
// `claude mcp list`. Accept a standalone user-level registration
|
|
91
|
-
// as the primary form, and warn if only a plugin-bundled copy exists
|
|
92
|
-
// (because that's the legacy layout that beta.11 had).
|
|
93
|
-
const userLevel = mcps.find((x) => x.name === m && x.plugin === null);
|
|
94
|
-
const pluginLevel = mcps.find((x) => x.name === m && x.plugin !== null);
|
|
95
|
-
|
|
96
|
-
if (userLevel) {
|
|
97
|
-
log.ok(`${m.padEnd(22)} ${color.dim("user-level (standard)")}`);
|
|
98
|
-
} else if (pluginLevel) {
|
|
99
|
-
log.warn(
|
|
100
|
-
`${m.padEnd(22)} registered via plugin:${pluginLevel.plugin} (legacy). ` +
|
|
101
|
-
`Run ${color.cyan("npx @curdx/flow install --all")} to migrate to user-level.`
|
|
102
|
-
);
|
|
103
|
-
warnings++;
|
|
104
|
-
} else {
|
|
105
|
-
if (curdx) {
|
|
106
|
-
log.warn(
|
|
107
|
-
`${m.padEnd(22)} missing. Run: ${color.cyan(`claude mcp add --scope user ${m} -- ${expected.command} ${expected.args.join(" ")}`)}`
|
|
108
|
-
);
|
|
109
|
-
warnings++;
|
|
110
|
-
} else {
|
|
111
|
-
log.info(`${m.padEnd(22)} waiting for curdx-flow install`);
|
|
112
|
-
}
|
|
67
|
+
try {
|
|
68
|
+
const activeSpec = await fs.readFile(path.join(flowDir, ".active-spec"), "utf-8");
|
|
69
|
+
return { exists: true, activeSpec: activeSpec.trim() };
|
|
70
|
+
} catch {
|
|
71
|
+
return { exists: true, activeSpec: null };
|
|
113
72
|
}
|
|
73
|
+
} catch {
|
|
74
|
+
return { exists: false, activeSpec: null };
|
|
114
75
|
}
|
|
76
|
+
}
|
|
115
77
|
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
);
|
|
125
|
-
warnings++;
|
|
126
|
-
}
|
|
127
|
-
if (p && p.status === "enabled") {
|
|
128
|
-
log.ok(`${r.name.padEnd(22)} ${color.dim(`v${p.version}`)}`);
|
|
129
|
-
if (r.postInstall === "claude-mem-runtimes") claudeMemEnabled = true;
|
|
130
|
-
} else if (p && p.status === "failed") {
|
|
131
|
-
log.err(`${r.name.padEnd(22)} load failed`);
|
|
132
|
-
errors++;
|
|
78
|
+
function renderLines(lines) {
|
|
79
|
+
for (const line of lines) {
|
|
80
|
+
if (line.level === "ok") {
|
|
81
|
+
log.ok(line.text);
|
|
82
|
+
} else if (line.level === "warn") {
|
|
83
|
+
log.warn(line.text);
|
|
84
|
+
} else if (line.level === "err") {
|
|
85
|
+
log.err(line.text);
|
|
133
86
|
} else {
|
|
134
|
-
log.
|
|
135
|
-
`${r.name.padEnd(22)} not installed ${color.dim(`(run: claude plugin install --scope ${r.scope} ${r.installSpec})`)}`
|
|
136
|
-
);
|
|
137
|
-
warnings++;
|
|
87
|
+
log.info(line.text);
|
|
138
88
|
}
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
// ---------- Legacy plugin-bundled MCP residue (beta.11 and earlier) ----------
|
|
142
|
-
// Beta.12 moved context7 + sequential-thinking to user-level registration.
|
|
143
|
-
// If both a user-level and a plugin-bundled copy are visible, the user
|
|
144
|
-
// was likely installed from an older beta.7-beta.11 that still had
|
|
145
|
-
// mcpServers in plugin.json and a `claude mcp update` hasn't refreshed
|
|
146
|
-
// the plugin cache yet. Point them at the migration command.
|
|
147
|
-
if (cv) {
|
|
148
|
-
const userCfg = readUserMcpConfig();
|
|
149
|
-
const duplicates = findDuplicateMcps(mcps, userCfg);
|
|
150
|
-
if (duplicates.length > 0) {
|
|
151
|
-
console.log(`\n${color.bold("Legacy plugin-bundled MCPs still present:")}`);
|
|
152
|
-
for (const d of duplicates) {
|
|
153
|
-
log.warn(
|
|
154
|
-
`${d.name.padEnd(22)} both user-level AND plugin:${d.pluginEntry.plugin} active`
|
|
155
|
-
);
|
|
156
|
-
console.log(
|
|
157
|
-
color.dim(
|
|
158
|
-
` → Migration: ${color.cyan(`claude plugin update curdx-flow@curdx-flow-marketplace`)}\n` +
|
|
159
|
-
` then restart Claude Code. Beta.12+ removes plugin-bundled\n` +
|
|
160
|
-
` versions in favor of the user-level entry you already have.`
|
|
161
|
-
)
|
|
162
|
-
);
|
|
163
|
-
warnings++;
|
|
164
|
-
}
|
|
165
|
-
}
|
|
166
|
-
}
|
|
167
89
|
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
console.log(`\n${color.bold("Runtime (claude-mem dependencies):")}`);
|
|
171
|
-
const rt = ensureClaudeMemRuntimes();
|
|
172
|
-
for (const [name, res] of Object.entries(rt)) {
|
|
173
|
-
if (res.status === "ok") {
|
|
174
|
-
log.ok(`${name.padEnd(22)} ${color.dim("visible on PATH")}`);
|
|
175
|
-
} else if (res.status === "linked") {
|
|
176
|
-
log.ok(
|
|
177
|
-
`${name.padEnd(22)} ${color.dim(`auto-linked ${res.link} → ${res.path}`)}`
|
|
178
|
-
);
|
|
179
|
-
} else if (res.status === "missing") {
|
|
180
|
-
log.warn(
|
|
181
|
-
`${name.padEnd(22)} not installed ${color.dim("(claude-mem will auto-install on next Claude Code session)")}`
|
|
182
|
-
);
|
|
183
|
-
warnings++;
|
|
184
|
-
} else if (res.status === "path-unwritable") {
|
|
185
|
-
const dir = res.path.split("/").slice(0, -1).join("/");
|
|
186
|
-
log.err(
|
|
187
|
-
`${name.padEnd(22)} installed but not on PATH ${color.dim(`(add export PATH="${dir}:$PATH" to your shell rc)`)}`
|
|
188
|
-
);
|
|
189
|
-
errors++;
|
|
190
|
-
}
|
|
90
|
+
for (const detail of line.details || []) {
|
|
91
|
+
console.log(color.dim(` → ${detail}`));
|
|
191
92
|
}
|
|
192
93
|
}
|
|
94
|
+
}
|
|
193
95
|
|
|
194
|
-
|
|
195
|
-
console.log(`\n${color.bold("Local project:")}`);
|
|
196
|
-
const cwd = process.cwd();
|
|
197
|
-
const flowDir = path.join(cwd, ".flow");
|
|
198
|
-
try {
|
|
199
|
-
const stat = await fs.stat(flowDir);
|
|
200
|
-
if (stat.isDirectory()) {
|
|
201
|
-
log.ok(`.flow/ ${color.dim(cwd)}`);
|
|
202
|
-
// Check active spec
|
|
203
|
-
try {
|
|
204
|
-
const active = await fs.readFile(path.join(flowDir, ".active-spec"), "utf-8");
|
|
205
|
-
log.info(`Active spec ${color.cyan(active.trim())}`);
|
|
206
|
-
} catch {
|
|
207
|
-
log.info("Active spec (none)");
|
|
208
|
-
}
|
|
209
|
-
}
|
|
210
|
-
} catch {
|
|
211
|
-
log.info(`.flow/ not a curdx-flow project (run: curdx-flow init)`);
|
|
212
|
-
}
|
|
213
|
-
|
|
214
|
-
// ---------- Summary ----------
|
|
96
|
+
function printSummary(report) {
|
|
215
97
|
console.log();
|
|
216
|
-
if (errors > 0) {
|
|
217
|
-
console.log(color.red(`Summary: ${errors} error(s), ${warnings} warning(s)`));
|
|
98
|
+
if (report.errors > 0) {
|
|
99
|
+
console.log(color.red(`Summary: ${report.errors} error(s), ${report.warnings} warning(s)`));
|
|
218
100
|
console.log(color.dim("Fix errors and re-run curdx-flow doctor"));
|
|
219
101
|
process.exit(1);
|
|
220
|
-
} else if (warnings > 0) {
|
|
221
|
-
console.log(color.yellow(`Summary: ${warnings} warning(s). Usable, but worth addressing.`));
|
|
222
|
-
} else {
|
|
223
|
-
console.log(color.green("Summary: all healthy ✓"));
|
|
224
102
|
}
|
|
225
103
|
|
|
226
|
-
if (
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
console.log(`\n${color.bold("Details:")}`);
|
|
230
|
-
console.log(color.dim(` Plugins raw:`));
|
|
231
|
-
console.log(runSync("claude", ["plugin", "list"]).stdout);
|
|
104
|
+
if (report.warnings > 0) {
|
|
105
|
+
console.log(color.yellow(`Summary: ${report.warnings} warning(s). Usable, but worth addressing.`));
|
|
106
|
+
return;
|
|
232
107
|
}
|
|
108
|
+
|
|
109
|
+
console.log(color.green("Summary: all healthy ✓"));
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
function printVerboseDetails() {
|
|
113
|
+
console.log(`\n${color.bold("Details:")}`);
|
|
114
|
+
console.log(color.dim(" Plugins raw:"));
|
|
115
|
+
console.log(runSync("claude", ["plugin", "list"]).stdout);
|
|
233
116
|
}
|
|
@@ -1,34 +1,38 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
color,
|
|
3
|
+
ensureClaudeMemRuntimes,
|
|
4
|
+
listPlugins,
|
|
5
|
+
log,
|
|
6
|
+
multiselectClack,
|
|
7
|
+
note,
|
|
8
|
+
resultLastLine,
|
|
9
|
+
text,
|
|
10
|
+
writeConfig,
|
|
11
|
+
} from "./utils.js";
|
|
2
12
|
import { BUNDLED_MCPS, RECOMMENDED_PLUGINS, REQUIRED_PLUGINS } from "./registry.js";
|
|
3
13
|
import { readUserMcpConfig } from "./utils.js";
|
|
4
14
|
import {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
} from "./lib/claude-
|
|
15
|
+
addMcp,
|
|
16
|
+
addPluginMarketplace,
|
|
17
|
+
installPlugin,
|
|
18
|
+
removeMcp,
|
|
19
|
+
} from "./lib/claude-ops.js";
|
|
10
20
|
|
|
11
21
|
const RECOMMENDED = RECOMMENDED_PLUGINS;
|
|
22
|
+
const CONTEXT7_COMMAND = "npx";
|
|
23
|
+
const CONTEXT7_ARGS = ["-y", "@upstash/context7-mcp"];
|
|
12
24
|
|
|
13
25
|
export async function installRequiredPlugins({ yes, language, config }) {
|
|
14
26
|
log.blank();
|
|
15
27
|
log.info("Installing required Claude Code plugins...");
|
|
16
28
|
for (const plugin of REQUIRED_PLUGINS) {
|
|
17
29
|
console.log(` ${color.cyan("▸")} Installing ${color.bold(plugin.name)}...`);
|
|
18
|
-
const ma = await
|
|
19
|
-
"claude",
|
|
20
|
-
pluginMarketplaceAddArgs(plugin),
|
|
21
|
-
{ silent: true }
|
|
22
|
-
);
|
|
30
|
+
const ma = await addPluginMarketplace(plugin);
|
|
23
31
|
if (ma.code !== 0 && !ma.stderr.includes("already")) {
|
|
24
32
|
log.warn(` marketplace add warning: ${ma.stderr.trim().split("\n")[0]}`);
|
|
25
33
|
}
|
|
26
34
|
|
|
27
|
-
const ir = await
|
|
28
|
-
"claude",
|
|
29
|
-
pluginInstallArgs(plugin),
|
|
30
|
-
{ silent: true }
|
|
31
|
-
);
|
|
35
|
+
const ir = await installPlugin(plugin);
|
|
32
36
|
if (ir.code === 0) {
|
|
33
37
|
console.log(` ${color.green("✓")} ${plugin.name} installed`);
|
|
34
38
|
|
|
@@ -65,11 +69,7 @@ export async function registerBundledMcps() {
|
|
|
65
69
|
);
|
|
66
70
|
continue;
|
|
67
71
|
}
|
|
68
|
-
const r = await
|
|
69
|
-
"claude",
|
|
70
|
-
mcpAddArgs(mcp),
|
|
71
|
-
{ silent: true }
|
|
72
|
-
);
|
|
72
|
+
const r = await addMcp(mcp);
|
|
73
73
|
if (r.code === 0) {
|
|
74
74
|
log.ok(` ${mcp.name.padEnd(22)} ${color.dim("registered")}`);
|
|
75
75
|
} else if (r.stderr.includes("already exists")) {
|
|
@@ -129,19 +129,13 @@ export async function installRecommendedPlugins({ all, yes, language }) {
|
|
|
129
129
|
console.log(` ${color.cyan("▸")} Installing ${color.bold(rec.name)}...`);
|
|
130
130
|
|
|
131
131
|
if (rec.marketplaceSource) {
|
|
132
|
-
const ma = await
|
|
133
|
-
"claude",
|
|
134
|
-
pluginMarketplaceAddArgs(rec),
|
|
135
|
-
{ silent: true }
|
|
136
|
-
);
|
|
132
|
+
const ma = await addPluginMarketplace(rec);
|
|
137
133
|
if (ma.code !== 0 && !ma.stderr.includes("already")) {
|
|
138
134
|
log.warn(` marketplace add warning: ${ma.stderr.trim().split("\n")[0]}`);
|
|
139
135
|
}
|
|
140
136
|
}
|
|
141
137
|
|
|
142
|
-
const ir = await
|
|
143
|
-
silent: true,
|
|
144
|
-
});
|
|
138
|
+
const ir = await installPlugin(rec);
|
|
145
139
|
if (ir.code === 0) {
|
|
146
140
|
console.log(` ${color.green("✓")} ${rec.name} installed`);
|
|
147
141
|
|
|
@@ -207,16 +201,7 @@ async function promptPluginConfig(plugin, language, config) {
|
|
|
207
201
|
config.context7ApiKey = apiKey;
|
|
208
202
|
writeConfig(config);
|
|
209
203
|
|
|
210
|
-
const r = await
|
|
211
|
-
"claude",
|
|
212
|
-
mcpAddArgs({
|
|
213
|
-
name: "context7",
|
|
214
|
-
env: [`CONTEXT7_API_KEY=${apiKey}`],
|
|
215
|
-
command: "npx",
|
|
216
|
-
args: ["-y", "@upstash/context7-mcp"],
|
|
217
|
-
}),
|
|
218
|
-
{ silent: true }
|
|
219
|
-
);
|
|
204
|
+
const r = await addMcp(buildContext7Mcp(apiKey));
|
|
220
205
|
|
|
221
206
|
if (r.code === 0) {
|
|
222
207
|
log.ok(
|
|
@@ -225,17 +210,8 @@ async function promptPluginConfig(plugin, language, config) {
|
|
|
225
210
|
: " Context7 API key configured"
|
|
226
211
|
);
|
|
227
212
|
} else if (r.stderr.includes("already exists")) {
|
|
228
|
-
await
|
|
229
|
-
const r2 = await
|
|
230
|
-
"claude",
|
|
231
|
-
mcpAddArgs({
|
|
232
|
-
name: "context7",
|
|
233
|
-
env: [`CONTEXT7_API_KEY=${apiKey}`],
|
|
234
|
-
command: "npx",
|
|
235
|
-
args: ["-y", "@upstash/context7-mcp"],
|
|
236
|
-
}),
|
|
237
|
-
{ silent: true }
|
|
238
|
-
);
|
|
213
|
+
await removeMcp({ name: "context7" });
|
|
214
|
+
const r2 = await addMcp(buildContext7Mcp(apiKey));
|
|
239
215
|
if (r2.code === 0) {
|
|
240
216
|
log.ok(
|
|
241
217
|
language === "zh"
|
|
@@ -252,8 +228,8 @@ async function promptPluginConfig(plugin, language, config) {
|
|
|
252
228
|
log.info(
|
|
253
229
|
color.dim(
|
|
254
230
|
language === "zh"
|
|
255
|
-
? ` 手动运行: claude mcp add --scope user context7 --env CONTEXT7_API_KEY=${apiKey} --
|
|
256
|
-
: ` Run manually: claude mcp add --scope user context7 --env CONTEXT7_API_KEY=${apiKey} --
|
|
231
|
+
? ` 手动运行: claude mcp add --scope user context7 --env CONTEXT7_API_KEY=${apiKey} -- ${CONTEXT7_COMMAND} ${CONTEXT7_ARGS.join(" ")}`
|
|
232
|
+
: ` Run manually: claude mcp add --scope user context7 --env CONTEXT7_API_KEY=${apiKey} -- ${CONTEXT7_COMMAND} ${CONTEXT7_ARGS.join(" ")}`
|
|
257
233
|
)
|
|
258
234
|
);
|
|
259
235
|
}
|
|
@@ -265,3 +241,12 @@ async function promptPluginConfig(plugin, language, config) {
|
|
|
265
241
|
);
|
|
266
242
|
}
|
|
267
243
|
}
|
|
244
|
+
|
|
245
|
+
function buildContext7Mcp(apiKey) {
|
|
246
|
+
return {
|
|
247
|
+
name: "context7",
|
|
248
|
+
env: [`CONTEXT7_API_KEY=${apiKey}`],
|
|
249
|
+
command: CONTEXT7_COMMAND,
|
|
250
|
+
args: CONTEXT7_ARGS,
|
|
251
|
+
};
|
|
252
|
+
}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import { color, log, resultOutput
|
|
1
|
+
import { color, log, resultOutput } from "./utils.js";
|
|
2
2
|
import {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
} from "./lib/claude-
|
|
3
|
+
addPluginMarketplace,
|
|
4
|
+
installPlugin,
|
|
5
|
+
removePluginMarketplace,
|
|
6
|
+
} from "./lib/claude-ops.js";
|
|
7
7
|
|
|
8
8
|
export async function addCurdxMarketplace({ marketplaceSource, marketplaceLabel, useOffline }) {
|
|
9
9
|
log.blank();
|
|
@@ -12,17 +12,9 @@ export async function addCurdxMarketplace({ marketplaceSource, marketplaceLabel,
|
|
|
12
12
|
// Remove any existing marketplace with the same name so we get a clean
|
|
13
13
|
// rebind to the chosen source. Errors are non-fatal (marketplace may
|
|
14
14
|
// simply not exist yet).
|
|
15
|
-
await
|
|
16
|
-
"claude",
|
|
17
|
-
pluginMarketplaceRemoveArgs("curdx-flow-marketplace"),
|
|
18
|
-
{ silent: true }
|
|
19
|
-
);
|
|
15
|
+
await removePluginMarketplace("curdx-flow-marketplace");
|
|
20
16
|
|
|
21
|
-
const addRes = await
|
|
22
|
-
"claude",
|
|
23
|
-
pluginMarketplaceAddArgs({ scope: "user", marketplaceSource }),
|
|
24
|
-
{ silent: true }
|
|
25
|
-
);
|
|
17
|
+
const addRes = await addPluginMarketplace({ scope: "user", marketplaceSource });
|
|
26
18
|
if (addRes.code !== 0 && !addRes.stderr.includes("already")) {
|
|
27
19
|
log.warn(`marketplace add output: ${resultOutput(addRes)}`);
|
|
28
20
|
} else {
|
|
@@ -50,14 +42,10 @@ export async function installCurdxFlowPlugin({ prevCurdxFlow, shippedVersion })
|
|
|
50
42
|
log.info(
|
|
51
43
|
`curdx-flow already at v${already.version}, re-registering...`
|
|
52
44
|
);
|
|
53
|
-
const r = await
|
|
54
|
-
"
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
installSpec: "curdx-flow@curdx-flow-marketplace",
|
|
58
|
-
}),
|
|
59
|
-
{ silent: true }
|
|
60
|
-
);
|
|
45
|
+
const r = await installPlugin({
|
|
46
|
+
scope: "user",
|
|
47
|
+
installSpec: "curdx-flow@curdx-flow-marketplace",
|
|
48
|
+
});
|
|
61
49
|
if (r.code !== 0) {
|
|
62
50
|
log.err(`Install failed: ${resultOutput(r)}`);
|
|
63
51
|
process.exit(1);
|
|
@@ -67,14 +55,10 @@ export async function installCurdxFlowPlugin({ prevCurdxFlow, shippedVersion })
|
|
|
67
55
|
log.info(
|
|
68
56
|
`curdx-flow v${already.version} → v${shippedVersion}, installing...`
|
|
69
57
|
);
|
|
70
|
-
const r = await
|
|
71
|
-
"
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
installSpec: "curdx-flow@curdx-flow-marketplace",
|
|
75
|
-
}),
|
|
76
|
-
{ silent: true }
|
|
77
|
-
);
|
|
58
|
+
const r = await installPlugin({
|
|
59
|
+
scope: "user",
|
|
60
|
+
installSpec: "curdx-flow@curdx-flow-marketplace",
|
|
61
|
+
});
|
|
78
62
|
if (r.code !== 0) {
|
|
79
63
|
log.err(`Install failed: ${resultOutput(r)}`);
|
|
80
64
|
process.exit(1);
|
|
@@ -84,28 +68,20 @@ export async function installCurdxFlowPlugin({ prevCurdxFlow, shippedVersion })
|
|
|
84
68
|
log.info(
|
|
85
69
|
`curdx-flow v${already.version} detected, re-registering...`
|
|
86
70
|
);
|
|
87
|
-
const r = await
|
|
88
|
-
"
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
installSpec: "curdx-flow@curdx-flow-marketplace",
|
|
92
|
-
}),
|
|
93
|
-
{ silent: true }
|
|
94
|
-
);
|
|
71
|
+
const r = await installPlugin({
|
|
72
|
+
scope: "user",
|
|
73
|
+
installSpec: "curdx-flow@curdx-flow-marketplace",
|
|
74
|
+
});
|
|
95
75
|
if (r.code !== 0) {
|
|
96
76
|
log.err(`Install failed: ${resultOutput(r)}`);
|
|
97
77
|
process.exit(1);
|
|
98
78
|
}
|
|
99
79
|
log.ok("curdx-flow re-registered");
|
|
100
80
|
} else {
|
|
101
|
-
const r = await
|
|
102
|
-
"
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
installSpec: "curdx-flow@curdx-flow-marketplace",
|
|
106
|
-
}),
|
|
107
|
-
{ silent: true }
|
|
108
|
-
);
|
|
81
|
+
const r = await installPlugin({
|
|
82
|
+
scope: "user",
|
|
83
|
+
installSpec: "curdx-flow@curdx-flow-marketplace",
|
|
84
|
+
});
|
|
109
85
|
if (r.code !== 0) {
|
|
110
86
|
log.err(`Install failed: ${resultOutput(r)}`);
|
|
111
87
|
process.exit(1);
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { run } from "./process.js";
|
|
2
|
+
import {
|
|
3
|
+
mcpAddArgs,
|
|
4
|
+
mcpRemoveArgs,
|
|
5
|
+
pluginInstallArgs,
|
|
6
|
+
pluginMarketplaceAddArgs,
|
|
7
|
+
pluginMarketplaceRemoveArgs,
|
|
8
|
+
pluginMarketplaceUpdateArgs,
|
|
9
|
+
pluginUninstallArgs,
|
|
10
|
+
pluginUpdateArgs,
|
|
11
|
+
} from "./claude-commands.js";
|
|
12
|
+
|
|
13
|
+
async function runClaude(args, { runner = run, silent = true } = {}) {
|
|
14
|
+
return runner("claude", args, { silent });
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export async function addPluginMarketplace(entry, options) {
|
|
18
|
+
return runClaude(pluginMarketplaceAddArgs(entry), options);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export async function updatePluginMarketplace(marketplaceId, options) {
|
|
22
|
+
return runClaude(pluginMarketplaceUpdateArgs(marketplaceId), options);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export async function removePluginMarketplace(marketplaceId, options) {
|
|
26
|
+
return runClaude(pluginMarketplaceRemoveArgs(marketplaceId), options);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export async function installPlugin(entry, options) {
|
|
30
|
+
return runClaude(pluginInstallArgs(entry), options);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export async function updatePlugin(spec, options) {
|
|
34
|
+
return runClaude(pluginUpdateArgs({ spec }), options);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export async function uninstallPlugin(entry, options) {
|
|
38
|
+
return runClaude(pluginUninstallArgs(entry), options);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export async function addMcp(entry, options) {
|
|
42
|
+
return runClaude(mcpAddArgs(entry), options);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export async function removeMcp(entry, options) {
|
|
46
|
+
return runClaude(mcpRemoveArgs(entry), options);
|
|
47
|
+
}
|