@curdx/flow 2.0.17 → 2.0.19
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/README.zh.md +5 -5
- 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/hooks/scripts/common.sh +35 -0
- package/hooks/scripts/inject-karpathy.sh +7 -9
- package/hooks/scripts/quick-mode-guard.sh +4 -2
- package/hooks/scripts/session-start.sh +4 -8
- package/hooks/scripts/stop-watcher.sh +4 -9
- package/package.json +1 -1
package/cli/uninstall.js
CHANGED
|
@@ -9,7 +9,6 @@ import { homedir } from "node:os";
|
|
|
9
9
|
import {
|
|
10
10
|
color,
|
|
11
11
|
log,
|
|
12
|
-
run,
|
|
13
12
|
resultLastLine,
|
|
14
13
|
resultOutput,
|
|
15
14
|
confirm,
|
|
@@ -20,28 +19,15 @@ import {
|
|
|
20
19
|
import { removeGlobalProtocols, GLOBAL_CLAUDE_MD } from "./protocols.js";
|
|
21
20
|
import { REQUIRED_PLUGINS, RECOMMENDED_PLUGINS, BUNDLED_MCPS } from "./registry.js";
|
|
22
21
|
import {
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
} from "./lib/claude-
|
|
22
|
+
removeMcp,
|
|
23
|
+
removePluginMarketplace,
|
|
24
|
+
uninstallPlugin,
|
|
25
|
+
} from "./lib/claude-ops.js";
|
|
27
26
|
|
|
28
27
|
const HOME = homedir();
|
|
29
28
|
|
|
30
|
-
|
|
31
|
-
const
|
|
32
|
-
name,
|
|
33
|
-
uninstallSpec,
|
|
34
|
-
uninstallArgs: uninstallArgs || [],
|
|
35
|
-
marketplaceId,
|
|
36
|
-
scope,
|
|
37
|
-
}));
|
|
38
|
-
const REQUIRED = REQUIRED_PLUGINS.map(({ name, uninstallSpec, uninstallArgs, marketplaceId, scope }) => ({
|
|
39
|
-
name,
|
|
40
|
-
uninstallSpec,
|
|
41
|
-
uninstallArgs: uninstallArgs || [],
|
|
42
|
-
marketplaceId,
|
|
43
|
-
scope,
|
|
44
|
-
}));
|
|
29
|
+
const RECOMMENDED = RECOMMENDED_PLUGINS.map(toUninstallTarget);
|
|
30
|
+
const REQUIRED = REQUIRED_PLUGINS.map(toUninstallTarget);
|
|
45
31
|
|
|
46
32
|
// Symlinks created by install.js (only cleaned with --purge)
|
|
47
33
|
const MANAGED_SYMLINKS = [
|
|
@@ -56,164 +42,175 @@ export async function uninstall(args = []) {
|
|
|
56
42
|
|
|
57
43
|
log.title("🗑️ CurdX-Flow Uninstaller");
|
|
58
44
|
|
|
59
|
-
// ---------- Step 1: claude CLI present? ----------
|
|
60
45
|
const cv = claudeVersion();
|
|
61
46
|
if (!cv) {
|
|
62
47
|
log.err("claude CLI not found, cannot uninstall plugin.");
|
|
63
48
|
process.exit(1);
|
|
64
49
|
}
|
|
65
50
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
`This will uninstall the ${color.bold("curdx-flow")} plugin. Continue?`,
|
|
70
|
-
false
|
|
71
|
-
);
|
|
72
|
-
if (!ok) {
|
|
73
|
-
log.info("Cancelled");
|
|
74
|
-
return;
|
|
75
|
-
}
|
|
51
|
+
if (!(await confirmUninstall({ yes }))) {
|
|
52
|
+
log.info("Cancelled");
|
|
53
|
+
return;
|
|
76
54
|
}
|
|
77
55
|
|
|
78
|
-
|
|
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() {
|
|
79
76
|
log.blank();
|
|
80
77
|
log.step(1, 4, "Uninstalling curdx-flow plugin...");
|
|
81
|
-
const
|
|
82
|
-
const curdx = installed.find((p) => p.name === "curdx-flow");
|
|
78
|
+
const curdx = listPlugins().find((plugin) => plugin.name === "curdx-flow");
|
|
83
79
|
if (!curdx) {
|
|
84
80
|
log.info("curdx-flow not installed, skipping");
|
|
85
|
-
|
|
86
|
-
const r = await run(
|
|
87
|
-
"claude",
|
|
88
|
-
pluginUninstallArgs({
|
|
89
|
-
scope: "user",
|
|
90
|
-
uninstallSpec: "curdx-flow@curdx-flow-marketplace",
|
|
91
|
-
}),
|
|
92
|
-
{ silent: true }
|
|
93
|
-
);
|
|
94
|
-
if (r.code === 0) {
|
|
95
|
-
log.ok("curdx-flow uninstalled");
|
|
96
|
-
} else {
|
|
97
|
-
log.err(`Uninstall failed: ${resultOutput(r)}`);
|
|
98
|
-
}
|
|
81
|
+
return;
|
|
99
82
|
}
|
|
100
83
|
|
|
101
|
-
|
|
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 }) {
|
|
102
97
|
log.blank();
|
|
103
98
|
log.step(2, 4, "Recommended plugins");
|
|
104
99
|
if (keepRecommended) {
|
|
105
100
|
log.info("Keeping recommended plugins (--keep-recommended)");
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
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)")
|
|
110
125
|
);
|
|
126
|
+
return [];
|
|
127
|
+
}
|
|
111
128
|
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
label: color.bold(r.name),
|
|
124
|
-
value: r.name,
|
|
125
|
-
hint: "",
|
|
126
|
-
}));
|
|
127
|
-
toRemove = await multiSelect(
|
|
128
|
-
"Which recommended plugins to also uninstall? (default: none)",
|
|
129
|
-
choices,
|
|
130
|
-
[] // default: nothing checked
|
|
131
|
-
);
|
|
132
|
-
}
|
|
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
|
+
}
|
|
133
140
|
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
{ silent: true }
|
|
142
|
-
);
|
|
143
|
-
if (r.code === 0) {
|
|
144
|
-
console.log(` ${color.green("✓")} ${rec.name} uninstalled`);
|
|
145
|
-
} else {
|
|
146
|
-
console.log(
|
|
147
|
-
` ${color.red("✗")} ${rec.name} uninstall failed: ${resultLastLine(r)}`
|
|
148
|
-
);
|
|
149
|
-
}
|
|
150
|
-
}
|
|
151
|
-
}
|
|
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;
|
|
152
148
|
}
|
|
153
149
|
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
150
|
+
console.log(
|
|
151
|
+
` ${color.red("✗")} ${entry.name} uninstall failed: ${resultLastLine(result)}`
|
|
152
|
+
);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
async function maybeRemoveBundledMcps({ yes, keepRecommended }) {
|
|
159
156
|
log.blank();
|
|
160
157
|
log.info("Required MCP servers (context7, sequential-thinking)");
|
|
161
158
|
if (keepRecommended || yes) {
|
|
162
159
|
log.info(
|
|
163
160
|
color.dim("--yes or --keep-recommended: keeping user-level MCPs (remove manually with `claude mcp remove <name>`)")
|
|
164
161
|
);
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
)
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
}
|
|
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`);
|
|
181
178
|
} else {
|
|
182
|
-
log.info("
|
|
179
|
+
log.info(` ${mcp.name.padEnd(22)} ${color.dim("not present or already removed")}`);
|
|
183
180
|
}
|
|
184
181
|
}
|
|
182
|
+
}
|
|
185
183
|
|
|
186
|
-
|
|
184
|
+
async function maybeUninstallRequiredPlugins({ yes }) {
|
|
187
185
|
log.blank();
|
|
188
186
|
log.info("Required companion plugins");
|
|
189
187
|
if (yes) {
|
|
190
188
|
log.info(
|
|
191
189
|
color.dim("--yes mode: keeping required companion plugins (use --purge to remove them)")
|
|
192
190
|
);
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
)
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
}
|
|
210
|
-
}
|
|
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`);
|
|
211
207
|
} else {
|
|
212
|
-
log.info("
|
|
208
|
+
log.info(` ${plugin.name.padEnd(22)} ${color.dim("not present or already removed")}`);
|
|
213
209
|
}
|
|
214
210
|
}
|
|
211
|
+
}
|
|
215
212
|
|
|
216
|
-
|
|
213
|
+
async function maybePurgeRuntimeArtifacts({ purge }) {
|
|
217
214
|
log.blank();
|
|
218
215
|
log.step(3, 4, "Runtime symlinks and marketplaces");
|
|
219
216
|
if (!purge) {
|
|
@@ -223,116 +220,139 @@ export async function uninstall(args = []) {
|
|
|
223
220
|
log.info(
|
|
224
221
|
color.dim("Reason: these bun/uv binaries may be used by other tools — confirm before deleting")
|
|
225
222
|
);
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
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)}`);
|
|
246
246
|
}
|
|
247
|
+
}
|
|
248
|
+
}
|
|
247
249
|
|
|
248
|
-
|
|
249
|
-
|
|
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
|
+
);
|
|
250
261
|
continue;
|
|
251
262
|
}
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
);
|
|
258
|
-
continue;
|
|
259
|
-
}
|
|
260
|
-
const target = readlinkSync(link);
|
|
261
|
-
unlinkSync(link);
|
|
262
|
-
log.ok(`Removed symlink ${link} ${color.dim(`(was → ${target})`)}`);
|
|
263
|
-
} catch (err) {
|
|
264
|
-
log.warn(`Failed to remove ${link}: ${err.message}`);
|
|
265
|
-
}
|
|
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}`);
|
|
266
268
|
}
|
|
267
269
|
}
|
|
270
|
+
}
|
|
268
271
|
|
|
269
|
-
|
|
272
|
+
function removeProtocols() {
|
|
270
273
|
log.blank();
|
|
271
274
|
console.log(color.dim("Removing global protocols from ~/.claude/CLAUDE.md..."));
|
|
272
275
|
try {
|
|
273
|
-
const
|
|
274
|
-
if (
|
|
276
|
+
const result = removeGlobalProtocols();
|
|
277
|
+
if (result.action === "removed") {
|
|
275
278
|
log.ok(`Global protocols removed ${color.dim(`(${GLOBAL_CLAUDE_MD})`)}`);
|
|
276
|
-
} else if (
|
|
277
|
-
log.info(
|
|
279
|
+
} else if (result.action === "not-present") {
|
|
280
|
+
log.info("Global protocols not present, skipping");
|
|
278
281
|
} else {
|
|
279
|
-
log.info(
|
|
282
|
+
log.info("~/.claude/CLAUDE.md does not exist, skipping");
|
|
280
283
|
}
|
|
281
284
|
} catch (err) {
|
|
282
285
|
log.warn(`Protocol removal failed: ${err.message}`);
|
|
283
286
|
}
|
|
287
|
+
}
|
|
284
288
|
|
|
285
|
-
|
|
289
|
+
async function maybeRemoveProjectState({ yes }) {
|
|
286
290
|
log.blank();
|
|
287
291
|
log.step(4, 4, "Project state directory");
|
|
288
292
|
const flowDir = join(process.cwd(), ".flow");
|
|
289
293
|
if (!existsSync(flowDir)) {
|
|
290
294
|
log.info(".flow/ does not exist, skipping");
|
|
291
|
-
|
|
295
|
+
return;
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
if (yes) {
|
|
292
299
|
log.info(
|
|
293
300
|
color.dim("--yes mode: keeping .flow/ (contains specs & decisions — confirm by hand before deleting)")
|
|
294
301
|
);
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
)
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
log.err(`Removal failed: ${err.message}`);
|
|
306
|
-
}
|
|
307
|
-
} else {
|
|
308
|
-
log.info("Keeping .flow/");
|
|
309
|
-
}
|
|
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;
|
|
310
312
|
}
|
|
311
313
|
|
|
312
|
-
|
|
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 }) {
|
|
313
323
|
log.blank();
|
|
314
324
|
console.log(color.bold("✅ Uninstall complete"));
|
|
315
|
-
if (
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
)
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
)
|
|
329
|
-
)
|
|
330
|
-
|
|
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
|
+
};
|
|
331
351
|
}
|
|
332
352
|
|
|
333
|
-
function isBrokenSymlink(
|
|
353
|
+
function isBrokenSymlink(pathname) {
|
|
334
354
|
try {
|
|
335
|
-
return lstatSync(
|
|
355
|
+
return lstatSync(pathname).isSymbolicLink();
|
|
336
356
|
} catch {
|
|
337
357
|
return false;
|
|
338
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,11 +23,7 @@ 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 {
|
|
@@ -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) {
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
|
|
3
|
+
has_python3() {
|
|
4
|
+
command -v python3 >/dev/null 2>&1
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
json_escape() {
|
|
8
|
+
local value="${1:-}"
|
|
9
|
+
|
|
10
|
+
if has_python3; then
|
|
11
|
+
printf '%s' "$value" | python3 -c 'import json,sys; print(json.dumps(sys.stdin.read()))'
|
|
12
|
+
return
|
|
13
|
+
fi
|
|
14
|
+
|
|
15
|
+
printf '%s' "$value" \
|
|
16
|
+
| sed 's/\\/\\\\/g; s/"/\\"/g' \
|
|
17
|
+
| awk 'BEGIN{printf "\""} {printf "%s\\n", $0} END{printf "\""}'
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
emit_session_start_context() {
|
|
21
|
+
local context="${1:-}"
|
|
22
|
+
printf '{"hookSpecificOutput":{"hookEventName":"SessionStart","additionalContext":%s}}\n' \
|
|
23
|
+
"$(json_escape "$context")"
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
emit_pretooluse_deny() {
|
|
27
|
+
local reason="${1:-}"
|
|
28
|
+
printf '{"hookSpecificOutput":{"hookEventName":"PreToolUse","permissionDecision":"deny","permissionDecisionReason":%s}}\n' \
|
|
29
|
+
"$(json_escape "$reason")"
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
emit_stop_block() {
|
|
33
|
+
local reason="${1:-}"
|
|
34
|
+
printf '{"decision":"block","reason":%s}\n' "$(json_escape "$reason")"
|
|
35
|
+
}
|
|
@@ -3,14 +3,14 @@
|
|
|
3
3
|
# Injects the L1 baseline (Karpathy 4 principles + mandatory tool rules + 3 red lines)
|
|
4
4
|
# as additionalContext at every session boot (startup, /clear, post-compact).
|
|
5
5
|
#
|
|
6
|
-
# Wired under SessionStart
|
|
7
|
-
#
|
|
8
|
-
# hookSpecificOutput / additionalContext. SessionStart with matcher
|
|
9
|
-
# "startup|clear|compact" gives the same "baseline is always on, even after
|
|
10
|
-
# compaction" property while staying within the supported schema.
|
|
6
|
+
# Wired under SessionStart with matcher "startup|clear|compact" so the
|
|
7
|
+
# baseline survives startup, /clear, and post-compact resume.
|
|
11
8
|
|
|
12
9
|
set -u
|
|
13
10
|
|
|
11
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
12
|
+
. "$SCRIPT_DIR/common.sh"
|
|
13
|
+
|
|
14
14
|
CONTEXT='## CurDX-Flow Mind Baseline (L1 — always on)
|
|
15
15
|
|
|
16
16
|
### 1. Think Before Coding
|
|
@@ -46,10 +46,8 @@ CONTEXT='## CurDX-Flow Mind Baseline (L1 — always on)
|
|
|
46
46
|
2. **Fact-driven**: verify before saying "probably". An unverified attribution is blame-shifting
|
|
47
47
|
3. **Exhaust everything**: before saying "I cannot", complete the systematic 4-stage debugging'
|
|
48
48
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
ESCAPED="$(printf '%s' "$CONTEXT" | python3 -c 'import json,sys; print(json.dumps(sys.stdin.read()))')"
|
|
52
|
-
printf '{"hookSpecificOutput":{"hookEventName":"SessionStart","additionalContext":%s}}\n' "$ESCAPED"
|
|
49
|
+
if has_python3; then
|
|
50
|
+
emit_session_start_context "$CONTEXT"
|
|
53
51
|
fi
|
|
54
52
|
|
|
55
53
|
exit 0
|