@curdx/flow 3.3.1 → 3.4.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/CHANGELOG.md +18 -0
- package/README.md +3 -0
- package/dist/index.mjs +230 -178
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,24 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to `@curdx/flow` are documented here. Format follows [Keep a Changelog](https://keepachangelog.com/) and the project follows [Semantic Versioning](https://semver.org/).
|
|
4
4
|
|
|
5
|
+
## 3.4.0 — 2026-04-27
|
|
6
|
+
|
|
7
|
+
### Added
|
|
8
|
+
|
|
9
|
+
- **`ralph-specum` is now a bundled plugin** — spec-driven development with autonomous task-by-task execution (research → requirements → design → tasks → implement, plus epic triage). Originally authored by [tzachbon](https://github.com/tzachbon/smart-ralph); ralph-specum v4.9.1 has been migrated into this repository as the canonical home and is no longer tracked against upstream. MIT license and authorship are preserved at `plugins/ralph-specum/LICENSE` and `plugins/ralph-specum/NOTICE.md`.
|
|
10
|
+
- **curdx-flow itself is now a Claude Code marketplace** — the repo ships `.claude-plugin/marketplace.json` so Claude CLI can install bundled plugins via `claude plugin marketplace add curdx/curdx-flow` + `claude plugin install ralph-specum@curdx-flow`. The flow installer wires this up automatically; users just run `npx @curdx/flow install` and ralph-specum is pre-checked in the multiselect along with the other not-installed items.
|
|
11
|
+
|
|
12
|
+
### Notes
|
|
13
|
+
|
|
14
|
+
- If you previously installed ralph-specum from the upstream `tzachbon/smart-ralph` marketplace, run `claude plugin uninstall ralph-specum@smart-ralph` before installing this version to avoid a name collision. Going forward, only the `ralph-specum@curdx-flow` build is maintained.
|
|
15
|
+
- Plugin files are not shipped in the npm tarball (`package.json` `files` is unchanged: `["dist", "CHANGELOG.md"]`). They live in the GitHub repo and are pulled by Claude CLI when the marketplace is added — so a `git push` of this repo must precede `npm publish` for a new release to be installable.
|
|
16
|
+
|
|
17
|
+
## 3.3.2 — 2026-04-27
|
|
18
|
+
|
|
19
|
+
### Fixed
|
|
20
|
+
|
|
21
|
+
- **CLAUDE.md sync no longer skipped on the "nothing selected" path** — when a user upgraded flow with all tools already installed and ran `install`, the multiselect would show nothing pre-checked; pressing enter without a selection caused the flow to early-return before reaching the sync step, so the managed block was never added to CLAUDE.md. Each of `install` / `update` / `uninstall` now wraps its body in `try / finally` and runs the sync at the end of any non-cancelled exit (including "nothing to do" paths). User-cancelled flows (Ctrl+C, multiselect cancel, uninstall confirm "no") still skip the sync to respect intent.
|
|
22
|
+
|
|
5
23
|
## 3.3.1 — 2026-04-27
|
|
6
24
|
|
|
7
25
|
### Fixed
|
package/README.md
CHANGED
|
@@ -31,9 +31,12 @@ npx @curdx/flow --lang en # override language
|
|
|
31
31
|
| `claude-mem` | plugin | `thedotmack/claude-mem` |
|
|
32
32
|
| `chrome-devtools-mcp` | plugin | `ChromeDevTools/chrome-devtools-mcp` |
|
|
33
33
|
| `frontend-design` | plugin | `claude-plugins-official` (built-in) |
|
|
34
|
+
| `ralph-specum` | plugin | bundled in this repo — spec-driven dev with autonomous task execution (originally [tzachbon/smart-ralph](https://github.com/tzachbon/smart-ralph), MIT) |
|
|
34
35
|
| `sequential-thinking` | mcp | `@modelcontextprotocol/server-sequential-thinking` |
|
|
35
36
|
| `context7` | mcp | HTTP — `https://mcp.context7.com/mcp` (optional API key) |
|
|
36
37
|
|
|
38
|
+
> If you previously installed ralph-specum from `tzachbon/smart-ralph`, run `claude plugin uninstall ralph-specum@smart-ralph` before installing this version. Only the `ralph-specum@curdx-flow` build is maintained going forward.
|
|
39
|
+
|
|
37
40
|
## What it writes to your filesystem
|
|
38
41
|
|
|
39
42
|
After every successful `install` / `update` / `uninstall`, flow keeps a short managed block in your global `~/.claude/CLAUDE.md` so Claude Code knows at session start which tools are installed and when to use them. The block looks like:
|
package/dist/index.mjs
CHANGED
|
@@ -518,6 +518,35 @@ var frontendDesign = {
|
|
|
518
518
|
};
|
|
519
519
|
var frontend_design_default = frontendDesign;
|
|
520
520
|
|
|
521
|
+
// src/registry/plugins/ralph-specum.ts
|
|
522
|
+
var PLUGIN_ID5 = "ralph-specum@curdx-flow";
|
|
523
|
+
var PLUGIN_NAME3 = "ralph-specum";
|
|
524
|
+
var MARKETPLACE_NAME4 = "curdx-flow";
|
|
525
|
+
var MARKETPLACE_SOURCE4 = "curdx/curdx-flow";
|
|
526
|
+
var ralphSpecum = {
|
|
527
|
+
id: "ralph-specum",
|
|
528
|
+
name: "ralph-specum",
|
|
529
|
+
description: "curdx-flow (originally tzachbon/smart-ralph) \u2014 spec-driven dev with autonomous task execution",
|
|
530
|
+
type: "plugin",
|
|
531
|
+
slashNamespace: "/ralph-specum:*",
|
|
532
|
+
whenToUse: "for spec-driven multi-task work \u2014 research \u2192 requirements \u2192 design \u2192 tasks \u2192 autonomous execution per task. Use when starting a feature that benefits from upfront spec; skip for one-shot fixes or simple edits.",
|
|
533
|
+
marketplaces: () => [MARKETPLACE_NAME4],
|
|
534
|
+
isInstalled: () => isPluginInstalled(PLUGIN_ID5),
|
|
535
|
+
installedVersion: async () => {
|
|
536
|
+
const p10 = await findPlugin(PLUGIN_ID5);
|
|
537
|
+
const v = p10?.version;
|
|
538
|
+
return v && v !== "unknown" ? v : null;
|
|
539
|
+
},
|
|
540
|
+
latestVersion: () => getMarketplacePluginVersion(MARKETPLACE_NAME4, PLUGIN_NAME3),
|
|
541
|
+
install: async (ctx) => {
|
|
542
|
+
await ensureMarketplace(MARKETPLACE_NAME4, MARKETPLACE_SOURCE4, ctx);
|
|
543
|
+
await installPluginById(PLUGIN_ID5, ctx);
|
|
544
|
+
},
|
|
545
|
+
uninstall: (ctx) => uninstallPluginById(PLUGIN_ID5, ctx),
|
|
546
|
+
update: (ctx) => updatePluginById(PLUGIN_ID5, ctx)
|
|
547
|
+
};
|
|
548
|
+
var ralph_specum_default = ralphSpecum;
|
|
549
|
+
|
|
521
550
|
// src/registry/mcps/sequential-thinking.ts
|
|
522
551
|
var MCP_NAME = "sequential-thinking";
|
|
523
552
|
var sequentialThinking = {
|
|
@@ -615,6 +644,7 @@ var PKGS = [
|
|
|
615
644
|
claude_mem_default,
|
|
616
645
|
chrome_devtools_mcp_default,
|
|
617
646
|
frontend_design_default,
|
|
647
|
+
ralph_specum_default,
|
|
618
648
|
sequential_thinking_default,
|
|
619
649
|
context7_default
|
|
620
650
|
];
|
|
@@ -952,48 +982,55 @@ async function maybeRefreshMarketplaces(opts) {
|
|
|
952
982
|
);
|
|
953
983
|
}
|
|
954
984
|
async function installFlow(opts = {}) {
|
|
955
|
-
|
|
956
|
-
const explicit = opts.all || opts.ids && opts.ids.length > 0;
|
|
957
|
-
const candidates = explicit ? selectFromIds(opts) : [...PKGS];
|
|
958
|
-
if (candidates.length === 0) {
|
|
959
|
-
p4.log.info(t("install.nothingSelected"));
|
|
960
|
-
return;
|
|
961
|
-
}
|
|
962
|
-
const stateMap = /* @__PURE__ */ new Map();
|
|
963
|
-
const sp = p4.spinner();
|
|
964
|
-
sp.start(t("state.checking"));
|
|
985
|
+
let userCancelled = false;
|
|
965
986
|
try {
|
|
966
|
-
await
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
);
|
|
972
|
-
} finally {
|
|
973
|
-
sp.stop(t("state.checked", { count: candidates.length }));
|
|
974
|
-
}
|
|
975
|
-
let targets;
|
|
976
|
-
if (explicit) {
|
|
977
|
-
targets = candidates;
|
|
978
|
-
} else {
|
|
979
|
-
const picked = await selectInteractive(stateMap);
|
|
980
|
-
if (picked === null) {
|
|
981
|
-
p4.cancel(t("app.cancelled"));
|
|
987
|
+
await maybeRefreshMarketplaces(opts);
|
|
988
|
+
const explicit = opts.all || opts.ids && opts.ids.length > 0;
|
|
989
|
+
const candidates = explicit ? selectFromIds(opts) : [...PKGS];
|
|
990
|
+
if (candidates.length === 0) {
|
|
991
|
+
p4.log.info(t("install.nothingSelected"));
|
|
982
992
|
return;
|
|
983
993
|
}
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
+
const stateMap = /* @__PURE__ */ new Map();
|
|
995
|
+
const sp = p4.spinner();
|
|
996
|
+
sp.start(t("state.checking"));
|
|
997
|
+
try {
|
|
998
|
+
await Promise.all([listPlugins(), listMcp()]);
|
|
999
|
+
await Promise.all(
|
|
1000
|
+
candidates.map(async (pkg) => {
|
|
1001
|
+
stateMap.set(pkg.id, await deriveState(pkg));
|
|
1002
|
+
})
|
|
1003
|
+
);
|
|
1004
|
+
} finally {
|
|
1005
|
+
sp.stop(t("state.checked", { count: candidates.length }));
|
|
1006
|
+
}
|
|
1007
|
+
let targets;
|
|
1008
|
+
if (explicit) {
|
|
1009
|
+
targets = candidates;
|
|
1010
|
+
} else {
|
|
1011
|
+
const picked = await selectInteractive(stateMap);
|
|
1012
|
+
if (picked === null) {
|
|
1013
|
+
userCancelled = true;
|
|
1014
|
+
p4.cancel(t("app.cancelled"));
|
|
1015
|
+
return;
|
|
1016
|
+
}
|
|
1017
|
+
targets = picked;
|
|
1018
|
+
}
|
|
1019
|
+
if (targets.length === 0) {
|
|
1020
|
+
p4.log.info(t("install.nothingSelected"));
|
|
1021
|
+
return;
|
|
1022
|
+
}
|
|
1023
|
+
const results = [];
|
|
1024
|
+
for (const pkg of targets) {
|
|
1025
|
+
const state = stateMap.get(pkg.id) ?? { kind: "not_installed" };
|
|
1026
|
+
results.push(await runOne(pkg, state, opts));
|
|
1027
|
+
}
|
|
1028
|
+
summarize(results);
|
|
1029
|
+
} finally {
|
|
1030
|
+
if (!userCancelled) {
|
|
1031
|
+
await syncFromState({ skip: opts.noClaudeMd });
|
|
1032
|
+
}
|
|
994
1033
|
}
|
|
995
|
-
summarize(results);
|
|
996
|
-
await syncFromState({ skip: opts.noClaudeMd });
|
|
997
1034
|
}
|
|
998
1035
|
|
|
999
1036
|
// src/flows/uninstall.ts
|
|
@@ -1017,80 +1054,88 @@ async function probeInstalled() {
|
|
|
1017
1054
|
}
|
|
1018
1055
|
}
|
|
1019
1056
|
async function uninstallFlow(opts = {}) {
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
targets
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1057
|
+
let userCancelled = false;
|
|
1058
|
+
try {
|
|
1059
|
+
const installed = await probeInstalled();
|
|
1060
|
+
let targets;
|
|
1061
|
+
if (opts.ids && opts.ids.length > 0) {
|
|
1062
|
+
targets = [];
|
|
1063
|
+
for (const id of opts.ids) {
|
|
1064
|
+
const pkg = findPkg(id);
|
|
1065
|
+
if (!pkg) {
|
|
1066
|
+
p5.log.warn(`Unknown id: ${id}`);
|
|
1067
|
+
continue;
|
|
1068
|
+
}
|
|
1069
|
+
if (!installed.some((x2) => x2.id === pkg.id)) {
|
|
1070
|
+
p5.log.warn(`${pkg.name}: ${t("pkg.notInstalled")}`);
|
|
1071
|
+
continue;
|
|
1072
|
+
}
|
|
1073
|
+
targets.push(pkg);
|
|
1029
1074
|
}
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1075
|
+
} else {
|
|
1076
|
+
if (installed.length === 0) {
|
|
1077
|
+
p5.log.info(t("uninstall.noneInstalled"));
|
|
1078
|
+
return;
|
|
1033
1079
|
}
|
|
1034
|
-
|
|
1080
|
+
const picked = await p5.multiselect({
|
|
1081
|
+
message: t("uninstall.selectPrompt"),
|
|
1082
|
+
options: installed.map((pkg) => ({
|
|
1083
|
+
value: pkg.id,
|
|
1084
|
+
label: `${pkg.name} ${pc2.dim(`(${pkg.type})`)}`,
|
|
1085
|
+
hint: pkg.description
|
|
1086
|
+
})),
|
|
1087
|
+
required: false
|
|
1088
|
+
});
|
|
1089
|
+
if (p5.isCancel(picked)) {
|
|
1090
|
+
userCancelled = true;
|
|
1091
|
+
p5.cancel(t("app.cancelled"));
|
|
1092
|
+
return;
|
|
1093
|
+
}
|
|
1094
|
+
targets = picked.map((id) => findPkg(id)).filter((x2) => Boolean(x2));
|
|
1035
1095
|
}
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
p5.log.info(t("uninstall.noneInstalled"));
|
|
1096
|
+
if (targets.length === 0) {
|
|
1097
|
+
p5.log.info(t("install.nothingSelected"));
|
|
1039
1098
|
return;
|
|
1040
1099
|
}
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
p5.cancel(t("app.cancelled"));
|
|
1052
|
-
return;
|
|
1053
|
-
}
|
|
1054
|
-
targets = picked.map((id) => findPkg(id)).filter((x2) => Boolean(x2));
|
|
1055
|
-
}
|
|
1056
|
-
if (targets.length === 0) {
|
|
1057
|
-
p5.log.info(t("install.nothingSelected"));
|
|
1058
|
-
return;
|
|
1059
|
-
}
|
|
1060
|
-
if (!opts.yes) {
|
|
1061
|
-
const ok2 = await p5.confirm({
|
|
1062
|
-
message: t("uninstall.confirm", { count: targets.length }),
|
|
1063
|
-
initialValue: false
|
|
1064
|
-
});
|
|
1065
|
-
if (p5.isCancel(ok2) || ok2 === false) {
|
|
1066
|
-
p5.cancel(t("app.cancelled"));
|
|
1067
|
-
return;
|
|
1100
|
+
if (!opts.yes) {
|
|
1101
|
+
const ok2 = await p5.confirm({
|
|
1102
|
+
message: t("uninstall.confirm", { count: targets.length }),
|
|
1103
|
+
initialValue: false
|
|
1104
|
+
});
|
|
1105
|
+
if (p5.isCancel(ok2) || ok2 === false) {
|
|
1106
|
+
userCancelled = true;
|
|
1107
|
+
p5.cancel(t("app.cancelled"));
|
|
1108
|
+
return;
|
|
1109
|
+
}
|
|
1068
1110
|
}
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
log5.error(`${t("uninstall.failed", { name: pkg.name })}
|
|
1111
|
+
const results = [];
|
|
1112
|
+
for (const pkg of targets) {
|
|
1113
|
+
const log5 = p5.taskLog({ title: t("uninstall.starting", { name: pkg.name }) });
|
|
1114
|
+
try {
|
|
1115
|
+
await pkg.uninstall({ log: log5, config: {}, t });
|
|
1116
|
+
log5.success(t("uninstall.success", { name: pkg.name }));
|
|
1117
|
+
results.push({ id: pkg.id, status: "ok" });
|
|
1118
|
+
} catch (err) {
|
|
1119
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
1120
|
+
log5.error(`${t("uninstall.failed", { name: pkg.name })}
|
|
1080
1121
|
${msg}`);
|
|
1081
|
-
|
|
1122
|
+
results.push({ id: pkg.id, status: "fail", message: msg });
|
|
1123
|
+
}
|
|
1124
|
+
}
|
|
1125
|
+
const ok = results.filter((r) => r.status === "ok").length;
|
|
1126
|
+
const fail = results.filter((r) => r.status === "fail").length;
|
|
1127
|
+
p5.note(
|
|
1128
|
+
[
|
|
1129
|
+
pc2.green(t("install.summaryOk", { count: ok })),
|
|
1130
|
+
pc2.red(t("install.summaryFail", { count: fail }))
|
|
1131
|
+
].join("\n"),
|
|
1132
|
+
t("install.summaryTitle")
|
|
1133
|
+
);
|
|
1134
|
+
} finally {
|
|
1135
|
+
if (!userCancelled) {
|
|
1136
|
+
await syncFromState({ skip: opts.noClaudeMd });
|
|
1082
1137
|
}
|
|
1083
1138
|
}
|
|
1084
|
-
const ok = results.filter((r) => r.status === "ok").length;
|
|
1085
|
-
const fail = results.filter((r) => r.status === "fail").length;
|
|
1086
|
-
p5.note(
|
|
1087
|
-
[
|
|
1088
|
-
pc2.green(t("install.summaryOk", { count: ok })),
|
|
1089
|
-
pc2.red(t("install.summaryFail", { count: fail }))
|
|
1090
|
-
].join("\n"),
|
|
1091
|
-
t("install.summaryTitle")
|
|
1092
|
-
);
|
|
1093
|
-
await syncFromState({ skip: opts.noClaudeMd });
|
|
1094
1139
|
}
|
|
1095
1140
|
|
|
1096
1141
|
// src/flows/update.ts
|
|
@@ -1114,89 +1159,96 @@ async function probeInstalled2() {
|
|
|
1114
1159
|
}
|
|
1115
1160
|
}
|
|
1116
1161
|
async function updateFlow(opts = {}) {
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
targets
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1162
|
+
let userCancelled = false;
|
|
1163
|
+
try {
|
|
1164
|
+
const installed = await probeInstalled2();
|
|
1165
|
+
if (installed.length === 0) {
|
|
1166
|
+
p6.log.info(t("update.noneInstalled"));
|
|
1167
|
+
return;
|
|
1168
|
+
}
|
|
1169
|
+
let targets;
|
|
1170
|
+
if (opts.all) {
|
|
1171
|
+
targets = installed;
|
|
1172
|
+
} else if (opts.ids && opts.ids.length > 0) {
|
|
1173
|
+
targets = [];
|
|
1174
|
+
for (const id of opts.ids) {
|
|
1175
|
+
const pkg = findPkg(id);
|
|
1176
|
+
if (!pkg) {
|
|
1177
|
+
p6.log.warn(`Unknown id: ${id}`);
|
|
1178
|
+
continue;
|
|
1179
|
+
}
|
|
1180
|
+
if (!installed.some((x2) => x2.id === pkg.id)) {
|
|
1181
|
+
p6.log.warn(`${pkg.name}: ${t("pkg.notInstalled")}`);
|
|
1182
|
+
continue;
|
|
1183
|
+
}
|
|
1184
|
+
targets.push(pkg);
|
|
1132
1185
|
}
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1186
|
+
} else {
|
|
1187
|
+
const picked = await p6.multiselect({
|
|
1188
|
+
message: t("update.selectPrompt"),
|
|
1189
|
+
options: installed.map((pkg) => ({
|
|
1190
|
+
value: pkg.id,
|
|
1191
|
+
label: `${pkg.name} ${pc3.dim(`(${pkg.type})`)}`,
|
|
1192
|
+
hint: pkg.description
|
|
1193
|
+
})),
|
|
1194
|
+
required: false
|
|
1195
|
+
});
|
|
1196
|
+
if (p6.isCancel(picked)) {
|
|
1197
|
+
userCancelled = true;
|
|
1198
|
+
p6.cancel(t("app.cancelled"));
|
|
1199
|
+
return;
|
|
1136
1200
|
}
|
|
1137
|
-
targets.
|
|
1201
|
+
targets = picked.map((id) => findPkg(id)).filter((x2) => Boolean(x2));
|
|
1138
1202
|
}
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
message: t("update.selectPrompt"),
|
|
1142
|
-
options: installed.map((pkg) => ({
|
|
1143
|
-
value: pkg.id,
|
|
1144
|
-
label: `${pkg.name} ${pc3.dim(`(${pkg.type})`)}`,
|
|
1145
|
-
hint: pkg.description
|
|
1146
|
-
})),
|
|
1147
|
-
required: false
|
|
1148
|
-
});
|
|
1149
|
-
if (p6.isCancel(picked)) {
|
|
1150
|
-
p6.cancel(t("app.cancelled"));
|
|
1203
|
+
if (targets.length === 0) {
|
|
1204
|
+
p6.log.info(t("install.nothingSelected"));
|
|
1151
1205
|
return;
|
|
1152
1206
|
}
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
const results = [];
|
|
1160
|
-
for (const pkg of targets) {
|
|
1161
|
-
if (pkg.id === "sequential-thinking") {
|
|
1162
|
-
p6.log.info(t("update.mcpAutoNote", { name: pkg.name }));
|
|
1163
|
-
results.push({ id: pkg.id, status: "noop" });
|
|
1164
|
-
continue;
|
|
1165
|
-
}
|
|
1166
|
-
if (pkg.id === "context7") {
|
|
1167
|
-
p6.log.info(t("update.context7Note"));
|
|
1168
|
-
results.push({ id: pkg.id, status: "noop" });
|
|
1169
|
-
continue;
|
|
1170
|
-
}
|
|
1171
|
-
const log5 = p6.taskLog({ title: t("update.starting", { name: pkg.name }) });
|
|
1172
|
-
try {
|
|
1173
|
-
if (pkg.update) {
|
|
1174
|
-
await pkg.update({ log: log5, config: {}, t });
|
|
1175
|
-
} else {
|
|
1176
|
-
await pkg.uninstall({ log: log5, config: {}, t });
|
|
1177
|
-
await pkg.install({ log: log5, config: {}, t });
|
|
1207
|
+
const results = [];
|
|
1208
|
+
for (const pkg of targets) {
|
|
1209
|
+
if (pkg.id === "sequential-thinking") {
|
|
1210
|
+
p6.log.info(t("update.mcpAutoNote", { name: pkg.name }));
|
|
1211
|
+
results.push({ id: pkg.id, status: "noop" });
|
|
1212
|
+
continue;
|
|
1178
1213
|
}
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1214
|
+
if (pkg.id === "context7") {
|
|
1215
|
+
p6.log.info(t("update.context7Note"));
|
|
1216
|
+
results.push({ id: pkg.id, status: "noop" });
|
|
1217
|
+
continue;
|
|
1218
|
+
}
|
|
1219
|
+
const log5 = p6.taskLog({ title: t("update.starting", { name: pkg.name }) });
|
|
1220
|
+
try {
|
|
1221
|
+
if (pkg.update) {
|
|
1222
|
+
await pkg.update({ log: log5, config: {}, t });
|
|
1223
|
+
} else {
|
|
1224
|
+
await pkg.uninstall({ log: log5, config: {}, t });
|
|
1225
|
+
await pkg.install({ log: log5, config: {}, t });
|
|
1226
|
+
}
|
|
1227
|
+
log5.success(t("update.success", { name: pkg.name }));
|
|
1228
|
+
results.push({ id: pkg.id, status: "ok" });
|
|
1229
|
+
} catch (err) {
|
|
1230
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
1231
|
+
log5.error(`${t("update.failed", { name: pkg.name })}
|
|
1184
1232
|
${msg}`);
|
|
1185
|
-
|
|
1233
|
+
results.push({ id: pkg.id, status: "fail", message: msg });
|
|
1234
|
+
}
|
|
1235
|
+
}
|
|
1236
|
+
const ok = results.filter((r) => r.status === "ok").length;
|
|
1237
|
+
const fail = results.filter((r) => r.status === "fail").length;
|
|
1238
|
+
const noop = results.filter((r) => r.status === "noop").length;
|
|
1239
|
+
p6.note(
|
|
1240
|
+
[
|
|
1241
|
+
pc3.green(t("install.summaryOk", { count: ok })),
|
|
1242
|
+
pc3.red(t("install.summaryFail", { count: fail })),
|
|
1243
|
+
pc3.dim(`noop: ${noop}`)
|
|
1244
|
+
].join("\n"),
|
|
1245
|
+
t("install.summaryTitle")
|
|
1246
|
+
);
|
|
1247
|
+
} finally {
|
|
1248
|
+
if (!userCancelled) {
|
|
1249
|
+
await syncFromState({ skip: opts.noClaudeMd });
|
|
1186
1250
|
}
|
|
1187
1251
|
}
|
|
1188
|
-
const ok = results.filter((r) => r.status === "ok").length;
|
|
1189
|
-
const fail = results.filter((r) => r.status === "fail").length;
|
|
1190
|
-
const noop = results.filter((r) => r.status === "noop").length;
|
|
1191
|
-
p6.note(
|
|
1192
|
-
[
|
|
1193
|
-
pc3.green(t("install.summaryOk", { count: ok })),
|
|
1194
|
-
pc3.red(t("install.summaryFail", { count: fail })),
|
|
1195
|
-
pc3.dim(`noop: ${noop}`)
|
|
1196
|
-
].join("\n"),
|
|
1197
|
-
t("install.summaryTitle")
|
|
1198
|
-
);
|
|
1199
|
-
await syncFromState({ skip: opts.noClaudeMd });
|
|
1200
1252
|
}
|
|
1201
1253
|
|
|
1202
1254
|
// src/flows/status.ts
|
|
@@ -1361,7 +1413,7 @@ var SUBCOMMANDS = /* @__PURE__ */ new Set(["install", "uninstall", "update", "st
|
|
|
1361
1413
|
var root = defineCommand({
|
|
1362
1414
|
meta: {
|
|
1363
1415
|
name: "@curdx/flow",
|
|
1364
|
-
version: "3.3.
|
|
1416
|
+
version: "3.3.2",
|
|
1365
1417
|
description: "Interactive installer for Claude Code plugins and MCP servers"
|
|
1366
1418
|
},
|
|
1367
1419
|
args: sharedArgs,
|