@curdx/flow 2.0.16 → 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 +42 -57
- package/cli/install-curdx-plugin.js +28 -52
- package/cli/lib/claude-ops.js +47 -0
- package/cli/lib/doctor-report.js +189 -0
- package/cli/lib/process.js +11 -0
- package/cli/uninstall.js +235 -213
- package/cli/upgrade.js +8 -12
- package/cli/utils.js +1 -1
- package/package.json +1 -1
package/cli/uninstall.js
CHANGED
|
@@ -9,7 +9,8 @@ import { homedir } from "node:os";
|
|
|
9
9
|
import {
|
|
10
10
|
color,
|
|
11
11
|
log,
|
|
12
|
-
|
|
12
|
+
resultLastLine,
|
|
13
|
+
resultOutput,
|
|
13
14
|
confirm,
|
|
14
15
|
multiSelect,
|
|
15
16
|
claudeVersion,
|
|
@@ -18,28 +19,15 @@ import {
|
|
|
18
19
|
import { removeGlobalProtocols, GLOBAL_CLAUDE_MD } from "./protocols.js";
|
|
19
20
|
import { REQUIRED_PLUGINS, RECOMMENDED_PLUGINS, BUNDLED_MCPS } from "./registry.js";
|
|
20
21
|
import {
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
} from "./lib/claude-
|
|
22
|
+
removeMcp,
|
|
23
|
+
removePluginMarketplace,
|
|
24
|
+
uninstallPlugin,
|
|
25
|
+
} from "./lib/claude-ops.js";
|
|
25
26
|
|
|
26
27
|
const HOME = homedir();
|
|
27
28
|
|
|
28
|
-
|
|
29
|
-
const
|
|
30
|
-
name,
|
|
31
|
-
uninstallSpec,
|
|
32
|
-
uninstallArgs: uninstallArgs || [],
|
|
33
|
-
marketplaceId,
|
|
34
|
-
scope,
|
|
35
|
-
}));
|
|
36
|
-
const REQUIRED = REQUIRED_PLUGINS.map(({ name, uninstallSpec, uninstallArgs, marketplaceId, scope }) => ({
|
|
37
|
-
name,
|
|
38
|
-
uninstallSpec,
|
|
39
|
-
uninstallArgs: uninstallArgs || [],
|
|
40
|
-
marketplaceId,
|
|
41
|
-
scope,
|
|
42
|
-
}));
|
|
29
|
+
const RECOMMENDED = RECOMMENDED_PLUGINS.map(toUninstallTarget);
|
|
30
|
+
const REQUIRED = REQUIRED_PLUGINS.map(toUninstallTarget);
|
|
43
31
|
|
|
44
32
|
// Symlinks created by install.js (only cleaned with --purge)
|
|
45
33
|
const MANAGED_SYMLINKS = [
|
|
@@ -54,164 +42,175 @@ export async function uninstall(args = []) {
|
|
|
54
42
|
|
|
55
43
|
log.title("🗑️ CurdX-Flow Uninstaller");
|
|
56
44
|
|
|
57
|
-
// ---------- Step 1: claude CLI present? ----------
|
|
58
45
|
const cv = claudeVersion();
|
|
59
46
|
if (!cv) {
|
|
60
47
|
log.err("claude CLI not found, cannot uninstall plugin.");
|
|
61
48
|
process.exit(1);
|
|
62
49
|
}
|
|
63
50
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
`This will uninstall the ${color.bold("curdx-flow")} plugin. Continue?`,
|
|
68
|
-
false
|
|
69
|
-
);
|
|
70
|
-
if (!ok) {
|
|
71
|
-
log.info("Cancelled");
|
|
72
|
-
return;
|
|
73
|
-
}
|
|
51
|
+
if (!(await confirmUninstall({ yes }))) {
|
|
52
|
+
log.info("Cancelled");
|
|
53
|
+
return;
|
|
74
54
|
}
|
|
75
55
|
|
|
76
|
-
|
|
56
|
+
await uninstallCurdxFlowPlugin();
|
|
57
|
+
await maybeUninstallRecommendedPlugins({ yes, keepRecommended });
|
|
58
|
+
await maybeRemoveBundledMcps({ yes, keepRecommended });
|
|
59
|
+
await maybeUninstallRequiredPlugins({ yes });
|
|
60
|
+
await maybePurgeRuntimeArtifacts({ purge });
|
|
61
|
+
removeProtocols();
|
|
62
|
+
await maybeRemoveProjectState({ yes });
|
|
63
|
+
|
|
64
|
+
printSummary({ purge });
|
|
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
|
+
);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
async function uninstallCurdxFlowPlugin() {
|
|
77
76
|
log.blank();
|
|
78
77
|
log.step(1, 4, "Uninstalling curdx-flow plugin...");
|
|
79
|
-
const
|
|
80
|
-
const curdx = installed.find((p) => p.name === "curdx-flow");
|
|
78
|
+
const curdx = listPlugins().find((plugin) => plugin.name === "curdx-flow");
|
|
81
79
|
if (!curdx) {
|
|
82
80
|
log.info("curdx-flow not installed, skipping");
|
|
83
|
-
|
|
84
|
-
const r = await run(
|
|
85
|
-
"claude",
|
|
86
|
-
pluginUninstallArgs({
|
|
87
|
-
scope: "user",
|
|
88
|
-
uninstallSpec: "curdx-flow@curdx-flow-marketplace",
|
|
89
|
-
}),
|
|
90
|
-
{ silent: true }
|
|
91
|
-
);
|
|
92
|
-
if (r.code === 0) {
|
|
93
|
-
log.ok("curdx-flow uninstalled");
|
|
94
|
-
} else {
|
|
95
|
-
log.err(`Uninstall failed: ${r.stderr.trim() || r.stdout.trim()}`);
|
|
96
|
-
}
|
|
81
|
+
return;
|
|
97
82
|
}
|
|
98
83
|
|
|
99
|
-
|
|
84
|
+
const result = await uninstallPlugin({
|
|
85
|
+
scope: "user",
|
|
86
|
+
uninstallSpec: "curdx-flow@curdx-flow-marketplace",
|
|
87
|
+
});
|
|
88
|
+
if (result.code === 0) {
|
|
89
|
+
log.ok("curdx-flow uninstalled");
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
log.err(`Uninstall failed: ${resultOutput(result)}`);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
async function maybeUninstallRecommendedPlugins({ yes, keepRecommended }) {
|
|
100
97
|
log.blank();
|
|
101
98
|
log.step(2, 4, "Recommended plugins");
|
|
102
99
|
if (keepRecommended) {
|
|
103
100
|
log.info("Keeping recommended plugins (--keep-recommended)");
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
const currentlyInstalled = listPlugins();
|
|
105
|
+
const present = RECOMMENDED.filter((entry) =>
|
|
106
|
+
currentlyInstalled.some((plugin) => plugin.name === entry.name)
|
|
107
|
+
);
|
|
108
|
+
if (present.length === 0) {
|
|
109
|
+
log.info("No installed recommended plugins");
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
const selected = await selectPluginsToRemove({ yes, present });
|
|
114
|
+
for (const name of selected) {
|
|
115
|
+
const entry = present.find((plugin) => plugin.name === name);
|
|
116
|
+
if (!entry) continue;
|
|
117
|
+
await uninstallNamedPlugin(entry);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
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)")
|
|
108
125
|
);
|
|
126
|
+
return [];
|
|
127
|
+
}
|
|
109
128
|
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
label: color.bold(r.name),
|
|
122
|
-
value: r.name,
|
|
123
|
-
hint: "",
|
|
124
|
-
}));
|
|
125
|
-
toRemove = await multiSelect(
|
|
126
|
-
"Which recommended plugins to also uninstall? (default: none)",
|
|
127
|
-
choices,
|
|
128
|
-
[] // default: nothing checked
|
|
129
|
-
);
|
|
130
|
-
}
|
|
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
|
+
}
|
|
131
140
|
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
{ silent: true }
|
|
140
|
-
);
|
|
141
|
-
if (r.code === 0) {
|
|
142
|
-
console.log(` ${color.green("✓")} ${rec.name} uninstalled`);
|
|
143
|
-
} else {
|
|
144
|
-
console.log(
|
|
145
|
-
` ${color.red("✗")} ${rec.name} uninstall failed: ${r.stderr.trim().split("\n").pop()}`
|
|
146
|
-
);
|
|
147
|
-
}
|
|
148
|
-
}
|
|
149
|
-
}
|
|
141
|
+
async function uninstallNamedPlugin(entry) {
|
|
142
|
+
log.blank();
|
|
143
|
+
console.log(` ${color.cyan("▸")} Uninstalling ${color.bold(entry.name)}...`);
|
|
144
|
+
const result = await uninstallPlugin(entry);
|
|
145
|
+
if (result.code === 0) {
|
|
146
|
+
console.log(` ${color.green("✓")} ${entry.name} uninstalled`);
|
|
147
|
+
return;
|
|
150
148
|
}
|
|
151
149
|
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
150
|
+
console.log(
|
|
151
|
+
` ${color.red("✗")} ${entry.name} uninstall failed: ${resultLastLine(result)}`
|
|
152
|
+
);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
async function maybeRemoveBundledMcps({ yes, keepRecommended }) {
|
|
157
156
|
log.blank();
|
|
158
157
|
log.info("Required MCP servers (context7, sequential-thinking)");
|
|
159
158
|
if (keepRecommended || yes) {
|
|
160
159
|
log.info(
|
|
161
160
|
color.dim("--yes or --keep-recommended: keeping user-level MCPs (remove manually with `claude mcp remove <name>`)")
|
|
162
161
|
);
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
)
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
}
|
|
162
|
+
return;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
const removeMcps = await confirm(
|
|
166
|
+
`Remove user-level MCPs registered by install (${BUNDLED_MCPS.map((mcp) => mcp.name).join(", ")})? ${color.dim("(keeps them if other tools depend on them)")}`,
|
|
167
|
+
false
|
|
168
|
+
);
|
|
169
|
+
if (!removeMcps) {
|
|
170
|
+
log.info("Keeping user-level MCPs");
|
|
171
|
+
return;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
for (const mcp of BUNDLED_MCPS) {
|
|
175
|
+
const result = await removeMcp({ name: mcp.name });
|
|
176
|
+
if (result.code === 0) {
|
|
177
|
+
log.ok(` ${mcp.name.padEnd(22)} removed`);
|
|
179
178
|
} else {
|
|
180
|
-
log.info("
|
|
179
|
+
log.info(` ${mcp.name.padEnd(22)} ${color.dim("not present or already removed")}`);
|
|
181
180
|
}
|
|
182
181
|
}
|
|
182
|
+
}
|
|
183
183
|
|
|
184
|
-
|
|
184
|
+
async function maybeUninstallRequiredPlugins({ yes }) {
|
|
185
185
|
log.blank();
|
|
186
186
|
log.info("Required companion plugins");
|
|
187
187
|
if (yes) {
|
|
188
188
|
log.info(
|
|
189
189
|
color.dim("--yes mode: keeping required companion plugins (use --purge to remove them)")
|
|
190
190
|
);
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
)
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
}
|
|
208
|
-
}
|
|
191
|
+
return;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
const removeRequired = await confirm(
|
|
195
|
+
`Remove required companion plugins (${REQUIRED.map((plugin) => plugin.name).join(", ")})? ${color.dim("(keeps shared tools available if other workflows depend on them)")}`,
|
|
196
|
+
false
|
|
197
|
+
);
|
|
198
|
+
if (!removeRequired) {
|
|
199
|
+
log.info("Keeping required companion plugins");
|
|
200
|
+
return;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
for (const plugin of REQUIRED) {
|
|
204
|
+
const result = await uninstallPlugin(plugin);
|
|
205
|
+
if (result.code === 0) {
|
|
206
|
+
log.ok(` ${plugin.name.padEnd(22)} uninstalled`);
|
|
209
207
|
} else {
|
|
210
|
-
log.info("
|
|
208
|
+
log.info(` ${plugin.name.padEnd(22)} ${color.dim("not present or already removed")}`);
|
|
211
209
|
}
|
|
212
210
|
}
|
|
211
|
+
}
|
|
213
212
|
|
|
214
|
-
|
|
213
|
+
async function maybePurgeRuntimeArtifacts({ purge }) {
|
|
215
214
|
log.blank();
|
|
216
215
|
log.step(3, 4, "Runtime symlinks and marketplaces");
|
|
217
216
|
if (!purge) {
|
|
@@ -221,116 +220,139 @@ export async function uninstall(args = []) {
|
|
|
221
220
|
log.info(
|
|
222
221
|
color.dim("Reason: these bun/uv binaries may be used by other tools — confirm before deleting")
|
|
223
222
|
);
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
223
|
+
return;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
await purgeManagedMarketplaces();
|
|
227
|
+
removeManagedSymlinks();
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
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
|
+
];
|
|
239
|
+
|
|
240
|
+
for (const marketplaceId of marketplaceIds) {
|
|
241
|
+
const result = await removePluginMarketplace(marketplaceId);
|
|
242
|
+
if (result.code === 0) {
|
|
243
|
+
log.ok(`Removed marketplace ${marketplaceId}`);
|
|
244
|
+
} else if (!result.stderr.includes("not found")) {
|
|
245
|
+
log.warn(`Failed to remove marketplace ${marketplaceId}: ${resultLastLine(result)}`);
|
|
244
246
|
}
|
|
247
|
+
}
|
|
248
|
+
}
|
|
245
249
|
|
|
246
|
-
|
|
247
|
-
|
|
250
|
+
function removeManagedSymlinks() {
|
|
251
|
+
for (const link of MANAGED_SYMLINKS) {
|
|
252
|
+
if (!existsSync(link) && !isBrokenSymlink(link)) {
|
|
253
|
+
continue;
|
|
254
|
+
}
|
|
255
|
+
try {
|
|
256
|
+
const stat = lstatSync(link);
|
|
257
|
+
if (!stat.isSymbolicLink()) {
|
|
258
|
+
log.warn(
|
|
259
|
+
`${link} is not a symlink (likely a real file placed by the user), skipping`
|
|
260
|
+
);
|
|
248
261
|
continue;
|
|
249
262
|
}
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
);
|
|
256
|
-
continue;
|
|
257
|
-
}
|
|
258
|
-
const target = readlinkSync(link);
|
|
259
|
-
unlinkSync(link);
|
|
260
|
-
log.ok(`Removed symlink ${link} ${color.dim(`(was → ${target})`)}`);
|
|
261
|
-
} catch (err) {
|
|
262
|
-
log.warn(`Failed to remove ${link}: ${err.message}`);
|
|
263
|
-
}
|
|
263
|
+
const target = readlinkSync(link);
|
|
264
|
+
unlinkSync(link);
|
|
265
|
+
log.ok(`Removed symlink ${link} ${color.dim(`(was → ${target})`)}`);
|
|
266
|
+
} catch (err) {
|
|
267
|
+
log.warn(`Failed to remove ${link}: ${err.message}`);
|
|
264
268
|
}
|
|
265
269
|
}
|
|
270
|
+
}
|
|
266
271
|
|
|
267
|
-
|
|
272
|
+
function removeProtocols() {
|
|
268
273
|
log.blank();
|
|
269
274
|
console.log(color.dim("Removing global protocols from ~/.claude/CLAUDE.md..."));
|
|
270
275
|
try {
|
|
271
|
-
const
|
|
272
|
-
if (
|
|
276
|
+
const result = removeGlobalProtocols();
|
|
277
|
+
if (result.action === "removed") {
|
|
273
278
|
log.ok(`Global protocols removed ${color.dim(`(${GLOBAL_CLAUDE_MD})`)}`);
|
|
274
|
-
} else if (
|
|
275
|
-
log.info(
|
|
279
|
+
} else if (result.action === "not-present") {
|
|
280
|
+
log.info("Global protocols not present, skipping");
|
|
276
281
|
} else {
|
|
277
|
-
log.info(
|
|
282
|
+
log.info("~/.claude/CLAUDE.md does not exist, skipping");
|
|
278
283
|
}
|
|
279
284
|
} catch (err) {
|
|
280
285
|
log.warn(`Protocol removal failed: ${err.message}`);
|
|
281
286
|
}
|
|
287
|
+
}
|
|
282
288
|
|
|
283
|
-
|
|
289
|
+
async function maybeRemoveProjectState({ yes }) {
|
|
284
290
|
log.blank();
|
|
285
291
|
log.step(4, 4, "Project state directory");
|
|
286
292
|
const flowDir = join(process.cwd(), ".flow");
|
|
287
293
|
if (!existsSync(flowDir)) {
|
|
288
294
|
log.info(".flow/ does not exist, skipping");
|
|
289
|
-
|
|
295
|
+
return;
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
if (yes) {
|
|
290
299
|
log.info(
|
|
291
300
|
color.dim("--yes mode: keeping .flow/ (contains specs & decisions — confirm by hand before deleting)")
|
|
292
301
|
);
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
)
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
log.err(`Removal failed: ${err.message}`);
|
|
304
|
-
}
|
|
305
|
-
} else {
|
|
306
|
-
log.info("Keeping .flow/");
|
|
307
|
-
}
|
|
302
|
+
return;
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
const ok = await confirm(
|
|
306
|
+
`${color.red("DANGER:")} delete the ${color.bold(".flow/")} directory of the current project? ${color.dim("(includes all specs / decisions, not recoverable)")}`,
|
|
307
|
+
false
|
|
308
|
+
);
|
|
309
|
+
if (!ok) {
|
|
310
|
+
log.info("Keeping .flow/");
|
|
311
|
+
return;
|
|
308
312
|
}
|
|
309
313
|
|
|
310
|
-
|
|
314
|
+
try {
|
|
315
|
+
rmSync(flowDir, { recursive: true, force: true });
|
|
316
|
+
log.ok(`Removed ${flowDir}`);
|
|
317
|
+
} catch (err) {
|
|
318
|
+
log.err(`Removal failed: ${err.message}`);
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
function printSummary({ purge }) {
|
|
311
323
|
log.blank();
|
|
312
324
|
console.log(color.bold("✅ Uninstall complete"));
|
|
313
|
-
if (
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
)
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
)
|
|
327
|
-
)
|
|
328
|
-
|
|
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
|
+
function toUninstallTarget(entry) {
|
|
344
|
+
return {
|
|
345
|
+
name: entry.name,
|
|
346
|
+
uninstallSpec: entry.uninstallSpec,
|
|
347
|
+
uninstallArgs: entry.uninstallArgs || [],
|
|
348
|
+
marketplaceId: entry.marketplaceId,
|
|
349
|
+
scope: entry.scope,
|
|
350
|
+
};
|
|
329
351
|
}
|
|
330
352
|
|
|
331
|
-
function isBrokenSymlink(
|
|
353
|
+
function isBrokenSymlink(pathname) {
|
|
332
354
|
try {
|
|
333
|
-
return lstatSync(
|
|
355
|
+
return lstatSync(pathname).isSymbolicLink();
|
|
334
356
|
} catch {
|
|
335
357
|
return false;
|
|
336
358
|
}
|
package/cli/upgrade.js
CHANGED
|
@@ -2,15 +2,15 @@
|
|
|
2
2
|
* upgrade command — update curdx-flow + recommended plugins to latest.
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
|
-
import { color, log,
|
|
5
|
+
import { color, log, listPlugins, claudeVersion, resultLastLine } from "./utils.js";
|
|
6
6
|
import {
|
|
7
7
|
PLUGINS_TO_UPDATE,
|
|
8
8
|
MARKETPLACES_TO_REFRESH,
|
|
9
9
|
} from "./registry.js";
|
|
10
10
|
import {
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
} from "./lib/claude-
|
|
11
|
+
updatePlugin,
|
|
12
|
+
updatePluginMarketplace,
|
|
13
|
+
} from "./lib/claude-ops.js";
|
|
14
14
|
|
|
15
15
|
export async function upgrade(args = []) {
|
|
16
16
|
log.title("⬆️ CurdX-Flow upgrade");
|
|
@@ -23,17 +23,13 @@ export async function upgrade(args = []) {
|
|
|
23
23
|
// Refresh marketplaces first (derived from cli/registry.js)
|
|
24
24
|
log.step(1, 2, "Refreshing marketplaces...");
|
|
25
25
|
for (const mp of MARKETPLACES_TO_REFRESH) {
|
|
26
|
-
const r = await
|
|
27
|
-
"claude",
|
|
28
|
-
pluginMarketplaceUpdateArgs(mp),
|
|
29
|
-
{ silent: true }
|
|
30
|
-
);
|
|
26
|
+
const r = await updatePluginMarketplace(mp);
|
|
31
27
|
if (r.code === 0) {
|
|
32
28
|
log.ok(` ${mp}`);
|
|
33
29
|
} else {
|
|
34
30
|
// Not a fatal — might not be added
|
|
35
31
|
if (!r.stderr.includes("not found")) {
|
|
36
|
-
log.warn(` ${mp}: ${r
|
|
32
|
+
log.warn(` ${mp}: ${resultLastLine(r)}`);
|
|
37
33
|
}
|
|
38
34
|
}
|
|
39
35
|
}
|
|
@@ -51,7 +47,7 @@ export async function upgrade(args = []) {
|
|
|
51
47
|
continue;
|
|
52
48
|
}
|
|
53
49
|
|
|
54
|
-
const r = await
|
|
50
|
+
const r = await updatePlugin(spec);
|
|
55
51
|
if (r.code === 0) {
|
|
56
52
|
const updated = r.stdout.includes("updated from");
|
|
57
53
|
if (updated) {
|
|
@@ -65,7 +61,7 @@ export async function upgrade(args = []) {
|
|
|
65
61
|
log.info(` ${pluginName.padEnd(22)} already up to date`);
|
|
66
62
|
}
|
|
67
63
|
} else {
|
|
68
|
-
log.warn(` ${pluginName.padEnd(22)} ${r
|
|
64
|
+
log.warn(` ${pluginName.padEnd(22)} ${resultLastLine(r)}`);
|
|
69
65
|
}
|
|
70
66
|
}
|
|
71
67
|
|
package/cli/utils.js
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
|
|
8
8
|
export { VERSION } from "./lib/version.js";
|
|
9
9
|
export { color, log } from "./lib/logging.js";
|
|
10
|
-
export { has, run, runSync } from "./lib/process.js";
|
|
10
|
+
export { has, resultLastLine, resultOutput, run, runSync } from "./lib/process.js";
|
|
11
11
|
export {
|
|
12
12
|
confirm,
|
|
13
13
|
intro,
|