@curdx/flow 3.1.0 → 3.2.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 +17 -0
- package/dist/index.mjs +209 -39
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,23 @@
|
|
|
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.2.0 — 2026-04-26
|
|
6
|
+
|
|
7
|
+
### Added
|
|
8
|
+
|
|
9
|
+
- **Version-aware install** — `flow install` now detects already-installed items with newer versions available upstream and presents a third state `↑ installed v3.0.0 → v3.2.3 available` in the multiselect. Items with updates are pre-selected by default alongside not-installed items, so a single Enter ships "install missing + upgrade outdated".
|
|
10
|
+
- **Smart dispatch** — selected items route to the right operation:
|
|
11
|
+
- not installed → `install` (full)
|
|
12
|
+
- update available → `update` (incremental, via `claude plugin update <id>`)
|
|
13
|
+
- already installed but selected → reinstall confirmation prompt (uninstall + install)
|
|
14
|
+
- **Marketplace cache refresh** — install flow runs `claude plugin marketplace update <name>` for each pkg's marketplace before reading `latestVersion`. Skipped per-marketplace if its cache mtime is within 1 hour. New flag `--no-refresh` to opt out entirely (CI / offline use).
|
|
15
|
+
- **`flow status --json` enriched** — now includes `installedVersion`, `latestVersion`, and `updateAvailable` fields for each item, so external scripts can detect upgrade candidates without parsing the multiselect UI.
|
|
16
|
+
- **`Pkg.installedVersion` / `Pkg.latestVersion` / `Pkg.marketplaces`** — optional methods on the registry interface. Implemented for `pua` and `claude-mem` (the two items whose marketplaces declare `version` in `.claude-plugin/marketplace.json`). Other items gracefully fall back to the boolean installed/not-installed display when versions aren't available.
|
|
17
|
+
|
|
18
|
+
### Notes
|
|
19
|
+
|
|
20
|
+
Of the 6 bundled items, only `pua` and `claude-mem` expose comparable versions. `chrome-devtools-mcp` and `frontend-design` (Anthropic official marketplace) don't declare `version` in marketplace metadata and so always render as "installed" without version. Both MCP servers (`sequential-thinking`, `context7`) have no installed-version concept (`npx -y` auto-fetches latest each launch / remote HTTP) and behave the same way.
|
|
21
|
+
|
|
5
22
|
## 3.1.0 — 2026-04-26
|
|
6
23
|
|
|
7
24
|
Major rewrite preserving the same goal (one-command installer for Claude Code plugins and MCP servers) with a cleaner internal architecture and broader coverage.
|
package/dist/index.mjs
CHANGED
|
@@ -24,6 +24,12 @@ var messages = {
|
|
|
24
24
|
"pkg.installed": "\u5DF2\u5B89\u88C5",
|
|
25
25
|
"pkg.notInstalled": "\u672A\u5B89\u88C5",
|
|
26
26
|
"pkg.unknown": "\u672A\u77E5",
|
|
27
|
+
"pkg.upToDateWithVersion": "\u5DF2\u5B89\u88C5 v{version}",
|
|
28
|
+
"pkg.updateAvailable": "\u5DF2\u5B89\u88C5 v{current} \u2192 v{latest} \u53EF\u7528",
|
|
29
|
+
"marketplace.refreshing": "\u5237\u65B0 marketplace \u7F13\u5B58\u2026",
|
|
30
|
+
"marketplace.refreshed": "\u5DF2\u5237\u65B0 {count} \u4E2A marketplace",
|
|
31
|
+
"marketplace.refreshSkipped": "marketplace \u7F13\u5B58\u4ECD\u662F\u65B0\u9C9C\u7684\uFF0C\u8DF3\u8FC7\u5237\u65B0",
|
|
32
|
+
"install.updating": '\u66F4\u65B0 "{name}" \u5230 v{version}',
|
|
27
33
|
"install.selectPrompt": "\u52FE\u9009\u8981\u5B89\u88C5 / \u91CD\u88C5\u7684\u6761\u76EE\uFF08\u9ED8\u8BA4\u52FE\u9009\u672A\u5B89\u88C5\u7684\uFF09",
|
|
28
34
|
"install.nothingSelected": "\u6CA1\u6709\u9009\u62E9\u4EFB\u4F55\u6761\u76EE\uFF0C\u5DF2\u9000\u51FA\u3002",
|
|
29
35
|
"install.confirmReinstall": '"{name}" \u5DF2\u5B89\u88C5\uFF0C\u662F\u5426\u91CD\u65B0\u5B89\u88C5\uFF08\u5148\u5378\u8F7D\u518D\u5B89\u88C5\uFF09\uFF1F',
|
|
@@ -81,6 +87,12 @@ var messages2 = {
|
|
|
81
87
|
"pkg.installed": "installed",
|
|
82
88
|
"pkg.notInstalled": "not installed",
|
|
83
89
|
"pkg.unknown": "unknown",
|
|
90
|
+
"pkg.upToDateWithVersion": "installed v{version}",
|
|
91
|
+
"pkg.updateAvailable": "v{current} \u2192 v{latest} available",
|
|
92
|
+
"marketplace.refreshing": "Refreshing marketplace caches\u2026",
|
|
93
|
+
"marketplace.refreshed": "Refreshed {count} marketplace(s)",
|
|
94
|
+
"marketplace.refreshSkipped": "Marketplace caches are fresh, skipping refresh",
|
|
95
|
+
"install.updating": 'Updating "{name}" to v{version}',
|
|
84
96
|
"install.selectPrompt": "Select items to install / reinstall (not-installed are pre-selected)",
|
|
85
97
|
"install.nothingSelected": "Nothing selected. Exiting.",
|
|
86
98
|
"install.confirmReinstall": '"{name}" is already installed. Reinstall (uninstall then install)?',
|
|
@@ -172,6 +184,11 @@ import * as p7 from "@clack/prompts";
|
|
|
172
184
|
import * as p3 from "@clack/prompts";
|
|
173
185
|
import pc from "picocolors";
|
|
174
186
|
|
|
187
|
+
// src/runner/state.ts
|
|
188
|
+
import { promises as fs } from "fs";
|
|
189
|
+
import path from "path";
|
|
190
|
+
import os from "os";
|
|
191
|
+
|
|
175
192
|
// src/runner/exec.ts
|
|
176
193
|
import { x } from "tinyexec";
|
|
177
194
|
async function run(cmd, args) {
|
|
@@ -298,6 +315,55 @@ async function isMcpInstalled(name) {
|
|
|
298
315
|
const list = await listMcp();
|
|
299
316
|
return list.some((m) => m.name === name);
|
|
300
317
|
}
|
|
318
|
+
async function findPlugin(id) {
|
|
319
|
+
const list = await listPlugins();
|
|
320
|
+
return list.find((p9) => p9.id === id);
|
|
321
|
+
}
|
|
322
|
+
var marketplaceJsonCache = /* @__PURE__ */ new Map();
|
|
323
|
+
function marketplaceDir(name) {
|
|
324
|
+
return path.join(os.homedir(), ".claude", "plugins", "marketplaces", name);
|
|
325
|
+
}
|
|
326
|
+
async function readMarketplaceJson(name) {
|
|
327
|
+
if (marketplaceJsonCache.has(name)) return marketplaceJsonCache.get(name) ?? null;
|
|
328
|
+
const file = path.join(marketplaceDir(name), ".claude-plugin", "marketplace.json");
|
|
329
|
+
try {
|
|
330
|
+
const raw = await fs.readFile(file, "utf8");
|
|
331
|
+
const parsed = JSON.parse(raw);
|
|
332
|
+
marketplaceJsonCache.set(name, parsed);
|
|
333
|
+
return parsed;
|
|
334
|
+
} catch {
|
|
335
|
+
marketplaceJsonCache.set(name, null);
|
|
336
|
+
return null;
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
async function getMarketplacePluginVersion(marketplaceName, pluginName) {
|
|
340
|
+
const m = await readMarketplaceJson(marketplaceName);
|
|
341
|
+
if (!m?.plugins) return null;
|
|
342
|
+
const entry = m.plugins.find((p9) => p9.name === pluginName);
|
|
343
|
+
return entry?.version ?? null;
|
|
344
|
+
}
|
|
345
|
+
var REFRESH_TTL_MS = 60 * 60 * 1e3;
|
|
346
|
+
async function shouldSkipRefresh(name) {
|
|
347
|
+
try {
|
|
348
|
+
const stat = await fs.stat(marketplaceDir(name));
|
|
349
|
+
return Date.now() - stat.mtimeMs < REFRESH_TTL_MS;
|
|
350
|
+
} catch {
|
|
351
|
+
return false;
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
async function refreshMarketplaces(names) {
|
|
355
|
+
const unique = [...new Set(names)];
|
|
356
|
+
const toRefresh = [];
|
|
357
|
+
for (const name of unique) {
|
|
358
|
+
if (!await shouldSkipRefresh(name)) toRefresh.push(name);
|
|
359
|
+
}
|
|
360
|
+
if (toRefresh.length === 0) return [];
|
|
361
|
+
await Promise.all(
|
|
362
|
+
toRefresh.map((name) => run("claude", ["plugin", "marketplace", "update", name]))
|
|
363
|
+
);
|
|
364
|
+
for (const name of toRefresh) marketplaceJsonCache.delete(name);
|
|
365
|
+
return toRefresh;
|
|
366
|
+
}
|
|
301
367
|
|
|
302
368
|
// src/registry/plugins/_helpers.ts
|
|
303
369
|
async function ensureMarketplace(marketplaceName, marketplaceSource, ctx) {
|
|
@@ -325,6 +391,7 @@ async function updatePluginById(pluginId, ctx) {
|
|
|
325
391
|
|
|
326
392
|
// src/registry/plugins/pua.ts
|
|
327
393
|
var PLUGIN_ID = "pua@pua-skills";
|
|
394
|
+
var PLUGIN_NAME = "pua";
|
|
328
395
|
var MARKETPLACE_NAME = "pua-skills";
|
|
329
396
|
var MARKETPLACE_SOURCE = "tanweai/pua";
|
|
330
397
|
var pua = {
|
|
@@ -332,7 +399,14 @@ var pua = {
|
|
|
332
399
|
name: "pua",
|
|
333
400
|
description: "tanweai/pua \u2014 Chinese Claude Code skills bundle",
|
|
334
401
|
type: "plugin",
|
|
402
|
+
marketplaces: () => [MARKETPLACE_NAME],
|
|
335
403
|
isInstalled: () => isPluginInstalled(PLUGIN_ID),
|
|
404
|
+
installedVersion: async () => {
|
|
405
|
+
const p9 = await findPlugin(PLUGIN_ID);
|
|
406
|
+
const v = p9?.version;
|
|
407
|
+
return v && v !== "unknown" ? v : null;
|
|
408
|
+
},
|
|
409
|
+
latestVersion: () => getMarketplacePluginVersion(MARKETPLACE_NAME, PLUGIN_NAME),
|
|
336
410
|
install: async (ctx) => {
|
|
337
411
|
await ensureMarketplace(MARKETPLACE_NAME, MARKETPLACE_SOURCE, ctx);
|
|
338
412
|
await installPluginById(PLUGIN_ID, ctx);
|
|
@@ -344,6 +418,7 @@ var pua_default = pua;
|
|
|
344
418
|
|
|
345
419
|
// src/registry/plugins/claude-mem.ts
|
|
346
420
|
var PLUGIN_ID2 = "claude-mem@thedotmack";
|
|
421
|
+
var PLUGIN_NAME2 = "claude-mem";
|
|
347
422
|
var MARKETPLACE_NAME2 = "thedotmack";
|
|
348
423
|
var MARKETPLACE_SOURCE2 = "thedotmack/claude-mem";
|
|
349
424
|
var claudeMem = {
|
|
@@ -351,7 +426,14 @@ var claudeMem = {
|
|
|
351
426
|
name: "claude-mem",
|
|
352
427
|
description: "thedotmack/claude-mem \u2014 persistent cross-session memory for Claude Code",
|
|
353
428
|
type: "plugin",
|
|
429
|
+
marketplaces: () => [MARKETPLACE_NAME2],
|
|
354
430
|
isInstalled: () => isPluginInstalled(PLUGIN_ID2),
|
|
431
|
+
installedVersion: async () => {
|
|
432
|
+
const p9 = await findPlugin(PLUGIN_ID2);
|
|
433
|
+
const v = p9?.version;
|
|
434
|
+
return v && v !== "unknown" ? v : null;
|
|
435
|
+
},
|
|
436
|
+
latestVersion: () => getMarketplacePluginVersion(MARKETPLACE_NAME2, PLUGIN_NAME2),
|
|
355
437
|
install: async (ctx) => {
|
|
356
438
|
await ensureMarketplace(MARKETPLACE_NAME2, MARKETPLACE_SOURCE2, ctx);
|
|
357
439
|
await installPluginById(PLUGIN_ID2, ctx);
|
|
@@ -517,14 +599,41 @@ function findPkg(id) {
|
|
|
517
599
|
}
|
|
518
600
|
|
|
519
601
|
// src/flows/install.ts
|
|
520
|
-
async function
|
|
521
|
-
|
|
522
|
-
const
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
602
|
+
async function deriveState(pkg) {
|
|
603
|
+
if (!await pkg.isInstalled()) return { kind: "not_installed" };
|
|
604
|
+
const [installed, latest] = await Promise.all([
|
|
605
|
+
pkg.installedVersion?.() ?? Promise.resolve(null),
|
|
606
|
+
pkg.latestVersion?.() ?? Promise.resolve(null)
|
|
607
|
+
]);
|
|
608
|
+
if (installed && latest && installed !== latest) {
|
|
609
|
+
return { kind: "update_available", current: installed, latest };
|
|
610
|
+
}
|
|
611
|
+
return { kind: "up_to_date", version: installed };
|
|
612
|
+
}
|
|
613
|
+
function stateLabel(pkg, s) {
|
|
614
|
+
const head = `${pkg.name} ${pc.dim(`(${pkg.type})`)}`;
|
|
615
|
+
switch (s.kind) {
|
|
616
|
+
case "not_installed":
|
|
617
|
+
return `${head} ${pc.yellow(`\u2717 ${t("pkg.notInstalled")}`)}`;
|
|
618
|
+
case "up_to_date":
|
|
619
|
+
return `${head} ${pc.green(
|
|
620
|
+
s.version ? `\u2713 ${t("pkg.upToDateWithVersion", { version: s.version })}` : `\u2713 ${t("pkg.installed")}`
|
|
621
|
+
)}`;
|
|
622
|
+
case "update_available":
|
|
623
|
+
return `${head} ${pc.cyan(
|
|
624
|
+
`\u2191 ${t("pkg.updateAvailable", { current: s.current, latest: s.latest })}`
|
|
625
|
+
)}`;
|
|
626
|
+
}
|
|
627
|
+
}
|
|
628
|
+
async function selectInteractive(states) {
|
|
629
|
+
const options = PKGS.map((pkg) => {
|
|
630
|
+
const s = states.get(pkg.id);
|
|
631
|
+
return { value: pkg.id, label: stateLabel(pkg, s), hint: pkg.description };
|
|
632
|
+
});
|
|
633
|
+
const initialValues = PKGS.filter((pkg) => {
|
|
634
|
+
const s = states.get(pkg.id);
|
|
635
|
+
return s.kind === "not_installed" || s.kind === "update_available";
|
|
636
|
+
}).map((pkg) => pkg.id);
|
|
528
637
|
const picked = await p3.multiselect({
|
|
529
638
|
message: t("install.selectPrompt"),
|
|
530
639
|
options,
|
|
@@ -545,12 +654,14 @@ function selectFromIds(opts) {
|
|
|
545
654
|
}
|
|
546
655
|
return found;
|
|
547
656
|
}
|
|
548
|
-
async function runOne(pkg,
|
|
549
|
-
let
|
|
550
|
-
if (
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
657
|
+
async function runOne(pkg, state, opts) {
|
|
658
|
+
let mode;
|
|
659
|
+
if (state.kind === "not_installed") {
|
|
660
|
+
mode = "install";
|
|
661
|
+
} else if (state.kind === "update_available") {
|
|
662
|
+
mode = "update";
|
|
663
|
+
} else {
|
|
664
|
+
if (!opts.yes) {
|
|
554
665
|
const ans = await p3.confirm({
|
|
555
666
|
message: t("install.confirmReinstall", { name: pkg.name }),
|
|
556
667
|
initialValue: false
|
|
@@ -558,8 +669,8 @@ async function runOne(pkg, opts, alreadyInstalled) {
|
|
|
558
669
|
if (p3.isCancel(ans) || ans === false) {
|
|
559
670
|
return { id: pkg.id, status: "skip", message: t("install.skippedReinstall", { name: pkg.name }) };
|
|
560
671
|
}
|
|
561
|
-
reinstall = true;
|
|
562
672
|
}
|
|
673
|
+
mode = "reinstall";
|
|
563
674
|
}
|
|
564
675
|
if (pkg.prereqCheck) {
|
|
565
676
|
const r = await pkg.prereqCheck(t);
|
|
@@ -569,19 +680,35 @@ async function runOne(pkg, opts, alreadyInstalled) {
|
|
|
569
680
|
}
|
|
570
681
|
}
|
|
571
682
|
let config = {};
|
|
572
|
-
if (pkg.configPrompts) {
|
|
683
|
+
if (pkg.configPrompts && mode !== "update") {
|
|
573
684
|
const cfg = await pkg.configPrompts({ t });
|
|
574
685
|
if (cfg === null) return { id: pkg.id, status: "skip", message: t("app.cancelled") };
|
|
575
686
|
config = cfg;
|
|
576
687
|
}
|
|
577
|
-
const
|
|
688
|
+
const titleKey = mode === "update" ? "install.updating" : "install.starting";
|
|
689
|
+
const titleVars = { name: pkg.name };
|
|
690
|
+
if (mode === "update" && state.kind === "update_available") {
|
|
691
|
+
titleVars["version"] = state.latest;
|
|
692
|
+
}
|
|
693
|
+
const log4 = p3.taskLog({ title: t(titleKey, titleVars) });
|
|
578
694
|
try {
|
|
579
|
-
if (reinstall) {
|
|
695
|
+
if (mode === "reinstall") {
|
|
580
696
|
log4.message(t("reinstall.uninstalling"));
|
|
581
697
|
await pkg.uninstall({ log: log4, config, t });
|
|
582
698
|
log4.message(t("reinstall.installing"));
|
|
699
|
+
await pkg.install({ log: log4, config, t });
|
|
700
|
+
} else if (mode === "update") {
|
|
701
|
+
if (pkg.update) {
|
|
702
|
+
await pkg.update({ log: log4, config, t });
|
|
703
|
+
} else {
|
|
704
|
+
log4.message(t("reinstall.uninstalling"));
|
|
705
|
+
await pkg.uninstall({ log: log4, config, t });
|
|
706
|
+
log4.message(t("reinstall.installing"));
|
|
707
|
+
await pkg.install({ log: log4, config, t });
|
|
708
|
+
}
|
|
709
|
+
} else {
|
|
710
|
+
await pkg.install({ log: log4, config, t });
|
|
583
711
|
}
|
|
584
|
-
await pkg.install({ log: log4, config, t });
|
|
585
712
|
log4.success(t("install.success", { name: pkg.name }));
|
|
586
713
|
return { id: pkg.id, status: "ok" };
|
|
587
714
|
} catch (err) {
|
|
@@ -606,27 +733,53 @@ function summarize(results) {
|
|
|
606
733
|
];
|
|
607
734
|
p3.note(lines.join("\n"), t("install.summaryTitle"));
|
|
608
735
|
}
|
|
736
|
+
async function maybeRefreshMarketplaces(opts) {
|
|
737
|
+
if (opts.noRefresh) return;
|
|
738
|
+
const names = /* @__PURE__ */ new Set();
|
|
739
|
+
for (const pkg of PKGS) {
|
|
740
|
+
if (pkg.marketplaces) for (const n of pkg.marketplaces()) names.add(n);
|
|
741
|
+
}
|
|
742
|
+
if (names.size === 0) return;
|
|
743
|
+
const sp = p3.spinner();
|
|
744
|
+
sp.start(t("marketplace.refreshing"));
|
|
745
|
+
const refreshed = await refreshMarketplaces([...names]);
|
|
746
|
+
sp.stop(
|
|
747
|
+
refreshed.length > 0 ? t("marketplace.refreshed", { count: refreshed.length }) : t("marketplace.refreshSkipped")
|
|
748
|
+
);
|
|
749
|
+
}
|
|
609
750
|
async function installFlow(opts = {}) {
|
|
751
|
+
await maybeRefreshMarketplaces(opts);
|
|
610
752
|
const explicit = opts.all || opts.ids && opts.ids.length > 0;
|
|
611
|
-
const
|
|
612
|
-
if (
|
|
613
|
-
p3.cancel(t("app.cancelled"));
|
|
614
|
-
return;
|
|
615
|
-
}
|
|
616
|
-
if (targets.length === 0) {
|
|
753
|
+
const candidates = explicit ? selectFromIds(opts) : [...PKGS];
|
|
754
|
+
if (candidates.length === 0) {
|
|
617
755
|
p3.log.info(t("install.nothingSelected"));
|
|
618
756
|
return;
|
|
619
757
|
}
|
|
620
|
-
const
|
|
758
|
+
const stateMap = /* @__PURE__ */ new Map();
|
|
621
759
|
await Promise.all(
|
|
622
|
-
|
|
623
|
-
|
|
760
|
+
candidates.map(async (pkg) => {
|
|
761
|
+
stateMap.set(pkg.id, await deriveState(pkg));
|
|
624
762
|
})
|
|
625
763
|
);
|
|
764
|
+
let targets;
|
|
765
|
+
if (explicit) {
|
|
766
|
+
targets = candidates;
|
|
767
|
+
} else {
|
|
768
|
+
const picked = await selectInteractive(stateMap);
|
|
769
|
+
if (picked === null) {
|
|
770
|
+
p3.cancel(t("app.cancelled"));
|
|
771
|
+
return;
|
|
772
|
+
}
|
|
773
|
+
targets = picked;
|
|
774
|
+
}
|
|
775
|
+
if (targets.length === 0) {
|
|
776
|
+
p3.log.info(t("install.nothingSelected"));
|
|
777
|
+
return;
|
|
778
|
+
}
|
|
626
779
|
const results = [];
|
|
627
780
|
for (const pkg of targets) {
|
|
628
|
-
const
|
|
629
|
-
results.push(await runOne(pkg,
|
|
781
|
+
const state = stateMap.get(pkg.id) ?? { kind: "not_installed" };
|
|
782
|
+
results.push(await runOne(pkg, state, opts));
|
|
630
783
|
}
|
|
631
784
|
summarize(results);
|
|
632
785
|
}
|
|
@@ -811,12 +964,23 @@ import * as p6 from "@clack/prompts";
|
|
|
811
964
|
import pc4 from "picocolors";
|
|
812
965
|
async function statusFlow(opts = {}) {
|
|
813
966
|
const states = await Promise.all(
|
|
814
|
-
PKGS.map(async (pkg) =>
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
967
|
+
PKGS.map(async (pkg) => {
|
|
968
|
+
const installed = await pkg.isInstalled();
|
|
969
|
+
const installedVersion = installed && pkg.installedVersion ? await pkg.installedVersion() : null;
|
|
970
|
+
const latestVersion = pkg.latestVersion ? await pkg.latestVersion() : null;
|
|
971
|
+
const updateAvailable = Boolean(
|
|
972
|
+
installed && installedVersion && latestVersion && installedVersion !== latestVersion
|
|
973
|
+
);
|
|
974
|
+
return {
|
|
975
|
+
id: pkg.id,
|
|
976
|
+
name: pkg.name,
|
|
977
|
+
type: pkg.type,
|
|
978
|
+
installed,
|
|
979
|
+
installedVersion,
|
|
980
|
+
latestVersion,
|
|
981
|
+
updateAvailable
|
|
982
|
+
};
|
|
983
|
+
})
|
|
820
984
|
);
|
|
821
985
|
if (opts.json) {
|
|
822
986
|
process.stdout.write(JSON.stringify(states, null, 2) + "\n");
|
|
@@ -872,18 +1036,24 @@ var sharedArgs = {
|
|
|
872
1036
|
lang: { type: "string", description: "Override language: zh or en" }
|
|
873
1037
|
};
|
|
874
1038
|
var installCmd = defineCommand({
|
|
875
|
-
meta: { name: "install", description: "Install or
|
|
1039
|
+
meta: { name: "install", description: "Install, reinstall, or update plugins / MCP servers" },
|
|
876
1040
|
args: {
|
|
877
1041
|
...sharedArgs,
|
|
878
1042
|
all: { type: "boolean", description: "Install all known items" },
|
|
879
1043
|
yes: { type: "boolean", description: "Skip reinstall confirmation (assume yes)" },
|
|
1044
|
+
"no-refresh": { type: "boolean", description: "Skip refreshing marketplace caches" },
|
|
880
1045
|
ids: { type: "positional", required: false, description: "Item ids", default: "" }
|
|
881
1046
|
},
|
|
882
1047
|
async run({ args }) {
|
|
883
1048
|
await initLanguage(parseLang(args.lang));
|
|
884
1049
|
p8.intro(t("app.intro"));
|
|
885
1050
|
const ids = collectPositional(args);
|
|
886
|
-
await installFlow({
|
|
1051
|
+
await installFlow({
|
|
1052
|
+
ids,
|
|
1053
|
+
all: Boolean(args.all),
|
|
1054
|
+
yes: Boolean(args.yes),
|
|
1055
|
+
noRefresh: Boolean(args["no-refresh"])
|
|
1056
|
+
});
|
|
887
1057
|
p8.outro(t("app.outro"));
|
|
888
1058
|
}
|
|
889
1059
|
});
|
|
@@ -934,7 +1104,7 @@ var SUBCOMMANDS = /* @__PURE__ */ new Set(["install", "uninstall", "update", "st
|
|
|
934
1104
|
var root = defineCommand({
|
|
935
1105
|
meta: {
|
|
936
1106
|
name: "@curdx/flow",
|
|
937
|
-
version: "3.
|
|
1107
|
+
version: "3.2.0",
|
|
938
1108
|
description: "Interactive installer for Claude Code plugins and MCP servers"
|
|
939
1109
|
},
|
|
940
1110
|
args: sharedArgs,
|