@curdx/flow 3.3.0 → 3.3.1

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 CHANGED
@@ -2,6 +2,13 @@
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.3.1 — 2026-04-27
6
+
7
+ ### Fixed
8
+
9
+ - **Silent stalls between phases** — added spinners to the previously-silent windows where flow shells out to `claude plugin list --json` and `claude mcp list` (the latter performs an MCP server health check and can take 5-15 seconds). Affected sites: `install` (state-derivation between marketplace refresh and the multiselect), `update` and `uninstall` (state-derivation at flow entry), and the post-flow CLAUDE.md sync (after install/update/uninstall busts the cache, sync re-queries state). Each now shows `Checking installed state… (claude plugin list / mcp list)` with a result line so the run no longer feels frozen.
10
+ - **CLAUDE.md sync feedback** — replaced the post-hoc `p.log.info` line with a live spinner that converts to a final status line on completion, matching the marketplace-refresh and per-item install UX.
11
+
5
12
  ## 3.3.0 — 2026-04-27
6
13
 
7
14
  ### Added
package/dist/index.mjs CHANGED
@@ -67,6 +67,9 @@ var messages = {
67
67
  "chrome.prereqChrome": "\u9700\u8981\u672C\u673A\u5DF2\u5B89\u88C5 Chrome\uFF08chrome-devtools-mcp \u4F1A\u8C03\u7528\u672C\u5730\u6D4F\u89C8\u5668\uFF09",
68
68
  "reinstall.uninstalling": "\u5148\u5378\u8F7D\u65E7\u7248\u672C\u2026",
69
69
  "reinstall.installing": "\u5B89\u88C5\u65B0\u7248\u672C\u2026",
70
+ "state.checking": "\u68C0\u67E5\u5DF2\u5B89\u88C5\u72B6\u6001\u2026\uFF08claude plugin list / mcp list\uFF09",
71
+ "state.checked": "\u5DF2\u68C0\u67E5 {count} \u9879",
72
+ "claudeMd.syncing": "\u540C\u6B65 ~/.claude/CLAUDE.md \u2026",
70
73
  "claudeMd.synced": "CLAUDE.md \u5DF2\u66F4\u65B0\uFF08{path}\uFF09",
71
74
  "claudeMd.unchanged": "CLAUDE.md \u5DF2\u662F\u6700\u65B0",
72
75
  "claudeMd.removed": "\u5DF2\u4ECE CLAUDE.md \u79FB\u9664 @curdx/flow \u533A\u5757",
@@ -135,6 +138,9 @@ var messages2 = {
135
138
  "chrome.prereqChrome": "Requires Chrome installed locally (chrome-devtools-mcp drives the local browser)",
136
139
  "reinstall.uninstalling": "Uninstalling old version\u2026",
137
140
  "reinstall.installing": "Installing new version\u2026",
141
+ "state.checking": "Checking installed state\u2026 (claude plugin list / mcp list)",
142
+ "state.checked": "Checked {count} item(s)",
143
+ "claudeMd.syncing": "Syncing ~/.claude/CLAUDE.md\u2026",
138
144
  "claudeMd.synced": "CLAUDE.md updated ({path})",
139
145
  "claudeMd.unchanged": "CLAUDE.md already up to date",
140
146
  "claudeMd.removed": "Removed @curdx/flow block from CLAUDE.md",
@@ -769,23 +775,29 @@ async function syncClaudeMd(opts) {
769
775
  }
770
776
  }
771
777
  async function syncFromState(opts) {
772
- const r = await syncClaudeMd(opts);
778
+ if (opts?.skip) {
779
+ p3.log.info(t("claudeMd.skipped"));
780
+ return;
781
+ }
782
+ const sp = p3.spinner();
783
+ sp.start(t("claudeMd.syncing"));
784
+ const r = await syncClaudeMd();
773
785
  switch (r.status) {
774
786
  case "skipped":
775
- p3.log.info(t("claudeMd.skipped"));
787
+ sp.stop(t("claudeMd.skipped"));
776
788
  return;
777
789
  case "unchanged":
778
- p3.log.info(t("claudeMd.unchanged"));
790
+ sp.stop(t("claudeMd.unchanged"));
779
791
  return;
780
792
  case "created":
781
793
  case "updated":
782
- p3.log.info(t("claudeMd.synced", { path: r.path }));
794
+ sp.stop(t("claudeMd.synced", { path: r.path }));
783
795
  return;
784
796
  case "removed":
785
- p3.log.info(t("claudeMd.removed"));
797
+ sp.stop(t("claudeMd.removed"));
786
798
  return;
787
799
  case "failed":
788
- p3.log.warn(t("claudeMd.failed", { error: r.error ?? "unknown" }));
800
+ sp.stop(t("claudeMd.failed", { error: r.error ?? "unknown" }));
789
801
  return;
790
802
  }
791
803
  }
@@ -948,11 +960,18 @@ async function installFlow(opts = {}) {
948
960
  return;
949
961
  }
950
962
  const stateMap = /* @__PURE__ */ new Map();
951
- await Promise.all(
952
- candidates.map(async (pkg) => {
953
- stateMap.set(pkg.id, await deriveState(pkg));
954
- })
955
- );
963
+ const sp = p4.spinner();
964
+ sp.start(t("state.checking"));
965
+ try {
966
+ await Promise.all([listPlugins(), listMcp()]);
967
+ await Promise.all(
968
+ candidates.map(async (pkg) => {
969
+ stateMap.set(pkg.id, await deriveState(pkg));
970
+ })
971
+ );
972
+ } finally {
973
+ sp.stop(t("state.checked", { count: candidates.length }));
974
+ }
956
975
  let targets;
957
976
  if (explicit) {
958
977
  targets = candidates;
@@ -984,8 +1003,21 @@ async function getInstalled() {
984
1003
  const states = await Promise.all(PKGS.map(async (pkg) => ({ pkg, installed: await pkg.isInstalled() })));
985
1004
  return states.filter((s) => s.installed).map((s) => s.pkg);
986
1005
  }
1006
+ async function probeInstalled() {
1007
+ const sp = p5.spinner();
1008
+ sp.start(t("state.checking"));
1009
+ try {
1010
+ await Promise.all([listPlugins(), listMcp()]);
1011
+ const installed = await getInstalled();
1012
+ sp.stop(t("state.checked", { count: installed.length }));
1013
+ return installed;
1014
+ } catch (err) {
1015
+ sp.stop(t("state.checked", { count: 0 }));
1016
+ throw err;
1017
+ }
1018
+ }
987
1019
  async function uninstallFlow(opts = {}) {
988
- const installed = await getInstalled();
1020
+ const installed = await probeInstalled();
989
1021
  let targets;
990
1022
  if (opts.ids && opts.ids.length > 0) {
991
1023
  targets = [];
@@ -1068,8 +1100,21 @@ async function getInstalled2() {
1068
1100
  const states = await Promise.all(PKGS.map(async (pkg) => ({ pkg, installed: await pkg.isInstalled() })));
1069
1101
  return states.filter((s) => s.installed).map((s) => s.pkg);
1070
1102
  }
1103
+ async function probeInstalled2() {
1104
+ const sp = p6.spinner();
1105
+ sp.start(t("state.checking"));
1106
+ try {
1107
+ await Promise.all([listPlugins(), listMcp()]);
1108
+ const installed = await getInstalled2();
1109
+ sp.stop(t("state.checked", { count: installed.length }));
1110
+ return installed;
1111
+ } catch (err) {
1112
+ sp.stop(t("state.checked", { count: 0 }));
1113
+ throw err;
1114
+ }
1115
+ }
1071
1116
  async function updateFlow(opts = {}) {
1072
- const installed = await getInstalled2();
1117
+ const installed = await probeInstalled2();
1073
1118
  if (installed.length === 0) {
1074
1119
  p6.log.info(t("update.noneInstalled"));
1075
1120
  return;
@@ -1316,7 +1361,7 @@ var SUBCOMMANDS = /* @__PURE__ */ new Set(["install", "uninstall", "update", "st
1316
1361
  var root = defineCommand({
1317
1362
  meta: {
1318
1363
  name: "@curdx/flow",
1319
- version: "3.3.0",
1364
+ version: "3.3.1",
1320
1365
  description: "Interactive installer for Claude Code plugins and MCP servers"
1321
1366
  },
1322
1367
  args: sharedArgs,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@curdx/flow",
3
- "version": "3.3.0",
3
+ "version": "3.3.1",
4
4
  "description": "Interactive installer for Claude Code plugins and MCP servers",
5
5
  "type": "module",
6
6
  "bin": "./dist/index.mjs",