@curdx/flow 2.0.19 → 2.0.20
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-workflow.js +121 -0
- package/cli/doctor.js +15 -95
- package/cli/install-curdx-plugin.js +62 -61
- package/cli/install-self-update.js +93 -3
- package/cli/install-workflow.js +209 -0
- package/cli/install.js +33 -123
- package/cli/uninstall-workflow.js +146 -0
- package/cli/uninstall.js +33 -106
- package/cli/upgrade-workflow.js +80 -0
- package/cli/upgrade.js +31 -39
- package/package.json +1 -1
package/cli/uninstall.js
CHANGED
|
@@ -12,17 +12,27 @@ import {
|
|
|
12
12
|
resultLastLine,
|
|
13
13
|
resultOutput,
|
|
14
14
|
confirm,
|
|
15
|
-
multiSelect,
|
|
16
|
-
claudeVersion,
|
|
17
15
|
listPlugins,
|
|
18
16
|
} from "./utils.js";
|
|
19
|
-
import { removeGlobalProtocols, GLOBAL_CLAUDE_MD } from "./protocols.js";
|
|
20
17
|
import { REQUIRED_PLUGINS, RECOMMENDED_PLUGINS, BUNDLED_MCPS } from "./registry.js";
|
|
21
18
|
import {
|
|
22
19
|
removeMcp,
|
|
23
20
|
removePluginMarketplace,
|
|
24
21
|
uninstallPlugin,
|
|
25
22
|
} from "./lib/claude-ops.js";
|
|
23
|
+
import {
|
|
24
|
+
createUninstallContext,
|
|
25
|
+
ensureClaudeCliAvailableForUninstall,
|
|
26
|
+
getInstalledTargets,
|
|
27
|
+
getManagedMarketplaceIds,
|
|
28
|
+
printUninstallSummary,
|
|
29
|
+
removeProtocolsStep,
|
|
30
|
+
selectRecommendedPluginsToRemove,
|
|
31
|
+
shouldKeepBundledMcps,
|
|
32
|
+
shouldKeepRequiredPlugins,
|
|
33
|
+
UNINSTALL_STEP_COUNT,
|
|
34
|
+
confirmUninstallStep,
|
|
35
|
+
} from "./uninstall-workflow.js";
|
|
26
36
|
|
|
27
37
|
const HOME = homedir();
|
|
28
38
|
|
|
@@ -36,45 +46,30 @@ const MANAGED_SYMLINKS = [
|
|
|
36
46
|
];
|
|
37
47
|
|
|
38
48
|
export async function uninstall(args = []) {
|
|
39
|
-
const
|
|
40
|
-
const purge = args.includes("--purge");
|
|
41
|
-
const keepRecommended = args.includes("--keep-recommended");
|
|
49
|
+
const context = createUninstallContext(args);
|
|
42
50
|
|
|
43
51
|
log.title("🗑️ CurdX-Flow Uninstaller");
|
|
44
52
|
|
|
45
|
-
|
|
46
|
-
if (!cv) {
|
|
47
|
-
log.err("claude CLI not found, cannot uninstall plugin.");
|
|
48
|
-
process.exit(1);
|
|
49
|
-
}
|
|
53
|
+
ensureClaudeCliAvailableForUninstall();
|
|
50
54
|
|
|
51
|
-
if (!(await
|
|
52
|
-
log.info("Cancelled");
|
|
55
|
+
if (!(await confirmUninstallStep(context))) {
|
|
53
56
|
return;
|
|
54
57
|
}
|
|
55
58
|
|
|
56
59
|
await uninstallCurdxFlowPlugin();
|
|
57
|
-
await maybeUninstallRecommendedPlugins(
|
|
58
|
-
await maybeRemoveBundledMcps(
|
|
59
|
-
await maybeUninstallRequiredPlugins(
|
|
60
|
-
await maybePurgeRuntimeArtifacts(
|
|
61
|
-
|
|
62
|
-
await maybeRemoveProjectState(
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
async function confirmUninstall({ yes }) {
|
|
68
|
-
if (yes) return true;
|
|
69
|
-
return confirm(
|
|
70
|
-
`This will uninstall the ${color.bold("curdx-flow")} plugin. Continue?`,
|
|
71
|
-
false
|
|
72
|
-
);
|
|
60
|
+
await maybeUninstallRecommendedPlugins(context);
|
|
61
|
+
await maybeRemoveBundledMcps(context);
|
|
62
|
+
await maybeUninstallRequiredPlugins(context);
|
|
63
|
+
await maybePurgeRuntimeArtifacts(context);
|
|
64
|
+
removeProtocolsStep();
|
|
65
|
+
await maybeRemoveProjectState(context);
|
|
66
|
+
|
|
67
|
+
printUninstallSummary(context);
|
|
73
68
|
}
|
|
74
69
|
|
|
75
70
|
async function uninstallCurdxFlowPlugin() {
|
|
76
71
|
log.blank();
|
|
77
|
-
log.step(1,
|
|
72
|
+
log.step(1, UNINSTALL_STEP_COUNT, "Uninstalling curdx-flow plugin...");
|
|
78
73
|
const curdx = listPlugins().find((plugin) => plugin.name === "curdx-flow");
|
|
79
74
|
if (!curdx) {
|
|
80
75
|
log.info("curdx-flow not installed, skipping");
|
|
@@ -95,22 +90,19 @@ async function uninstallCurdxFlowPlugin() {
|
|
|
95
90
|
|
|
96
91
|
async function maybeUninstallRecommendedPlugins({ yes, keepRecommended }) {
|
|
97
92
|
log.blank();
|
|
98
|
-
log.step(2,
|
|
93
|
+
log.step(2, UNINSTALL_STEP_COUNT, "Recommended plugins");
|
|
99
94
|
if (keepRecommended) {
|
|
100
95
|
log.info("Keeping recommended plugins (--keep-recommended)");
|
|
101
96
|
return;
|
|
102
97
|
}
|
|
103
98
|
|
|
104
|
-
const
|
|
105
|
-
const present = RECOMMENDED.filter((entry) =>
|
|
106
|
-
currentlyInstalled.some((plugin) => plugin.name === entry.name)
|
|
107
|
-
);
|
|
99
|
+
const present = getInstalledTargets(RECOMMENDED);
|
|
108
100
|
if (present.length === 0) {
|
|
109
101
|
log.info("No installed recommended plugins");
|
|
110
102
|
return;
|
|
111
103
|
}
|
|
112
104
|
|
|
113
|
-
const selected = await
|
|
105
|
+
const selected = await selectRecommendedPluginsToRemove({ yes, present });
|
|
114
106
|
for (const name of selected) {
|
|
115
107
|
const entry = present.find((plugin) => plugin.name === name);
|
|
116
108
|
if (!entry) continue;
|
|
@@ -118,26 +110,6 @@ async function maybeUninstallRecommendedPlugins({ yes, keepRecommended }) {
|
|
|
118
110
|
}
|
|
119
111
|
}
|
|
120
112
|
|
|
121
|
-
async function selectPluginsToRemove({ yes, present }) {
|
|
122
|
-
if (yes) {
|
|
123
|
-
log.info(
|
|
124
|
-
color.dim("--yes mode: keeping recommended plugins (use --purge to remove them)")
|
|
125
|
-
);
|
|
126
|
-
return [];
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
const choices = present.map((entry) => ({
|
|
130
|
-
label: color.bold(entry.name),
|
|
131
|
-
value: entry.name,
|
|
132
|
-
hint: "",
|
|
133
|
-
}));
|
|
134
|
-
return multiSelect(
|
|
135
|
-
"Which recommended plugins to also uninstall? (default: none)",
|
|
136
|
-
choices,
|
|
137
|
-
[]
|
|
138
|
-
);
|
|
139
|
-
}
|
|
140
|
-
|
|
141
113
|
async function uninstallNamedPlugin(entry) {
|
|
142
114
|
log.blank();
|
|
143
115
|
console.log(` ${color.cyan("▸")} Uninstalling ${color.bold(entry.name)}...`);
|
|
@@ -155,7 +127,7 @@ async function uninstallNamedPlugin(entry) {
|
|
|
155
127
|
async function maybeRemoveBundledMcps({ yes, keepRecommended }) {
|
|
156
128
|
log.blank();
|
|
157
129
|
log.info("Required MCP servers (context7, sequential-thinking)");
|
|
158
|
-
if (keepRecommended
|
|
130
|
+
if (shouldKeepBundledMcps({ yes, keepRecommended })) {
|
|
159
131
|
log.info(
|
|
160
132
|
color.dim("--yes or --keep-recommended: keeping user-level MCPs (remove manually with `claude mcp remove <name>`)")
|
|
161
133
|
);
|
|
@@ -184,7 +156,7 @@ async function maybeRemoveBundledMcps({ yes, keepRecommended }) {
|
|
|
184
156
|
async function maybeUninstallRequiredPlugins({ yes }) {
|
|
185
157
|
log.blank();
|
|
186
158
|
log.info("Required companion plugins");
|
|
187
|
-
if (yes) {
|
|
159
|
+
if (shouldKeepRequiredPlugins({ yes })) {
|
|
188
160
|
log.info(
|
|
189
161
|
color.dim("--yes mode: keeping required companion plugins (use --purge to remove them)")
|
|
190
162
|
);
|
|
@@ -212,7 +184,7 @@ async function maybeUninstallRequiredPlugins({ yes }) {
|
|
|
212
184
|
|
|
213
185
|
async function maybePurgeRuntimeArtifacts({ purge }) {
|
|
214
186
|
log.blank();
|
|
215
|
-
log.step(3,
|
|
187
|
+
log.step(3, UNINSTALL_STEP_COUNT, "Runtime symlinks and marketplaces");
|
|
216
188
|
if (!purge) {
|
|
217
189
|
log.info(
|
|
218
190
|
color.dim("Keeping ~/.local/bin/bun, ~/.local/bin/uv (use --purge to remove)")
|
|
@@ -228,14 +200,7 @@ async function maybePurgeRuntimeArtifacts({ purge }) {
|
|
|
228
200
|
}
|
|
229
201
|
|
|
230
202
|
async function purgeManagedMarketplaces() {
|
|
231
|
-
const marketplaceIds =
|
|
232
|
-
...new Set(
|
|
233
|
-
RECOMMENDED
|
|
234
|
-
.concat(REQUIRED)
|
|
235
|
-
.map((entry) => entry.marketplaceId)
|
|
236
|
-
.filter((id) => id && id !== "claude-plugins-official")
|
|
237
|
-
),
|
|
238
|
-
];
|
|
203
|
+
const marketplaceIds = getManagedMarketplaceIds(RECOMMENDED.concat(REQUIRED));
|
|
239
204
|
|
|
240
205
|
for (const marketplaceId of marketplaceIds) {
|
|
241
206
|
const result = await removePluginMarketplace(marketplaceId);
|
|
@@ -269,26 +234,9 @@ function removeManagedSymlinks() {
|
|
|
269
234
|
}
|
|
270
235
|
}
|
|
271
236
|
|
|
272
|
-
function removeProtocols() {
|
|
273
|
-
log.blank();
|
|
274
|
-
console.log(color.dim("Removing global protocols from ~/.claude/CLAUDE.md..."));
|
|
275
|
-
try {
|
|
276
|
-
const result = removeGlobalProtocols();
|
|
277
|
-
if (result.action === "removed") {
|
|
278
|
-
log.ok(`Global protocols removed ${color.dim(`(${GLOBAL_CLAUDE_MD})`)}`);
|
|
279
|
-
} else if (result.action === "not-present") {
|
|
280
|
-
log.info("Global protocols not present, skipping");
|
|
281
|
-
} else {
|
|
282
|
-
log.info("~/.claude/CLAUDE.md does not exist, skipping");
|
|
283
|
-
}
|
|
284
|
-
} catch (err) {
|
|
285
|
-
log.warn(`Protocol removal failed: ${err.message}`);
|
|
286
|
-
}
|
|
287
|
-
}
|
|
288
|
-
|
|
289
237
|
async function maybeRemoveProjectState({ yes }) {
|
|
290
238
|
log.blank();
|
|
291
|
-
log.step(4,
|
|
239
|
+
log.step(4, UNINSTALL_STEP_COUNT, "Project state directory");
|
|
292
240
|
const flowDir = join(process.cwd(), ".flow");
|
|
293
241
|
if (!existsSync(flowDir)) {
|
|
294
242
|
log.info(".flow/ does not exist, skipping");
|
|
@@ -319,27 +267,6 @@ async function maybeRemoveProjectState({ yes }) {
|
|
|
319
267
|
}
|
|
320
268
|
}
|
|
321
269
|
|
|
322
|
-
function printSummary({ purge }) {
|
|
323
|
-
log.blank();
|
|
324
|
-
console.log(color.bold("✅ Uninstall complete"));
|
|
325
|
-
if (purge) return;
|
|
326
|
-
|
|
327
|
-
console.log(
|
|
328
|
-
color.dim(
|
|
329
|
-
`\nArtifacts kept:\n` +
|
|
330
|
-
` - ~/.local/bin/bun, ~/.local/bin/uv (symlinks; use --purge to remove)\n` +
|
|
331
|
-
` - bun/uv binaries themselves (~/.bun/bin/bun, ~/.local/bin/uv real installs)\n` +
|
|
332
|
-
` - claude-mem data (~/.claude-mem/)\n` +
|
|
333
|
-
` - claude marketplace cache`
|
|
334
|
-
)
|
|
335
|
-
);
|
|
336
|
-
console.log(
|
|
337
|
-
color.dim(
|
|
338
|
-
`\nFully purge: ${color.cyan("curdx-flow uninstall --purge")}`
|
|
339
|
-
)
|
|
340
|
-
);
|
|
341
|
-
}
|
|
342
|
-
|
|
343
270
|
function toUninstallTarget(entry) {
|
|
344
271
|
return {
|
|
345
272
|
name: entry.name,
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { claudeVersion, color, listPlugins, log, resultLastLine } from "./utils.js";
|
|
2
|
+
|
|
3
|
+
export const UPGRADE_STEP_COUNT = 2;
|
|
4
|
+
|
|
5
|
+
export function ensureClaudeCliAvailableForUpgrade(
|
|
6
|
+
{ claudeVersionImpl = claudeVersion, logImpl = log, exitImpl = process.exit } = {}
|
|
7
|
+
) {
|
|
8
|
+
const version = claudeVersionImpl();
|
|
9
|
+
if (!version) {
|
|
10
|
+
logImpl.err("claude CLI not found");
|
|
11
|
+
exitImpl(1);
|
|
12
|
+
return null;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
return version;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export function getInstalledPluginNames({ listPluginsImpl = listPlugins } = {}) {
|
|
19
|
+
return new Set(listPluginsImpl().map((plugin) => plugin.name));
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export function getPluginNameFromSpec(spec) {
|
|
23
|
+
return String(spec).split("@")[0];
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export function classifyPluginUpdateResult(result) {
|
|
27
|
+
if (result.code !== 0) {
|
|
28
|
+
return {
|
|
29
|
+
status: "failed",
|
|
30
|
+
message: resultLastLine(result),
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
if (!result.stdout.includes("updated from")) {
|
|
35
|
+
return {
|
|
36
|
+
status: "unchanged",
|
|
37
|
+
message: "already up to date",
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const match = result.stdout.match(/updated from (\S+) to (\S+)/);
|
|
42
|
+
if (!match) {
|
|
43
|
+
return {
|
|
44
|
+
status: "updated",
|
|
45
|
+
message: "updated",
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
return {
|
|
50
|
+
status: "updated",
|
|
51
|
+
from: match[1],
|
|
52
|
+
to: match[2],
|
|
53
|
+
message: color.dim(`${match[1]} → ${match[2]}`),
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export async function refreshMarketplaces(
|
|
58
|
+
marketplaceIds,
|
|
59
|
+
{ updatePluginMarketplaceImpl, logImpl = log } = {}
|
|
60
|
+
) {
|
|
61
|
+
logImpl.step(1, UPGRADE_STEP_COUNT, "Refreshing marketplaces...");
|
|
62
|
+
|
|
63
|
+
for (const marketplaceId of marketplaceIds) {
|
|
64
|
+
const result = await updatePluginMarketplaceImpl(marketplaceId);
|
|
65
|
+
if (result.code === 0) {
|
|
66
|
+
logImpl.ok(` ${marketplaceId}`);
|
|
67
|
+
continue;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
if (!result.stderr.includes("not found")) {
|
|
71
|
+
logImpl.warn(` ${marketplaceId}: ${resultLastLine(result)}`);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export function printUpgradeSummary({ logImpl = log } = {}) {
|
|
77
|
+
logImpl.blank();
|
|
78
|
+
logImpl.ok("Upgrade complete");
|
|
79
|
+
console.log(color.dim(" Some changes require a Claude Code restart to take effect"));
|
|
80
|
+
}
|
package/cli/upgrade.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* upgrade command — update curdx-flow + recommended plugins to latest.
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
|
-
import {
|
|
5
|
+
import { log } from "./utils.js";
|
|
6
6
|
import {
|
|
7
7
|
PLUGINS_TO_UPDATE,
|
|
8
8
|
MARKETPLACES_TO_REFRESH,
|
|
@@ -11,61 +11,53 @@ import {
|
|
|
11
11
|
updatePlugin,
|
|
12
12
|
updatePluginMarketplace,
|
|
13
13
|
} from "./lib/claude-ops.js";
|
|
14
|
+
import {
|
|
15
|
+
classifyPluginUpdateResult,
|
|
16
|
+
ensureClaudeCliAvailableForUpgrade,
|
|
17
|
+
getInstalledPluginNames,
|
|
18
|
+
getPluginNameFromSpec,
|
|
19
|
+
printUpgradeSummary,
|
|
20
|
+
refreshMarketplaces,
|
|
21
|
+
UPGRADE_STEP_COUNT,
|
|
22
|
+
} from "./upgrade-workflow.js";
|
|
14
23
|
|
|
15
24
|
export async function upgrade(args = []) {
|
|
16
25
|
log.title("⬆️ CurdX-Flow upgrade");
|
|
17
26
|
|
|
18
|
-
|
|
19
|
-
log.err("claude CLI not found");
|
|
20
|
-
process.exit(1);
|
|
21
|
-
}
|
|
27
|
+
ensureClaudeCliAvailableForUpgrade();
|
|
22
28
|
|
|
23
29
|
// Refresh marketplaces first (derived from cli/registry.js)
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
if (r.code === 0) {
|
|
28
|
-
log.ok(` ${mp}`);
|
|
29
|
-
} else {
|
|
30
|
-
// Not a fatal — might not be added
|
|
31
|
-
if (!r.stderr.includes("not found")) {
|
|
32
|
-
log.warn(` ${mp}: ${resultLastLine(r)}`);
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
}
|
|
30
|
+
await refreshMarketplaces(MARKETPLACES_TO_REFRESH, {
|
|
31
|
+
updatePluginMarketplaceImpl: updatePluginMarketplace,
|
|
32
|
+
});
|
|
36
33
|
|
|
37
34
|
// Update each plugin
|
|
38
35
|
log.blank();
|
|
39
|
-
log.step(2,
|
|
40
|
-
const
|
|
41
|
-
const installedNames = new Set(installed.map((p) => p.name));
|
|
36
|
+
log.step(2, UPGRADE_STEP_COUNT, "Updating installed plugins...");
|
|
37
|
+
const installedNames = getInstalledPluginNames();
|
|
42
38
|
|
|
43
39
|
for (const spec of PLUGINS_TO_UPDATE) {
|
|
44
|
-
const pluginName = spec
|
|
40
|
+
const pluginName = getPluginNameFromSpec(spec);
|
|
45
41
|
if (!installedNames.has(pluginName)) {
|
|
46
42
|
log.info(` ${pluginName.padEnd(22)} not installed, skipping`);
|
|
47
43
|
continue;
|
|
48
44
|
}
|
|
49
45
|
|
|
50
|
-
const
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
log.ok(` ${pluginName.padEnd(22)} ${color.dim(`${m[1]} → ${m[2]}`)}`);
|
|
57
|
-
} else {
|
|
58
|
-
log.ok(` ${pluginName.padEnd(22)} updated`);
|
|
59
|
-
}
|
|
60
|
-
} else {
|
|
61
|
-
log.info(` ${pluginName.padEnd(22)} already up to date`);
|
|
62
|
-
}
|
|
63
|
-
} else {
|
|
64
|
-
log.warn(` ${pluginName.padEnd(22)} ${resultLastLine(r)}`);
|
|
46
|
+
const result = await updatePlugin(spec);
|
|
47
|
+
const status = classifyPluginUpdateResult(result);
|
|
48
|
+
|
|
49
|
+
if (status.status === "updated") {
|
|
50
|
+
log.ok(` ${pluginName.padEnd(22)} ${status.message}`);
|
|
51
|
+
continue;
|
|
65
52
|
}
|
|
53
|
+
|
|
54
|
+
if (status.status === "unchanged") {
|
|
55
|
+
log.info(` ${pluginName.padEnd(22)} ${status.message}`);
|
|
56
|
+
continue;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
log.warn(` ${pluginName.padEnd(22)} ${status.message}`);
|
|
66
60
|
}
|
|
67
61
|
|
|
68
|
-
|
|
69
|
-
log.ok("Upgrade complete");
|
|
70
|
-
console.log(color.dim(" Some changes require a Claude Code restart to take effect"));
|
|
62
|
+
printUpgradeSummary();
|
|
71
63
|
}
|