@curdx/flow 3.2.0 → 3.3.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 +15 -0
- package/README.md +22 -0
- package/dist/index.mjs +311 -99
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,21 @@
|
|
|
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.0 — 2026-04-27
|
|
6
|
+
|
|
7
|
+
### Added
|
|
8
|
+
|
|
9
|
+
- **CLAUDE.md sync** — every successful `install` / `update` / `uninstall` now rewrites a small managed block in `~/.claude/CLAUDE.md` so Claude Code has session-start knowledge of which tools are installed and when to use each. The block lives between `<!-- BEGIN @curdx/flow v1 -->` / `<!-- END @curdx/flow v1 -->` markers; everything outside is preserved verbatim. Uninstalling all managed items removes the block entirely.
|
|
10
|
+
- **`Pkg.whenToUse` and `Pkg.slashNamespace`** — two new optional registry fields. `whenToUse` is the English trigger fragment shown in the CLAUDE.md "Available tools/plugins" list (e.g. "auto-fires on 2+ failures..."). `slashNamespace` is the slash invocation pattern (e.g. `/pua:*`) — only set on plugins that expose one. Both populated for the six bundled items, sourced from each upstream's own documentation.
|
|
11
|
+
- **Conditional Rules section** — the block's `Rules:` lines are emitted only for currently-installed tools, so the block never advises Claude to use a tool that isn't there. The "plan first" rule names whichever planners (`sequential-thinking`, `claude-mem`) are installed.
|
|
12
|
+
- **`--no-claude-md` flag and `CURDX_FLOW_NO_CLAUDE_MD` env var** — opt out of the CLAUDE.md sync (CI, locked-down filesystems, or users who prefer to manage CLAUDE.md by hand).
|
|
13
|
+
|
|
14
|
+
### Notes
|
|
15
|
+
|
|
16
|
+
- Sync is **safe by default**: writes are atomic (tmp + `fs.rename`), partial CLAUDE.md changes are impossible, and a failed sync prints a warning but never aborts a successful install.
|
|
17
|
+
- Forward-compatible: the BEGIN/END regex matches any `v\d+` suffix, so a future `v2` block format will silently replace any pre-existing `v1` block.
|
|
18
|
+
- Block content is always English regardless of `--lang`. CLAUDE.md's audience is Claude itself; English keeps instructions stable and avoids diff churn from alternating language runs.
|
|
19
|
+
|
|
5
20
|
## 3.2.0 — 2026-04-26
|
|
6
21
|
|
|
7
22
|
### Added
|
package/README.md
CHANGED
|
@@ -34,6 +34,28 @@ npx @curdx/flow --lang en # override language
|
|
|
34
34
|
| `sequential-thinking` | mcp | `@modelcontextprotocol/server-sequential-thinking` |
|
|
35
35
|
| `context7` | mcp | HTTP — `https://mcp.context7.com/mcp` (optional API key) |
|
|
36
36
|
|
|
37
|
+
## What it writes to your filesystem
|
|
38
|
+
|
|
39
|
+
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:
|
|
40
|
+
|
|
41
|
+
```
|
|
42
|
+
<!-- BEGIN @curdx/flow v1 -->
|
|
43
|
+
## Tool Usage
|
|
44
|
+
|
|
45
|
+
Available tools/plugins:
|
|
46
|
+
- pua (v3.0.0) — `/pua:*` — auto-fires on 2+ failures or user frustration; ...
|
|
47
|
+
- ...
|
|
48
|
+
|
|
49
|
+
Rules:
|
|
50
|
+
- Do not call every tool by default; ...
|
|
51
|
+
- ...
|
|
52
|
+
|
|
53
|
+
Run `npx @curdx/flow` to install / update / uninstall.
|
|
54
|
+
<!-- END @curdx/flow v1 -->
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
Anything outside the BEGIN/END markers is preserved verbatim — flow only ever rewrites or removes the block itself. Uninstalling all managed items removes the block entirely. Pass `--no-claude-md` (or set `CURDX_FLOW_NO_CLAUDE_MD=1`) to opt out.
|
|
58
|
+
|
|
37
59
|
## Requirements
|
|
38
60
|
|
|
39
61
|
- Node.js >= 20.12
|
package/dist/index.mjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
// src/index.ts
|
|
4
|
-
import * as
|
|
4
|
+
import * as p9 from "@clack/prompts";
|
|
5
5
|
import { defineCommand, runMain } from "citty";
|
|
6
6
|
|
|
7
7
|
// src/ui/language.ts
|
|
@@ -66,7 +66,12 @@ var messages = {
|
|
|
66
66
|
"chrome.prereqNode": "\u9700\u8981 Node.js >= 20.19\uFF0C\u5F53\u524D\u7248\u672C {current}",
|
|
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
|
-
"reinstall.installing": "\u5B89\u88C5\u65B0\u7248\u672C\u2026"
|
|
69
|
+
"reinstall.installing": "\u5B89\u88C5\u65B0\u7248\u672C\u2026",
|
|
70
|
+
"claudeMd.synced": "CLAUDE.md \u5DF2\u66F4\u65B0\uFF08{path}\uFF09",
|
|
71
|
+
"claudeMd.unchanged": "CLAUDE.md \u5DF2\u662F\u6700\u65B0",
|
|
72
|
+
"claudeMd.removed": "\u5DF2\u4ECE CLAUDE.md \u79FB\u9664 @curdx/flow \u533A\u5757",
|
|
73
|
+
"claudeMd.skipped": "\u5DF2\u8DF3\u8FC7 CLAUDE.md \u540C\u6B65\uFF08--no-claude-md\uFF09",
|
|
74
|
+
"claudeMd.failed": "CLAUDE.md \u540C\u6B65\u5931\u8D25\uFF1A{error}"
|
|
70
75
|
};
|
|
71
76
|
var zh_default = messages;
|
|
72
77
|
|
|
@@ -129,7 +134,12 @@ var messages2 = {
|
|
|
129
134
|
"chrome.prereqNode": "Requires Node.js >= 20.19 (current: {current})",
|
|
130
135
|
"chrome.prereqChrome": "Requires Chrome installed locally (chrome-devtools-mcp drives the local browser)",
|
|
131
136
|
"reinstall.uninstalling": "Uninstalling old version\u2026",
|
|
132
|
-
"reinstall.installing": "Installing new version\u2026"
|
|
137
|
+
"reinstall.installing": "Installing new version\u2026",
|
|
138
|
+
"claudeMd.synced": "CLAUDE.md updated ({path})",
|
|
139
|
+
"claudeMd.unchanged": "CLAUDE.md already up to date",
|
|
140
|
+
"claudeMd.removed": "Removed @curdx/flow block from CLAUDE.md",
|
|
141
|
+
"claudeMd.skipped": "Skipped CLAUDE.md sync (--no-claude-md)",
|
|
142
|
+
"claudeMd.failed": "CLAUDE.md sync failed: {error}"
|
|
133
143
|
};
|
|
134
144
|
var en_default = messages2;
|
|
135
145
|
|
|
@@ -178,10 +188,10 @@ async function initLanguage(override) {
|
|
|
178
188
|
}
|
|
179
189
|
|
|
180
190
|
// src/ui/menu.ts
|
|
181
|
-
import * as
|
|
191
|
+
import * as p8 from "@clack/prompts";
|
|
182
192
|
|
|
183
193
|
// src/flows/install.ts
|
|
184
|
-
import * as
|
|
194
|
+
import * as p4 from "@clack/prompts";
|
|
185
195
|
import pc from "picocolors";
|
|
186
196
|
|
|
187
197
|
// src/runner/state.ts
|
|
@@ -200,15 +210,15 @@ async function run(cmd, args) {
|
|
|
200
210
|
stderr: result.stderr
|
|
201
211
|
};
|
|
202
212
|
}
|
|
203
|
-
async function runStreaming(cmd, args,
|
|
204
|
-
|
|
213
|
+
async function runStreaming(cmd, args, log5) {
|
|
214
|
+
log5.message(`$ ${cmd} ${args.join(" ")}`);
|
|
205
215
|
const proc = x(cmd, args, { throwOnError: false });
|
|
206
216
|
let stdout = "";
|
|
207
217
|
for await (const line of proc) {
|
|
208
218
|
const trimmed = line.replace(/\r?\n$/, "");
|
|
209
219
|
if (trimmed.length > 0) {
|
|
210
220
|
stdout += trimmed + "\n";
|
|
211
|
-
|
|
221
|
+
log5.message(trimmed);
|
|
212
222
|
}
|
|
213
223
|
}
|
|
214
224
|
const finished = await proc;
|
|
@@ -251,15 +261,15 @@ async function listPlugins(force = false) {
|
|
|
251
261
|
}
|
|
252
262
|
try {
|
|
253
263
|
const arr = JSON.parse(res.stdout);
|
|
254
|
-
pluginCache = arr.map((
|
|
255
|
-
const [name =
|
|
264
|
+
pluginCache = arr.map((p10) => {
|
|
265
|
+
const [name = p10.id, marketplace = ""] = p10.id.split("@");
|
|
256
266
|
return {
|
|
257
|
-
id:
|
|
267
|
+
id: p10.id,
|
|
258
268
|
name,
|
|
259
269
|
marketplace,
|
|
260
|
-
version:
|
|
261
|
-
scope:
|
|
262
|
-
enabled:
|
|
270
|
+
version: p10.version,
|
|
271
|
+
scope: p10.scope,
|
|
272
|
+
enabled: p10.enabled
|
|
263
273
|
};
|
|
264
274
|
});
|
|
265
275
|
} catch {
|
|
@@ -305,7 +315,7 @@ async function listMcp(force = false) {
|
|
|
305
315
|
}
|
|
306
316
|
async function isPluginInstalled(id) {
|
|
307
317
|
const list = await listPlugins();
|
|
308
|
-
return list.some((
|
|
318
|
+
return list.some((p10) => p10.id === id);
|
|
309
319
|
}
|
|
310
320
|
async function isMarketplaceAdded(name) {
|
|
311
321
|
const list = await listMarketplaces();
|
|
@@ -317,7 +327,7 @@ async function isMcpInstalled(name) {
|
|
|
317
327
|
}
|
|
318
328
|
async function findPlugin(id) {
|
|
319
329
|
const list = await listPlugins();
|
|
320
|
-
return list.find((
|
|
330
|
+
return list.find((p10) => p10.id === id);
|
|
321
331
|
}
|
|
322
332
|
var marketplaceJsonCache = /* @__PURE__ */ new Map();
|
|
323
333
|
function marketplaceDir(name) {
|
|
@@ -339,7 +349,7 @@ async function readMarketplaceJson(name) {
|
|
|
339
349
|
async function getMarketplacePluginVersion(marketplaceName, pluginName) {
|
|
340
350
|
const m = await readMarketplaceJson(marketplaceName);
|
|
341
351
|
if (!m?.plugins) return null;
|
|
342
|
-
const entry = m.plugins.find((
|
|
352
|
+
const entry = m.plugins.find((p10) => p10.name === pluginName);
|
|
343
353
|
return entry?.version ?? null;
|
|
344
354
|
}
|
|
345
355
|
var REFRESH_TTL_MS = 60 * 60 * 1e3;
|
|
@@ -399,11 +409,13 @@ var pua = {
|
|
|
399
409
|
name: "pua",
|
|
400
410
|
description: "tanweai/pua \u2014 Chinese Claude Code skills bundle",
|
|
401
411
|
type: "plugin",
|
|
412
|
+
slashNamespace: "/pua:*",
|
|
413
|
+
whenToUse: "auto-fires on 2+ failures or user frustration; sub-modes p7 / p9 / pro / loop. Skip on first-attempt failures or when a known fix is executing.",
|
|
402
414
|
marketplaces: () => [MARKETPLACE_NAME],
|
|
403
415
|
isInstalled: () => isPluginInstalled(PLUGIN_ID),
|
|
404
416
|
installedVersion: async () => {
|
|
405
|
-
const
|
|
406
|
-
const v =
|
|
417
|
+
const p10 = await findPlugin(PLUGIN_ID);
|
|
418
|
+
const v = p10?.version;
|
|
407
419
|
return v && v !== "unknown" ? v : null;
|
|
408
420
|
},
|
|
409
421
|
latestVersion: () => getMarketplacePluginVersion(MARKETPLACE_NAME, PLUGIN_NAME),
|
|
@@ -426,11 +438,13 @@ var claudeMem = {
|
|
|
426
438
|
name: "claude-mem",
|
|
427
439
|
description: "thedotmack/claude-mem \u2014 persistent cross-session memory for Claude Code",
|
|
428
440
|
type: "plugin",
|
|
441
|
+
slashNamespace: "/claude-mem:*",
|
|
442
|
+
whenToUse: 'for cross-session memory search ("did we solve this before?"), phased planning (`make-plan`), or phased execution (`do`).',
|
|
429
443
|
marketplaces: () => [MARKETPLACE_NAME2],
|
|
430
444
|
isInstalled: () => isPluginInstalled(PLUGIN_ID2),
|
|
431
445
|
installedVersion: async () => {
|
|
432
|
-
const
|
|
433
|
-
const v =
|
|
446
|
+
const p10 = await findPlugin(PLUGIN_ID2);
|
|
447
|
+
const v = p10?.version;
|
|
434
448
|
return v && v !== "unknown" ? v : null;
|
|
435
449
|
},
|
|
436
450
|
latestVersion: () => getMarketplacePluginVersion(MARKETPLACE_NAME2, PLUGIN_NAME2),
|
|
@@ -461,6 +475,7 @@ var chromeDevtoolsMcp = {
|
|
|
461
475
|
name: "chrome-devtools-mcp",
|
|
462
476
|
description: "ChromeDevTools/chrome-devtools-mcp \u2014 drive a real Chrome from Claude Code",
|
|
463
477
|
type: "plugin",
|
|
478
|
+
whenToUse: "when debugging code that runs in a browser: perf traces, network / console inspection, DOM / CSS issues. Prefer snapshot over screenshot.",
|
|
464
479
|
prereqCheck: async (t2) => {
|
|
465
480
|
const major = Number(process.versions.node.split(".")[0] ?? "0");
|
|
466
481
|
const minor = Number(process.versions.node.split(".")[1] ?? "0");
|
|
@@ -489,6 +504,7 @@ var frontendDesign = {
|
|
|
489
504
|
name: "frontend-design",
|
|
490
505
|
description: "Anthropic official \u2014 UI/frontend design helpers",
|
|
491
506
|
type: "plugin",
|
|
507
|
+
whenToUse: "auto-fires when building UI / web components / pages. Best where visual personality matters (landing, marketing, portfolio).",
|
|
492
508
|
isInstalled: () => isPluginInstalled(PLUGIN_ID4),
|
|
493
509
|
install: (ctx) => installPluginById(PLUGIN_ID4, ctx),
|
|
494
510
|
uninstall: (ctx) => uninstallPluginById(PLUGIN_ID4, ctx),
|
|
@@ -503,6 +519,7 @@ var sequentialThinking = {
|
|
|
503
519
|
name: "sequential-thinking",
|
|
504
520
|
description: "modelcontextprotocol/server-sequential-thinking \u2014 structured reasoning helper",
|
|
505
521
|
type: "mcp",
|
|
522
|
+
whenToUse: "for complex multi-step problems where assumptions may shift (architecture comparison, risk-assessed migrations, prod-only debugging). Skip for simple queries.",
|
|
506
523
|
isInstalled: () => isMcpInstalled(MCP_NAME),
|
|
507
524
|
install: async (ctx) => {
|
|
508
525
|
const r = await runStreaming(
|
|
@@ -543,6 +560,7 @@ var context7 = {
|
|
|
543
560
|
name: "context7",
|
|
544
561
|
description: "upstash/context7 \u2014 up-to-date docs from any library (HTTP MCP, optional API key)",
|
|
545
562
|
type: "mcp",
|
|
563
|
+
whenToUse: "for any library / SDK / framework / API / Claude Code docs lookup. Use instead of web search.",
|
|
546
564
|
isInstalled: () => isMcpInstalled(MCP_NAME2),
|
|
547
565
|
configPrompts: async ({ t: t2 }) => {
|
|
548
566
|
p2.note(`${t2("context7.dashboardHint")}
|
|
@@ -595,7 +613,181 @@ var PKGS = [
|
|
|
595
613
|
context7_default
|
|
596
614
|
];
|
|
597
615
|
function findPkg(id) {
|
|
598
|
-
return PKGS.find((
|
|
616
|
+
return PKGS.find((p10) => p10.id === id);
|
|
617
|
+
}
|
|
618
|
+
|
|
619
|
+
// src/runner/claudeMd.ts
|
|
620
|
+
import { promises as fs2 } from "fs";
|
|
621
|
+
import path2 from "path";
|
|
622
|
+
import os2 from "os";
|
|
623
|
+
import * as p3 from "@clack/prompts";
|
|
624
|
+
var BEGIN_MARKER = "<!-- BEGIN @curdx/flow v1 -->";
|
|
625
|
+
var END_MARKER = "<!-- END @curdx/flow v1 -->";
|
|
626
|
+
var BLOCK_RE = /<!-- BEGIN @curdx\/flow v\d+[^>]*-->[\s\S]*?<!-- END @curdx\/flow v\d+ -->/;
|
|
627
|
+
function claudeMdPath() {
|
|
628
|
+
return path2.join(os2.homedir(), ".claude", "CLAUDE.md");
|
|
629
|
+
}
|
|
630
|
+
function renderItemLine(item) {
|
|
631
|
+
let line = `- ${item.name}`;
|
|
632
|
+
if (item.version) line += ` (v${item.version})`;
|
|
633
|
+
if (item.slashNamespace) line += ` \u2014 \`${item.slashNamespace}\``;
|
|
634
|
+
if (item.whenToUse) line += ` \u2014 ${item.whenToUse}`;
|
|
635
|
+
return line;
|
|
636
|
+
}
|
|
637
|
+
var ALWAYS_ON_RULES = [
|
|
638
|
+
"Do not call every tool by default; pick by the trigger condition above.",
|
|
639
|
+
"For first-attempt failures or simple edits, skip extra tools."
|
|
640
|
+
];
|
|
641
|
+
function buildConditionalRules(installedIds) {
|
|
642
|
+
const out = [];
|
|
643
|
+
const planners = [];
|
|
644
|
+
if (installedIds.has("sequential-thinking")) planners.push("sequential-thinking");
|
|
645
|
+
if (installedIds.has("claude-mem")) planners.push("claude-mem `make-plan`");
|
|
646
|
+
if (planners.length > 0) {
|
|
647
|
+
out.push(`For complex / risky changes, plan first (${planners.join(" or ")}).`);
|
|
648
|
+
}
|
|
649
|
+
if (installedIds.has("context7")) {
|
|
650
|
+
out.push("For library / SDK lookups, prefer context7 over web search.");
|
|
651
|
+
}
|
|
652
|
+
if (installedIds.has("chrome-devtools-mcp")) {
|
|
653
|
+
out.push("For browser-rendered behavior, verify in chrome-devtools-mcp instead of guessing.");
|
|
654
|
+
}
|
|
655
|
+
return out;
|
|
656
|
+
}
|
|
657
|
+
function renderBlock(items) {
|
|
658
|
+
const installedIds = new Set(items.map((i) => i.id));
|
|
659
|
+
const rules = [...ALWAYS_ON_RULES, ...buildConditionalRules(installedIds)];
|
|
660
|
+
return [
|
|
661
|
+
BEGIN_MARKER,
|
|
662
|
+
"## Tool Usage",
|
|
663
|
+
"",
|
|
664
|
+
"Available tools/plugins:",
|
|
665
|
+
...items.map(renderItemLine),
|
|
666
|
+
"",
|
|
667
|
+
"Rules:",
|
|
668
|
+
...rules.map((r) => `- ${r}`),
|
|
669
|
+
"",
|
|
670
|
+
"Run `npx @curdx/flow` to install / update / uninstall.",
|
|
671
|
+
END_MARKER
|
|
672
|
+
].join("\n");
|
|
673
|
+
}
|
|
674
|
+
function withEol(s, eol) {
|
|
675
|
+
return eol === "\n" ? s : s.split("\n").join(eol);
|
|
676
|
+
}
|
|
677
|
+
function ensureSingleTrailingNewline(s, eol) {
|
|
678
|
+
if (s.length === 0) return s;
|
|
679
|
+
return s.replace(/[\r\n]+$/, "") + eol;
|
|
680
|
+
}
|
|
681
|
+
function upsertBlock(existing, blockBody, eol) {
|
|
682
|
+
const block = withEol(blockBody, eol);
|
|
683
|
+
if (BLOCK_RE.test(existing)) {
|
|
684
|
+
return existing.replace(BLOCK_RE, block);
|
|
685
|
+
}
|
|
686
|
+
if (existing.length === 0) {
|
|
687
|
+
return block + eol;
|
|
688
|
+
}
|
|
689
|
+
const trimmed = existing.replace(/[\r\n\s]+$/, "");
|
|
690
|
+
return trimmed + eol + eol + block + eol;
|
|
691
|
+
}
|
|
692
|
+
function removeBlock(existing, eol) {
|
|
693
|
+
if (!BLOCK_RE.test(existing)) return existing;
|
|
694
|
+
let next = existing.replace(BLOCK_RE, "");
|
|
695
|
+
const tripleEol = new RegExp(`(?:\\r?\\n){3,}`, "g");
|
|
696
|
+
next = next.replace(tripleEol, eol + eol);
|
|
697
|
+
if (next.replace(/[\s\r\n]/g, "").length === 0) return "";
|
|
698
|
+
return ensureSingleTrailingNewline(next, eol);
|
|
699
|
+
}
|
|
700
|
+
async function pkgToItem(pkg) {
|
|
701
|
+
let version;
|
|
702
|
+
if (pkg.installedVersion) {
|
|
703
|
+
const v = await pkg.installedVersion();
|
|
704
|
+
if (v) version = v;
|
|
705
|
+
}
|
|
706
|
+
return {
|
|
707
|
+
id: pkg.id,
|
|
708
|
+
name: pkg.name,
|
|
709
|
+
type: pkg.type,
|
|
710
|
+
version,
|
|
711
|
+
whenToUse: pkg.whenToUse,
|
|
712
|
+
slashNamespace: pkg.slashNamespace
|
|
713
|
+
};
|
|
714
|
+
}
|
|
715
|
+
async function collectInstalledItems() {
|
|
716
|
+
await Promise.all([listPlugins(true), listMcp(true)]);
|
|
717
|
+
const items = [];
|
|
718
|
+
for (const pkg of PKGS) {
|
|
719
|
+
if (await pkg.isInstalled()) {
|
|
720
|
+
items.push(await pkgToItem(pkg));
|
|
721
|
+
}
|
|
722
|
+
}
|
|
723
|
+
items.sort((a, b) => {
|
|
724
|
+
if (a.type !== b.type) return a.type === "plugin" ? -1 : 1;
|
|
725
|
+
return a.name.localeCompare(b.name);
|
|
726
|
+
});
|
|
727
|
+
return items;
|
|
728
|
+
}
|
|
729
|
+
async function syncClaudeMd(opts) {
|
|
730
|
+
const file = claudeMdPath();
|
|
731
|
+
if (opts?.skip) return { status: "skipped", path: file };
|
|
732
|
+
try {
|
|
733
|
+
const items = await collectInstalledItems();
|
|
734
|
+
let existing = "";
|
|
735
|
+
let existed = true;
|
|
736
|
+
try {
|
|
737
|
+
existing = await fs2.readFile(file, "utf8");
|
|
738
|
+
} catch (err) {
|
|
739
|
+
if (err.code === "ENOENT") {
|
|
740
|
+
existed = false;
|
|
741
|
+
} else {
|
|
742
|
+
throw err;
|
|
743
|
+
}
|
|
744
|
+
}
|
|
745
|
+
const eol = existing.includes("\r\n") ? "\r\n" : "\n";
|
|
746
|
+
const hadBlock = BLOCK_RE.test(existing);
|
|
747
|
+
let next;
|
|
748
|
+
if (items.length === 0) {
|
|
749
|
+
if (!hadBlock) {
|
|
750
|
+
return { status: "unchanged", path: file };
|
|
751
|
+
}
|
|
752
|
+
next = removeBlock(existing, eol);
|
|
753
|
+
} else {
|
|
754
|
+
next = upsertBlock(existing, renderBlock(items), eol);
|
|
755
|
+
}
|
|
756
|
+
if (next === existing) {
|
|
757
|
+
return { status: "unchanged", path: file };
|
|
758
|
+
}
|
|
759
|
+
await fs2.mkdir(path2.dirname(file), { recursive: true });
|
|
760
|
+
const tmp = `${file}.tmp.${process.pid}`;
|
|
761
|
+
await fs2.writeFile(tmp, next, "utf8");
|
|
762
|
+
await fs2.rename(tmp, file);
|
|
763
|
+
if (!existed) return { status: "created", path: file };
|
|
764
|
+
if (hadBlock && items.length === 0) return { status: "removed", path: file };
|
|
765
|
+
return { status: "updated", path: file };
|
|
766
|
+
} catch (err) {
|
|
767
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
768
|
+
return { status: "failed", path: file, error: msg };
|
|
769
|
+
}
|
|
770
|
+
}
|
|
771
|
+
async function syncFromState(opts) {
|
|
772
|
+
const r = await syncClaudeMd(opts);
|
|
773
|
+
switch (r.status) {
|
|
774
|
+
case "skipped":
|
|
775
|
+
p3.log.info(t("claudeMd.skipped"));
|
|
776
|
+
return;
|
|
777
|
+
case "unchanged":
|
|
778
|
+
p3.log.info(t("claudeMd.unchanged"));
|
|
779
|
+
return;
|
|
780
|
+
case "created":
|
|
781
|
+
case "updated":
|
|
782
|
+
p3.log.info(t("claudeMd.synced", { path: r.path }));
|
|
783
|
+
return;
|
|
784
|
+
case "removed":
|
|
785
|
+
p3.log.info(t("claudeMd.removed"));
|
|
786
|
+
return;
|
|
787
|
+
case "failed":
|
|
788
|
+
p3.log.warn(t("claudeMd.failed", { error: r.error ?? "unknown" }));
|
|
789
|
+
return;
|
|
790
|
+
}
|
|
599
791
|
}
|
|
600
792
|
|
|
601
793
|
// src/flows/install.ts
|
|
@@ -634,13 +826,13 @@ async function selectInteractive(states) {
|
|
|
634
826
|
const s = states.get(pkg.id);
|
|
635
827
|
return s.kind === "not_installed" || s.kind === "update_available";
|
|
636
828
|
}).map((pkg) => pkg.id);
|
|
637
|
-
const picked = await
|
|
829
|
+
const picked = await p4.multiselect({
|
|
638
830
|
message: t("install.selectPrompt"),
|
|
639
831
|
options,
|
|
640
832
|
initialValues,
|
|
641
833
|
required: false
|
|
642
834
|
});
|
|
643
|
-
if (
|
|
835
|
+
if (p4.isCancel(picked)) return null;
|
|
644
836
|
return picked.map((id) => findPkg(id)).filter((x2) => Boolean(x2));
|
|
645
837
|
}
|
|
646
838
|
function selectFromIds(opts) {
|
|
@@ -650,7 +842,7 @@ function selectFromIds(opts) {
|
|
|
650
842
|
for (const id of opts.ids) {
|
|
651
843
|
const pkg = findPkg(id);
|
|
652
844
|
if (pkg) found.push(pkg);
|
|
653
|
-
else
|
|
845
|
+
else p4.log.warn(`Unknown id: ${id}`);
|
|
654
846
|
}
|
|
655
847
|
return found;
|
|
656
848
|
}
|
|
@@ -662,11 +854,11 @@ async function runOne(pkg, state, opts) {
|
|
|
662
854
|
mode = "update";
|
|
663
855
|
} else {
|
|
664
856
|
if (!opts.yes) {
|
|
665
|
-
const ans = await
|
|
857
|
+
const ans = await p4.confirm({
|
|
666
858
|
message: t("install.confirmReinstall", { name: pkg.name }),
|
|
667
859
|
initialValue: false
|
|
668
860
|
});
|
|
669
|
-
if (
|
|
861
|
+
if (p4.isCancel(ans) || ans === false) {
|
|
670
862
|
return { id: pkg.id, status: "skip", message: t("install.skippedReinstall", { name: pkg.name }) };
|
|
671
863
|
}
|
|
672
864
|
}
|
|
@@ -675,7 +867,7 @@ async function runOne(pkg, state, opts) {
|
|
|
675
867
|
if (pkg.prereqCheck) {
|
|
676
868
|
const r = await pkg.prereqCheck(t);
|
|
677
869
|
if (!r.ok) {
|
|
678
|
-
|
|
870
|
+
p4.log.warn(t("install.prereqFail", { name: pkg.name, reason: r.reason }));
|
|
679
871
|
return { id: pkg.id, status: "skip", message: r.reason };
|
|
680
872
|
}
|
|
681
873
|
}
|
|
@@ -690,30 +882,30 @@ async function runOne(pkg, state, opts) {
|
|
|
690
882
|
if (mode === "update" && state.kind === "update_available") {
|
|
691
883
|
titleVars["version"] = state.latest;
|
|
692
884
|
}
|
|
693
|
-
const
|
|
885
|
+
const log5 = p4.taskLog({ title: t(titleKey, titleVars) });
|
|
694
886
|
try {
|
|
695
887
|
if (mode === "reinstall") {
|
|
696
|
-
|
|
697
|
-
await pkg.uninstall({ log:
|
|
698
|
-
|
|
699
|
-
await pkg.install({ log:
|
|
888
|
+
log5.message(t("reinstall.uninstalling"));
|
|
889
|
+
await pkg.uninstall({ log: log5, config, t });
|
|
890
|
+
log5.message(t("reinstall.installing"));
|
|
891
|
+
await pkg.install({ log: log5, config, t });
|
|
700
892
|
} else if (mode === "update") {
|
|
701
893
|
if (pkg.update) {
|
|
702
|
-
await pkg.update({ log:
|
|
894
|
+
await pkg.update({ log: log5, config, t });
|
|
703
895
|
} else {
|
|
704
|
-
|
|
705
|
-
await pkg.uninstall({ log:
|
|
706
|
-
|
|
707
|
-
await pkg.install({ log:
|
|
896
|
+
log5.message(t("reinstall.uninstalling"));
|
|
897
|
+
await pkg.uninstall({ log: log5, config, t });
|
|
898
|
+
log5.message(t("reinstall.installing"));
|
|
899
|
+
await pkg.install({ log: log5, config, t });
|
|
708
900
|
}
|
|
709
901
|
} else {
|
|
710
|
-
await pkg.install({ log:
|
|
902
|
+
await pkg.install({ log: log5, config, t });
|
|
711
903
|
}
|
|
712
|
-
|
|
904
|
+
log5.success(t("install.success", { name: pkg.name }));
|
|
713
905
|
return { id: pkg.id, status: "ok" };
|
|
714
906
|
} catch (err) {
|
|
715
907
|
const msg = err instanceof Error ? err.message : String(err);
|
|
716
|
-
|
|
908
|
+
log5.error(`${t("install.failed", { name: pkg.name })}
|
|
717
909
|
${msg}`);
|
|
718
910
|
return { id: pkg.id, status: "fail", message: msg };
|
|
719
911
|
}
|
|
@@ -731,7 +923,7 @@ function summarize(results) {
|
|
|
731
923
|
...skip.map((r) => ` ${pc.yellow("-")} ${r.id}${r.message ? pc.dim(` (${r.message})`) : ""}`),
|
|
732
924
|
...fail.map((r) => ` ${pc.red("\u2717")} ${r.id}${r.message ? pc.dim(` (${r.message.split("\n")[0]})`) : ""}`)
|
|
733
925
|
];
|
|
734
|
-
|
|
926
|
+
p4.note(lines.join("\n"), t("install.summaryTitle"));
|
|
735
927
|
}
|
|
736
928
|
async function maybeRefreshMarketplaces(opts) {
|
|
737
929
|
if (opts.noRefresh) return;
|
|
@@ -740,7 +932,7 @@ async function maybeRefreshMarketplaces(opts) {
|
|
|
740
932
|
if (pkg.marketplaces) for (const n of pkg.marketplaces()) names.add(n);
|
|
741
933
|
}
|
|
742
934
|
if (names.size === 0) return;
|
|
743
|
-
const sp =
|
|
935
|
+
const sp = p4.spinner();
|
|
744
936
|
sp.start(t("marketplace.refreshing"));
|
|
745
937
|
const refreshed = await refreshMarketplaces([...names]);
|
|
746
938
|
sp.stop(
|
|
@@ -752,7 +944,7 @@ async function installFlow(opts = {}) {
|
|
|
752
944
|
const explicit = opts.all || opts.ids && opts.ids.length > 0;
|
|
753
945
|
const candidates = explicit ? selectFromIds(opts) : [...PKGS];
|
|
754
946
|
if (candidates.length === 0) {
|
|
755
|
-
|
|
947
|
+
p4.log.info(t("install.nothingSelected"));
|
|
756
948
|
return;
|
|
757
949
|
}
|
|
758
950
|
const stateMap = /* @__PURE__ */ new Map();
|
|
@@ -767,13 +959,13 @@ async function installFlow(opts = {}) {
|
|
|
767
959
|
} else {
|
|
768
960
|
const picked = await selectInteractive(stateMap);
|
|
769
961
|
if (picked === null) {
|
|
770
|
-
|
|
962
|
+
p4.cancel(t("app.cancelled"));
|
|
771
963
|
return;
|
|
772
964
|
}
|
|
773
965
|
targets = picked;
|
|
774
966
|
}
|
|
775
967
|
if (targets.length === 0) {
|
|
776
|
-
|
|
968
|
+
p4.log.info(t("install.nothingSelected"));
|
|
777
969
|
return;
|
|
778
970
|
}
|
|
779
971
|
const results = [];
|
|
@@ -782,10 +974,11 @@ async function installFlow(opts = {}) {
|
|
|
782
974
|
results.push(await runOne(pkg, state, opts));
|
|
783
975
|
}
|
|
784
976
|
summarize(results);
|
|
977
|
+
await syncFromState({ skip: opts.noClaudeMd });
|
|
785
978
|
}
|
|
786
979
|
|
|
787
980
|
// src/flows/uninstall.ts
|
|
788
|
-
import * as
|
|
981
|
+
import * as p5 from "@clack/prompts";
|
|
789
982
|
import pc2 from "picocolors";
|
|
790
983
|
async function getInstalled() {
|
|
791
984
|
const states = await Promise.all(PKGS.map(async (pkg) => ({ pkg, installed: await pkg.isInstalled() })));
|
|
@@ -799,21 +992,21 @@ async function uninstallFlow(opts = {}) {
|
|
|
799
992
|
for (const id of opts.ids) {
|
|
800
993
|
const pkg = findPkg(id);
|
|
801
994
|
if (!pkg) {
|
|
802
|
-
|
|
995
|
+
p5.log.warn(`Unknown id: ${id}`);
|
|
803
996
|
continue;
|
|
804
997
|
}
|
|
805
998
|
if (!installed.some((x2) => x2.id === pkg.id)) {
|
|
806
|
-
|
|
999
|
+
p5.log.warn(`${pkg.name}: ${t("pkg.notInstalled")}`);
|
|
807
1000
|
continue;
|
|
808
1001
|
}
|
|
809
1002
|
targets.push(pkg);
|
|
810
1003
|
}
|
|
811
1004
|
} else {
|
|
812
1005
|
if (installed.length === 0) {
|
|
813
|
-
|
|
1006
|
+
p5.log.info(t("uninstall.noneInstalled"));
|
|
814
1007
|
return;
|
|
815
1008
|
}
|
|
816
|
-
const picked = await
|
|
1009
|
+
const picked = await p5.multiselect({
|
|
817
1010
|
message: t("uninstall.selectPrompt"),
|
|
818
1011
|
options: installed.map((pkg) => ({
|
|
819
1012
|
value: pkg.id,
|
|
@@ -822,53 +1015,54 @@ async function uninstallFlow(opts = {}) {
|
|
|
822
1015
|
})),
|
|
823
1016
|
required: false
|
|
824
1017
|
});
|
|
825
|
-
if (
|
|
826
|
-
|
|
1018
|
+
if (p5.isCancel(picked)) {
|
|
1019
|
+
p5.cancel(t("app.cancelled"));
|
|
827
1020
|
return;
|
|
828
1021
|
}
|
|
829
1022
|
targets = picked.map((id) => findPkg(id)).filter((x2) => Boolean(x2));
|
|
830
1023
|
}
|
|
831
1024
|
if (targets.length === 0) {
|
|
832
|
-
|
|
1025
|
+
p5.log.info(t("install.nothingSelected"));
|
|
833
1026
|
return;
|
|
834
1027
|
}
|
|
835
1028
|
if (!opts.yes) {
|
|
836
|
-
const ok2 = await
|
|
1029
|
+
const ok2 = await p5.confirm({
|
|
837
1030
|
message: t("uninstall.confirm", { count: targets.length }),
|
|
838
1031
|
initialValue: false
|
|
839
1032
|
});
|
|
840
|
-
if (
|
|
841
|
-
|
|
1033
|
+
if (p5.isCancel(ok2) || ok2 === false) {
|
|
1034
|
+
p5.cancel(t("app.cancelled"));
|
|
842
1035
|
return;
|
|
843
1036
|
}
|
|
844
1037
|
}
|
|
845
1038
|
const results = [];
|
|
846
1039
|
for (const pkg of targets) {
|
|
847
|
-
const
|
|
1040
|
+
const log5 = p5.taskLog({ title: t("uninstall.starting", { name: pkg.name }) });
|
|
848
1041
|
try {
|
|
849
|
-
await pkg.uninstall({ log:
|
|
850
|
-
|
|
1042
|
+
await pkg.uninstall({ log: log5, config: {}, t });
|
|
1043
|
+
log5.success(t("uninstall.success", { name: pkg.name }));
|
|
851
1044
|
results.push({ id: pkg.id, status: "ok" });
|
|
852
1045
|
} catch (err) {
|
|
853
1046
|
const msg = err instanceof Error ? err.message : String(err);
|
|
854
|
-
|
|
1047
|
+
log5.error(`${t("uninstall.failed", { name: pkg.name })}
|
|
855
1048
|
${msg}`);
|
|
856
1049
|
results.push({ id: pkg.id, status: "fail", message: msg });
|
|
857
1050
|
}
|
|
858
1051
|
}
|
|
859
1052
|
const ok = results.filter((r) => r.status === "ok").length;
|
|
860
1053
|
const fail = results.filter((r) => r.status === "fail").length;
|
|
861
|
-
|
|
1054
|
+
p5.note(
|
|
862
1055
|
[
|
|
863
1056
|
pc2.green(t("install.summaryOk", { count: ok })),
|
|
864
1057
|
pc2.red(t("install.summaryFail", { count: fail }))
|
|
865
1058
|
].join("\n"),
|
|
866
1059
|
t("install.summaryTitle")
|
|
867
1060
|
);
|
|
1061
|
+
await syncFromState({ skip: opts.noClaudeMd });
|
|
868
1062
|
}
|
|
869
1063
|
|
|
870
1064
|
// src/flows/update.ts
|
|
871
|
-
import * as
|
|
1065
|
+
import * as p6 from "@clack/prompts";
|
|
872
1066
|
import pc3 from "picocolors";
|
|
873
1067
|
async function getInstalled2() {
|
|
874
1068
|
const states = await Promise.all(PKGS.map(async (pkg) => ({ pkg, installed: await pkg.isInstalled() })));
|
|
@@ -877,7 +1071,7 @@ async function getInstalled2() {
|
|
|
877
1071
|
async function updateFlow(opts = {}) {
|
|
878
1072
|
const installed = await getInstalled2();
|
|
879
1073
|
if (installed.length === 0) {
|
|
880
|
-
|
|
1074
|
+
p6.log.info(t("update.noneInstalled"));
|
|
881
1075
|
return;
|
|
882
1076
|
}
|
|
883
1077
|
let targets;
|
|
@@ -888,17 +1082,17 @@ async function updateFlow(opts = {}) {
|
|
|
888
1082
|
for (const id of opts.ids) {
|
|
889
1083
|
const pkg = findPkg(id);
|
|
890
1084
|
if (!pkg) {
|
|
891
|
-
|
|
1085
|
+
p6.log.warn(`Unknown id: ${id}`);
|
|
892
1086
|
continue;
|
|
893
1087
|
}
|
|
894
1088
|
if (!installed.some((x2) => x2.id === pkg.id)) {
|
|
895
|
-
|
|
1089
|
+
p6.log.warn(`${pkg.name}: ${t("pkg.notInstalled")}`);
|
|
896
1090
|
continue;
|
|
897
1091
|
}
|
|
898
1092
|
targets.push(pkg);
|
|
899
1093
|
}
|
|
900
1094
|
} else {
|
|
901
|
-
const picked = await
|
|
1095
|
+
const picked = await p6.multiselect({
|
|
902
1096
|
message: t("update.selectPrompt"),
|
|
903
1097
|
options: installed.map((pkg) => ({
|
|
904
1098
|
value: pkg.id,
|
|
@@ -907,41 +1101,41 @@ async function updateFlow(opts = {}) {
|
|
|
907
1101
|
})),
|
|
908
1102
|
required: false
|
|
909
1103
|
});
|
|
910
|
-
if (
|
|
911
|
-
|
|
1104
|
+
if (p6.isCancel(picked)) {
|
|
1105
|
+
p6.cancel(t("app.cancelled"));
|
|
912
1106
|
return;
|
|
913
1107
|
}
|
|
914
1108
|
targets = picked.map((id) => findPkg(id)).filter((x2) => Boolean(x2));
|
|
915
1109
|
}
|
|
916
1110
|
if (targets.length === 0) {
|
|
917
|
-
|
|
1111
|
+
p6.log.info(t("install.nothingSelected"));
|
|
918
1112
|
return;
|
|
919
1113
|
}
|
|
920
1114
|
const results = [];
|
|
921
1115
|
for (const pkg of targets) {
|
|
922
1116
|
if (pkg.id === "sequential-thinking") {
|
|
923
|
-
|
|
1117
|
+
p6.log.info(t("update.mcpAutoNote", { name: pkg.name }));
|
|
924
1118
|
results.push({ id: pkg.id, status: "noop" });
|
|
925
1119
|
continue;
|
|
926
1120
|
}
|
|
927
1121
|
if (pkg.id === "context7") {
|
|
928
|
-
|
|
1122
|
+
p6.log.info(t("update.context7Note"));
|
|
929
1123
|
results.push({ id: pkg.id, status: "noop" });
|
|
930
1124
|
continue;
|
|
931
1125
|
}
|
|
932
|
-
const
|
|
1126
|
+
const log5 = p6.taskLog({ title: t("update.starting", { name: pkg.name }) });
|
|
933
1127
|
try {
|
|
934
1128
|
if (pkg.update) {
|
|
935
|
-
await pkg.update({ log:
|
|
1129
|
+
await pkg.update({ log: log5, config: {}, t });
|
|
936
1130
|
} else {
|
|
937
|
-
await pkg.uninstall({ log:
|
|
938
|
-
await pkg.install({ log:
|
|
1131
|
+
await pkg.uninstall({ log: log5, config: {}, t });
|
|
1132
|
+
await pkg.install({ log: log5, config: {}, t });
|
|
939
1133
|
}
|
|
940
|
-
|
|
1134
|
+
log5.success(t("update.success", { name: pkg.name }));
|
|
941
1135
|
results.push({ id: pkg.id, status: "ok" });
|
|
942
1136
|
} catch (err) {
|
|
943
1137
|
const msg = err instanceof Error ? err.message : String(err);
|
|
944
|
-
|
|
1138
|
+
log5.error(`${t("update.failed", { name: pkg.name })}
|
|
945
1139
|
${msg}`);
|
|
946
1140
|
results.push({ id: pkg.id, status: "fail", message: msg });
|
|
947
1141
|
}
|
|
@@ -949,7 +1143,7 @@ ${msg}`);
|
|
|
949
1143
|
const ok = results.filter((r) => r.status === "ok").length;
|
|
950
1144
|
const fail = results.filter((r) => r.status === "fail").length;
|
|
951
1145
|
const noop = results.filter((r) => r.status === "noop").length;
|
|
952
|
-
|
|
1146
|
+
p6.note(
|
|
953
1147
|
[
|
|
954
1148
|
pc3.green(t("install.summaryOk", { count: ok })),
|
|
955
1149
|
pc3.red(t("install.summaryFail", { count: fail })),
|
|
@@ -957,10 +1151,11 @@ ${msg}`);
|
|
|
957
1151
|
].join("\n"),
|
|
958
1152
|
t("install.summaryTitle")
|
|
959
1153
|
);
|
|
1154
|
+
await syncFromState({ skip: opts.noClaudeMd });
|
|
960
1155
|
}
|
|
961
1156
|
|
|
962
1157
|
// src/flows/status.ts
|
|
963
|
-
import * as
|
|
1158
|
+
import * as p7 from "@clack/prompts";
|
|
964
1159
|
import pc4 from "picocolors";
|
|
965
1160
|
async function statusFlow(opts = {}) {
|
|
966
1161
|
const states = await Promise.all(
|
|
@@ -993,12 +1188,12 @@ async function statusFlow(opts = {}) {
|
|
|
993
1188
|
const rows = states.map(
|
|
994
1189
|
(s) => `${s.name.padEnd(nameW)} ${s.type.padEnd(typeW)} ${s.installed ? pc4.green(`\u2713 ${t("pkg.installed")}`) : pc4.yellow(`\u2717 ${t("pkg.notInstalled")}`)}`
|
|
995
1190
|
);
|
|
996
|
-
|
|
1191
|
+
p7.note([header, sep, ...rows].join("\n"), t("status.title"));
|
|
997
1192
|
}
|
|
998
1193
|
|
|
999
1194
|
// src/ui/menu.ts
|
|
1000
1195
|
async function mainMenu() {
|
|
1001
|
-
const action = await
|
|
1196
|
+
const action = await p8.select({
|
|
1002
1197
|
message: t("menu.title"),
|
|
1003
1198
|
options: [
|
|
1004
1199
|
{ value: "install", label: t("menu.install") },
|
|
@@ -1008,8 +1203,8 @@ async function mainMenu() {
|
|
|
1008
1203
|
{ value: "exit", label: t("menu.exit") }
|
|
1009
1204
|
]
|
|
1010
1205
|
});
|
|
1011
|
-
if (
|
|
1012
|
-
|
|
1206
|
+
if (p8.isCancel(action) || action === "exit") {
|
|
1207
|
+
p8.cancel(t("app.cancelled"));
|
|
1013
1208
|
return;
|
|
1014
1209
|
}
|
|
1015
1210
|
switch (action) {
|
|
@@ -1032,8 +1227,16 @@ async function mainMenu() {
|
|
|
1032
1227
|
function parseLang(v) {
|
|
1033
1228
|
return v === "zh" || v === "en" ? v : void 0;
|
|
1034
1229
|
}
|
|
1230
|
+
function noClaudeMdFromArgs(args) {
|
|
1231
|
+
if (args["no-claude-md"]) return true;
|
|
1232
|
+
return Boolean(process.env["CURDX_FLOW_NO_CLAUDE_MD"]);
|
|
1233
|
+
}
|
|
1035
1234
|
var sharedArgs = {
|
|
1036
|
-
lang: { type: "string", description: "Override language: zh or en" }
|
|
1235
|
+
lang: { type: "string", description: "Override language: zh or en" },
|
|
1236
|
+
"no-claude-md": {
|
|
1237
|
+
type: "boolean",
|
|
1238
|
+
description: "Skip syncing the @curdx/flow block in ~/.claude/CLAUDE.md"
|
|
1239
|
+
}
|
|
1037
1240
|
};
|
|
1038
1241
|
var installCmd = defineCommand({
|
|
1039
1242
|
meta: { name: "install", description: "Install, reinstall, or update plugins / MCP servers" },
|
|
@@ -1046,15 +1249,16 @@ var installCmd = defineCommand({
|
|
|
1046
1249
|
},
|
|
1047
1250
|
async run({ args }) {
|
|
1048
1251
|
await initLanguage(parseLang(args.lang));
|
|
1049
|
-
|
|
1252
|
+
p9.intro(t("app.intro"));
|
|
1050
1253
|
const ids = collectPositional(args);
|
|
1051
1254
|
await installFlow({
|
|
1052
1255
|
ids,
|
|
1053
1256
|
all: Boolean(args.all),
|
|
1054
1257
|
yes: Boolean(args.yes),
|
|
1055
|
-
noRefresh: Boolean(args["no-refresh"])
|
|
1258
|
+
noRefresh: Boolean(args["no-refresh"]),
|
|
1259
|
+
noClaudeMd: noClaudeMdFromArgs(args)
|
|
1056
1260
|
});
|
|
1057
|
-
|
|
1261
|
+
p9.outro(t("app.outro"));
|
|
1058
1262
|
}
|
|
1059
1263
|
});
|
|
1060
1264
|
var uninstallCmd = defineCommand({
|
|
@@ -1066,10 +1270,14 @@ var uninstallCmd = defineCommand({
|
|
|
1066
1270
|
},
|
|
1067
1271
|
async run({ args }) {
|
|
1068
1272
|
await initLanguage(parseLang(args.lang));
|
|
1069
|
-
|
|
1273
|
+
p9.intro(t("app.intro"));
|
|
1070
1274
|
const ids = collectPositional(args);
|
|
1071
|
-
await uninstallFlow({
|
|
1072
|
-
|
|
1275
|
+
await uninstallFlow({
|
|
1276
|
+
ids,
|
|
1277
|
+
yes: Boolean(args.yes),
|
|
1278
|
+
noClaudeMd: noClaudeMdFromArgs(args)
|
|
1279
|
+
});
|
|
1280
|
+
p9.outro(t("app.outro"));
|
|
1073
1281
|
}
|
|
1074
1282
|
});
|
|
1075
1283
|
var updateCmd = defineCommand({
|
|
@@ -1081,10 +1289,14 @@ var updateCmd = defineCommand({
|
|
|
1081
1289
|
},
|
|
1082
1290
|
async run({ args }) {
|
|
1083
1291
|
await initLanguage(parseLang(args.lang));
|
|
1084
|
-
|
|
1292
|
+
p9.intro(t("app.intro"));
|
|
1085
1293
|
const ids = collectPositional(args);
|
|
1086
|
-
await updateFlow({
|
|
1087
|
-
|
|
1294
|
+
await updateFlow({
|
|
1295
|
+
ids,
|
|
1296
|
+
all: Boolean(args.all),
|
|
1297
|
+
noClaudeMd: noClaudeMdFromArgs(args)
|
|
1298
|
+
});
|
|
1299
|
+
p9.outro(t("app.outro"));
|
|
1088
1300
|
}
|
|
1089
1301
|
});
|
|
1090
1302
|
var statusCmd = defineCommand({
|
|
@@ -1095,16 +1307,16 @@ var statusCmd = defineCommand({
|
|
|
1095
1307
|
},
|
|
1096
1308
|
async run({ args }) {
|
|
1097
1309
|
await initLanguage(parseLang(args.lang));
|
|
1098
|
-
if (!args.json)
|
|
1310
|
+
if (!args.json) p9.intro(t("app.intro"));
|
|
1099
1311
|
await statusFlow({ json: Boolean(args.json) });
|
|
1100
|
-
if (!args.json)
|
|
1312
|
+
if (!args.json) p9.outro(t("app.outro"));
|
|
1101
1313
|
}
|
|
1102
1314
|
});
|
|
1103
1315
|
var SUBCOMMANDS = /* @__PURE__ */ new Set(["install", "uninstall", "update", "status"]);
|
|
1104
1316
|
var root = defineCommand({
|
|
1105
1317
|
meta: {
|
|
1106
1318
|
name: "@curdx/flow",
|
|
1107
|
-
version: "3.
|
|
1319
|
+
version: "3.3.0",
|
|
1108
1320
|
description: "Interactive installer for Claude Code plugins and MCP servers"
|
|
1109
1321
|
},
|
|
1110
1322
|
args: sharedArgs,
|
|
@@ -1139,9 +1351,9 @@ async function runInteractive(argv2) {
|
|
|
1139
1351
|
else if (argv2[i]?.startsWith("--lang=")) lang = parseLang(argv2[i].slice("--lang=".length));
|
|
1140
1352
|
}
|
|
1141
1353
|
await initLanguage(lang);
|
|
1142
|
-
|
|
1354
|
+
p9.intro(t("app.intro"));
|
|
1143
1355
|
await mainMenu();
|
|
1144
|
-
|
|
1356
|
+
p9.outro(t("app.outro"));
|
|
1145
1357
|
}
|
|
1146
1358
|
var argv = process.argv.slice(2);
|
|
1147
1359
|
var first = firstNonFlag(argv);
|