@curdx/flow 2.0.0-beta.16 → 2.0.0-beta.18
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.md +4 -3
- package/README.zh.md +16 -18
- package/cli/README.md +8 -6
- package/cli/doctor.js +39 -10
- package/cli/install.js +67 -20
- package/cli/registry.js +47 -32
- package/cli/uninstall.js +66 -5
- package/cli/upgrade.js +1 -1
- package/cli/utils.js +23 -3
- package/package.json +1 -1
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
},
|
|
7
7
|
"metadata": {
|
|
8
8
|
"description": "Claude Code Discipline Layer — spec-driven workflow + goal-backward verification + Karpathy 4 principles enforced via gates. Stops Claude from faking \"done\" on non-trivial features.",
|
|
9
|
-
"version": "2.0.0-beta.
|
|
9
|
+
"version": "2.0.0-beta.18"
|
|
10
10
|
},
|
|
11
11
|
"plugins": [
|
|
12
12
|
{
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "curdx-flow",
|
|
3
|
-
"version": "2.0.0-beta.
|
|
3
|
+
"version": "2.0.0-beta.18",
|
|
4
4
|
"description": "Claude Code Discipline Layer — spec-driven workflow + goal-backward verification + Karpathy 4 principles enforced via gates. Stops Claude from faking \"done\" on non-trivial features.",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "wdx",
|
package/README.md
CHANGED
|
@@ -28,7 +28,7 @@ Not a framework. Not a methodology library. A discipline layer.
|
|
|
28
28
|
npx @curdx/flow install --all
|
|
29
29
|
```
|
|
30
30
|
|
|
31
|
-
This installs the Claude Code plugin (fully offline from the npm package — no GitHub fetch), the
|
|
31
|
+
This installs the Claude Code plugin (fully offline from the npm package — no GitHub fetch), the official Context7 plugin, the required sequential-thinking MCP server, and four recommended companion plugins (pua, claude-mem, frontend-design, chrome-devtools-mcp).
|
|
32
32
|
|
|
33
33
|
## 9 slash commands, that's it
|
|
34
34
|
|
|
@@ -97,7 +97,8 @@ npm i -g @curdx/flow@^1.1
|
|
|
97
97
|
- **8 composable gates** — Karpathy / Verification / TDD / Coverage / Adversarial / Edge-Case / Security / DevEx
|
|
98
98
|
- **4 execution strategies** for `/curdx-flow:implement` (linear / subagent / stop-hook / wave, auto-routed)
|
|
99
99
|
- **5 hook events** that enforce the discipline without user action
|
|
100
|
-
- **
|
|
100
|
+
- **Required docs/reasoning tools** — Context7 official plugin (MCP + skill + docs agent) and sequential-thinking MCP
|
|
101
|
+
- **4 recommended companion plugins** — pua, claude-mem, frontend-design, chrome-devtools-mcp
|
|
101
102
|
- **Offline-capable install** — the npm package ships the full plugin body; zero GitHub round-trips during install
|
|
102
103
|
|
|
103
104
|
## Documentation
|
|
@@ -123,7 +124,7 @@ CurDX-Flow v2 is a distillation, not an invention. Ideas we depend on:
|
|
|
123
124
|
- [**superpowers**](https://github.com/obra/superpowers) — subagent discipline + TDD + two-stage review
|
|
124
125
|
- [**pua**](https://github.com/tanweai/pua) — the three red lines
|
|
125
126
|
- [**claude-mem**](https://github.com/thedotmack/claude-mem) — cross-session memory
|
|
126
|
-
- **
|
|
127
|
+
- **MCP / plugin ecosystem** — context7 (Upstash), sequential-thinking, chrome-devtools-mcp (Chrome DevTools team), frontend-design skill
|
|
127
128
|
|
|
128
129
|
## License
|
|
129
130
|
|
package/README.zh.md
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
> **Claude Code 的 AI 工程工作流元框架**
|
|
4
4
|
> 把 Claude Code 变成有工程纪律的 AI 团队 — 编排 MCP 和插件,强制 Karpathy 4 原则,用规格驱动工作流交付高质量软件。
|
|
5
5
|
|
|
6
|
-
[](https://www.npmjs.com/package/@curdx/flow)
|
|
7
7
|
[](./LICENSE)
|
|
8
8
|
[](https://code.claude.com)
|
|
9
9
|
[](./README.md)
|
|
@@ -16,15 +16,15 @@ CurDX-Flow 是一个 Claude Code 插件,把 6 个验证过的 AI 工程工作
|
|
|
16
16
|
|
|
17
17
|
**不重造轮子。编排好轮子。**
|
|
18
18
|
|
|
19
|
-
## 一览(
|
|
19
|
+
## 一览(v2)
|
|
20
20
|
|
|
21
|
-
- **
|
|
22
|
-
- **
|
|
21
|
+
- **9 个命令** — 初始化 / 启动规格 / 规格 / 执行 / 验证 / 审查 / 调试 / fast / 帮助
|
|
22
|
+
- **15 个内部代理** — 由命令调度,不再暴露 v1 的人格角色
|
|
23
23
|
- **8 个可组合 Gate** — Karpathy / Verification / TDD / Coverage / Adversarial / Edge-Case / Security / DevEx
|
|
24
24
|
- **4 种执行策略** — linear / subagent / stop-hook / wave(自动路由)
|
|
25
25
|
- **10 个知识文档** — 规格驱动 / POC-First / 原子提交 / 执行策略 / ...
|
|
26
|
-
- **
|
|
27
|
-
-
|
|
26
|
+
- **5 个 hook 事件** — SessionStart / InstructionsLoaded / PostToolUseFailure / Stop / PreToolUse
|
|
27
|
+
- **必需文档/推理工具 + 4 个推荐插件** — Context7 官方插件 / sequential-thinking MCP + pua / claude-mem / frontend-design / chrome-devtools-mcp
|
|
28
28
|
- **优雅降级** — 依赖缺失时进入 fallback 模式并清晰告知
|
|
29
29
|
|
|
30
30
|
## 为什么用
|
|
@@ -41,23 +41,21 @@ CurDX-Flow 是一个 Claude Code 插件,把 6 个验证过的 AI 工程工作
|
|
|
41
41
|
|
|
42
42
|
## 安装
|
|
43
43
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
/plugin marketplace add wdx/curdx-flow
|
|
47
|
-
/plugin install curdx-flow
|
|
44
|
+
```bash
|
|
45
|
+
npx @curdx/flow install --all
|
|
48
46
|
```
|
|
49
47
|
|
|
48
|
+
这会用 `claude plugin ...` 非交互 CLI 安装 CurDX-Flow 插件、Context7 官方插件、必需 MCP,并安装推荐插件。
|
|
49
|
+
|
|
50
50
|
### 本地 dev
|
|
51
51
|
```bash
|
|
52
|
-
git clone https://github.com/
|
|
52
|
+
git clone https://github.com/curdx/curdx-flow
|
|
53
53
|
claude --plugin-dir ./curdx-flow
|
|
54
54
|
```
|
|
55
55
|
|
|
56
|
-
|
|
56
|
+
安装后在 Claude Code 内运行:
|
|
57
57
|
|
|
58
58
|
```
|
|
59
|
-
/curdx-flow:install-deps # 交互式安装 pua / claude-mem / frontend-design
|
|
60
|
-
/curdx-flow:doctor # 验证健康
|
|
61
59
|
/curdx-flow:init # 初始化你的项目
|
|
62
60
|
```
|
|
63
61
|
|
|
@@ -102,8 +100,8 @@ claude --plugin-dir ./curdx-flow
|
|
|
102
100
|
| 文档 | 何时读 |
|
|
103
101
|
|-----|------|
|
|
104
102
|
| [`docs/getting-started.md`](./docs/getting-started.md) | 首次使用,5 分钟上手 |
|
|
105
|
-
| [`docs/command-reference.md`](./docs/command-reference.md) |
|
|
106
|
-
| [`docs/agent-reference.md`](./docs/agent-reference.md) |
|
|
103
|
+
| [`docs/command-reference.md`](./docs/command-reference.md) | 9 个命令完整参考 |
|
|
104
|
+
| [`docs/agent-reference.md`](./docs/agent-reference.md) | 15 个内部代理 |
|
|
107
105
|
| [`docs/workflows.md`](./docs/workflows.md) | 5 种典型场景(greenfield/brownfield/epic/fast/UI) |
|
|
108
106
|
| [`docs/architecture.md`](./docs/architecture.md) | 内部设计(给扩展者) |
|
|
109
107
|
| [`docs/ethos.md`](./docs/ethos.md) | 设计决策的理由 |
|
|
@@ -132,7 +130,7 @@ CurDX-Flow 是蒸馏,不是原创。深深致谢:
|
|
|
132
130
|
- [**pua**](https://github.com/tanweai/pua) — 持续性 + 三条红线
|
|
133
131
|
- [**claude-mem**](https://github.com/thedotmack/claude-mem) — 自动跨会话记忆
|
|
134
132
|
- [**frontend-design skill**](https://claude.ai/skills/frontend-design) — distinctive UI(Anthropic 官方)
|
|
135
|
-
- **
|
|
133
|
+
- **Context7**(Upstash)+ **sequential-thinking**(Anthropic)+ **chrome-devtools-mcp**(Chrome 团队)
|
|
136
134
|
|
|
137
135
|
## 许可
|
|
138
136
|
|
|
@@ -140,7 +138,7 @@ MIT。见 [`LICENSE`](./LICENSE)。
|
|
|
140
138
|
|
|
141
139
|
## 贡献
|
|
142
140
|
|
|
143
|
-
- Issues: https://github.com/
|
|
141
|
+
- Issues: https://github.com/curdx/curdx-flow/issues
|
|
144
142
|
- 欢迎 PR,但请先用 `/curdx-flow:spec` 自己走一遍(吃自己的狗粮)
|
|
145
143
|
- 社区准则:友善 + 具体 + 不对着 LLM 发火
|
|
146
144
|
|
package/cli/README.md
CHANGED
|
@@ -24,9 +24,11 @@ npx @curdx/flow upgrade
|
|
|
24
24
|
|
|
25
25
|
Steps:
|
|
26
26
|
1. Verify the `claude` CLI is installed
|
|
27
|
-
2. `claude plugin marketplace add curdx/curdx-flow`
|
|
28
|
-
3. `claude plugin install curdx-flow@curdx-flow-marketplace`
|
|
29
|
-
4.
|
|
27
|
+
2. `claude plugin marketplace add --scope user curdx/curdx-flow`
|
|
28
|
+
3. `claude plugin install --scope user curdx-flow@curdx-flow-marketplace`
|
|
29
|
+
4. Install required companion plugin: **context7-plugin@context7-marketplace**
|
|
30
|
+
5. Register required user-level MCP: **sequential-thinking**
|
|
31
|
+
6. Interactively (or automatically) install recommended plugins: **pua**, **claude-mem**, **frontend-design**, **chrome-devtools-mcp**
|
|
30
32
|
|
|
31
33
|
| Flag | Purpose |
|
|
32
34
|
|------|---------|
|
|
@@ -35,7 +37,7 @@ Steps:
|
|
|
35
37
|
|
|
36
38
|
### `doctor [--verbose]`
|
|
37
39
|
|
|
38
|
-
External diagnostics: claude CLI / curdx-flow /
|
|
40
|
+
External diagnostics: claude CLI / curdx-flow / required MCPs / recommended plugins / current directory `.flow/` state.
|
|
39
41
|
|
|
40
42
|
### Project initialization (not a CLI command)
|
|
41
43
|
|
|
@@ -50,11 +52,11 @@ This keeps the CLI scoped to install-time and lifecycle operations only — anyt
|
|
|
50
52
|
|
|
51
53
|
### `upgrade`
|
|
52
54
|
|
|
53
|
-
`claude plugin marketplace update` + `claude plugin update` for every installed curdx-flow-related plugin.
|
|
55
|
+
`claude plugin marketplace update` + `claude plugin update --scope user` for every installed curdx-flow-related plugin.
|
|
54
56
|
|
|
55
57
|
### `uninstall [-y] [--keep-recommended] [--purge]`
|
|
56
58
|
|
|
57
|
-
Inverse of `install`. By default removes only the curdx-flow plugin. With `--purge`, also removes the `~/.local/bin/bun`
|
|
59
|
+
Inverse of `install`. By default removes only the curdx-flow plugin. Recommended plugins are kept unless selected interactively. With `--purge`, also removes third-party marketplaces and the `~/.local/bin/bun` / `~/.local/bin/uv` symlinks created by install.
|
|
58
60
|
|
|
59
61
|
## Why a CLI?
|
|
60
62
|
|
package/cli/doctor.js
CHANGED
|
@@ -10,12 +10,13 @@ import {
|
|
|
10
10
|
runSync,
|
|
11
11
|
claudeVersion,
|
|
12
12
|
listPlugins,
|
|
13
|
+
listPluginMarketplaces,
|
|
13
14
|
listMcps,
|
|
14
15
|
ensureClaudeMemRuntimes,
|
|
15
16
|
readUserMcpConfig,
|
|
16
17
|
findDuplicateMcps,
|
|
17
18
|
} from "./utils.js";
|
|
18
|
-
import { RECOMMENDED_PLUGINS } from "./registry.js";
|
|
19
|
+
import { BUNDLED_MCPS, REQUIRED_PLUGINS, RECOMMENDED_PLUGINS } from "./registry.js";
|
|
19
20
|
|
|
20
21
|
export async function doctor(args = []) {
|
|
21
22
|
const verbose = args.includes("--verbose") || args.includes("-v");
|
|
@@ -52,15 +53,37 @@ export async function doctor(args = []) {
|
|
|
52
53
|
warnings++;
|
|
53
54
|
}
|
|
54
55
|
|
|
56
|
+
const marketplaces = cv ? listPluginMarketplaces() : [];
|
|
57
|
+
const marketplaceNames = new Set(marketplaces.map((m) => m.name));
|
|
58
|
+
|
|
59
|
+
// ---------- Required plugins ----------
|
|
60
|
+
console.log(`\n${color.bold("Required plugins:")}`);
|
|
61
|
+
for (const r of REQUIRED_PLUGINS) {
|
|
62
|
+
const p = plugins.find((x) => x.id === r.id || x.name === r.name);
|
|
63
|
+
if (r.marketplaceSource && !marketplaceNames.has(r.marketplaceId)) {
|
|
64
|
+
log.warn(
|
|
65
|
+
`${r.marketplaceId.padEnd(22)} marketplace missing ${color.dim(`(run: claude plugin marketplace add --scope ${r.scope} ${r.marketplaceSource})`)}`
|
|
66
|
+
);
|
|
67
|
+
warnings++;
|
|
68
|
+
}
|
|
69
|
+
if (p && p.status === "enabled") {
|
|
70
|
+
log.ok(`${r.name.padEnd(22)} ${color.dim(`v${p.version || "unknown"}`)}`);
|
|
71
|
+
} else if (p && p.status === "failed") {
|
|
72
|
+
log.err(`${r.name.padEnd(22)} load failed`);
|
|
73
|
+
errors++;
|
|
74
|
+
} else {
|
|
75
|
+
log.warn(
|
|
76
|
+
`${r.name.padEnd(22)} not installed ${color.dim(`(run: claude plugin install --scope ${r.scope} ${r.installSpec})`)}`
|
|
77
|
+
);
|
|
78
|
+
warnings++;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
55
82
|
// ---------- MCPs ----------
|
|
56
|
-
// Bundled by curdx-flow plugin via .claude-plugin/plugin.json mcpServers.
|
|
57
|
-
// chrome-devtools is NOT here anymore — it was extracted into its own
|
|
58
|
-
// recommended plugin (see below) to align with the "each MCP owned by one
|
|
59
|
-
// plugin" model and avoid double-spawning the chrome-devtools-mcp process.
|
|
60
83
|
console.log(`\n${color.bold("MCP Servers (required by L2 mandatory tools):")}`);
|
|
61
84
|
const mcps = cv ? listMcps() : [];
|
|
62
|
-
const
|
|
63
|
-
|
|
85
|
+
for (const expected of BUNDLED_MCPS) {
|
|
86
|
+
const m = expected.name;
|
|
64
87
|
// Beta.12 onward: the required MCPs are registered at user-level
|
|
65
88
|
// (via `claude mcp add`), NOT plugin-bundled. Both still show up in
|
|
66
89
|
// `claude mcp list`. Accept a standalone user-level registration
|
|
@@ -80,7 +103,7 @@ export async function doctor(args = []) {
|
|
|
80
103
|
} else {
|
|
81
104
|
if (curdx) {
|
|
82
105
|
log.warn(
|
|
83
|
-
`${m.padEnd(22)} missing. Run: ${color.cyan(`claude mcp add ${m} --
|
|
106
|
+
`${m.padEnd(22)} missing. Run: ${color.cyan(`claude mcp add --scope user ${m} -- ${expected.command} ${expected.args.join(" ")}`)}`
|
|
84
107
|
);
|
|
85
108
|
warnings++;
|
|
86
109
|
} else {
|
|
@@ -93,7 +116,13 @@ export async function doctor(args = []) {
|
|
|
93
116
|
console.log(`\n${color.bold("Recommended plugins:")}`);
|
|
94
117
|
let claudeMemEnabled = false;
|
|
95
118
|
for (const r of RECOMMENDED_PLUGINS) {
|
|
96
|
-
const p = plugins.find((x) => x.name === r.name);
|
|
119
|
+
const p = plugins.find((x) => x.id === r.id || x.name === r.name);
|
|
120
|
+
if (r.marketplaceSource && !marketplaceNames.has(r.marketplaceId)) {
|
|
121
|
+
log.warn(
|
|
122
|
+
`${r.marketplaceId.padEnd(22)} marketplace missing ${color.dim(`(run: claude plugin marketplace add --scope ${r.scope} ${r.marketplaceSource})`)}`
|
|
123
|
+
);
|
|
124
|
+
warnings++;
|
|
125
|
+
}
|
|
97
126
|
if (p && p.status === "enabled") {
|
|
98
127
|
log.ok(`${r.name.padEnd(22)} ${color.dim(`v${p.version}`)}`);
|
|
99
128
|
if (r.postInstall === "claude-mem-runtimes") claudeMemEnabled = true;
|
|
@@ -102,7 +131,7 @@ export async function doctor(args = []) {
|
|
|
102
131
|
errors++;
|
|
103
132
|
} else {
|
|
104
133
|
log.warn(
|
|
105
|
-
`${r.name.padEnd(22)} not installed ${color.dim(`(run: claude plugin install ${r.installSpec})`)}`
|
|
134
|
+
`${r.name.padEnd(22)} not installed ${color.dim(`(run: claude plugin install --scope ${r.scope} ${r.installSpec})`)}`
|
|
106
135
|
);
|
|
107
136
|
warnings++;
|
|
108
137
|
}
|
package/cli/install.js
CHANGED
|
@@ -17,7 +17,7 @@ import {
|
|
|
17
17
|
ensureClaudeMemRuntimes,
|
|
18
18
|
} from "./utils.js";
|
|
19
19
|
import { injectGlobalProtocols, GLOBAL_CLAUDE_MD } from "./protocols.js";
|
|
20
|
-
import { RECOMMENDED_PLUGINS, BUNDLED_MCPS } from "./registry.js";
|
|
20
|
+
import { REQUIRED_PLUGINS, RECOMMENDED_PLUGINS, BUNDLED_MCPS } from "./registry.js";
|
|
21
21
|
import { readUserMcpConfig } from "./utils.js";
|
|
22
22
|
|
|
23
23
|
// When installed via npm, this CLI file lives at <pkg-root>/cli/install.js.
|
|
@@ -86,7 +86,7 @@ export async function install(args = []) {
|
|
|
86
86
|
|
|
87
87
|
const addRes = await run(
|
|
88
88
|
"claude",
|
|
89
|
-
["plugin", "marketplace", "add", marketplaceSource],
|
|
89
|
+
["plugin", "marketplace", "add", "--scope", "user", marketplaceSource],
|
|
90
90
|
{ silent: true }
|
|
91
91
|
);
|
|
92
92
|
if (addRes.code !== 0 && !addRes.stderr.includes("already")) {
|
|
@@ -121,12 +121,23 @@ export async function install(args = []) {
|
|
|
121
121
|
const already = prevCurdxFlow;
|
|
122
122
|
|
|
123
123
|
if (already && shippedVersion && already.version === shippedVersion) {
|
|
124
|
-
//
|
|
125
|
-
//
|
|
126
|
-
//
|
|
127
|
-
|
|
128
|
-
|
|
124
|
+
// Step 2 removes and re-adds the marketplace to rebind it to the current
|
|
125
|
+
// source. Claude Code removes plugins installed from that marketplace as
|
|
126
|
+
// part of `marketplace remove`, so even a same-version install must be
|
|
127
|
+
// re-registered here.
|
|
128
|
+
log.info(
|
|
129
|
+
`curdx-flow already at v${already.version}, re-registering...`
|
|
129
130
|
);
|
|
131
|
+
const r = await run(
|
|
132
|
+
"claude",
|
|
133
|
+
["plugin", "install", "--scope", "user", "curdx-flow@curdx-flow-marketplace"],
|
|
134
|
+
{ silent: true }
|
|
135
|
+
);
|
|
136
|
+
if (r.code !== 0) {
|
|
137
|
+
log.err(`Install failed: ${r.stderr.trim() || r.stdout.trim()}`);
|
|
138
|
+
process.exit(1);
|
|
139
|
+
}
|
|
140
|
+
log.ok(`curdx-flow re-registered at v${shippedVersion}`);
|
|
130
141
|
} else if (already && shippedVersion) {
|
|
131
142
|
// Existing install, different version — frame as an upgrade.
|
|
132
143
|
log.info(
|
|
@@ -134,7 +145,7 @@ export async function install(args = []) {
|
|
|
134
145
|
);
|
|
135
146
|
const r = await run(
|
|
136
147
|
"claude",
|
|
137
|
-
["plugin", "install", "curdx-flow@curdx-flow-marketplace"],
|
|
148
|
+
["plugin", "install", "--scope", "user", "curdx-flow@curdx-flow-marketplace"],
|
|
138
149
|
{ silent: true }
|
|
139
150
|
);
|
|
140
151
|
if (r.code !== 0) {
|
|
@@ -151,7 +162,7 @@ export async function install(args = []) {
|
|
|
151
162
|
);
|
|
152
163
|
const r = await run(
|
|
153
164
|
"claude",
|
|
154
|
-
["plugin", "install", "curdx-flow@curdx-flow-marketplace"],
|
|
165
|
+
["plugin", "install", "--scope", "user", "curdx-flow@curdx-flow-marketplace"],
|
|
155
166
|
{ silent: true }
|
|
156
167
|
);
|
|
157
168
|
if (r.code !== 0) {
|
|
@@ -162,7 +173,7 @@ export async function install(args = []) {
|
|
|
162
173
|
} else {
|
|
163
174
|
const r = await run(
|
|
164
175
|
"claude",
|
|
165
|
-
["plugin", "install", "curdx-flow@curdx-flow-marketplace"],
|
|
176
|
+
["plugin", "install", "--scope", "user", "curdx-flow@curdx-flow-marketplace"],
|
|
166
177
|
{ silent: true }
|
|
167
178
|
);
|
|
168
179
|
if (r.code !== 0) {
|
|
@@ -176,10 +187,46 @@ export async function install(args = []) {
|
|
|
176
187
|
}
|
|
177
188
|
}
|
|
178
189
|
|
|
179
|
-
// ---------- Step 3.5:
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
190
|
+
// ---------- Step 3.5: Install required plugins + register user-level MCPs ----------
|
|
191
|
+
log.blank();
|
|
192
|
+
log.info("Installing required Claude Code plugins...");
|
|
193
|
+
for (const plugin of REQUIRED_PLUGINS) {
|
|
194
|
+
console.log(` ${color.cyan("▸")} Installing ${color.bold(plugin.name)}...`);
|
|
195
|
+
const ma = await run(
|
|
196
|
+
"claude",
|
|
197
|
+
["plugin", "marketplace", "add", "--scope", plugin.scope, plugin.marketplaceSource],
|
|
198
|
+
{ silent: true }
|
|
199
|
+
);
|
|
200
|
+
if (ma.code !== 0 && !ma.stderr.includes("already")) {
|
|
201
|
+
log.warn(` marketplace add warning: ${ma.stderr.trim().split("\n")[0]}`);
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
const ir = await run(
|
|
205
|
+
"claude",
|
|
206
|
+
["plugin", "install", "--scope", plugin.scope, plugin.installSpec],
|
|
207
|
+
{ silent: true }
|
|
208
|
+
);
|
|
209
|
+
if (ir.code === 0) {
|
|
210
|
+
console.log(` ${color.green("✓")} ${plugin.name} installed`);
|
|
211
|
+
} else {
|
|
212
|
+
console.log(
|
|
213
|
+
` ${color.red("✗")} ${plugin.name} install failed: ${ir.stderr.trim().split("\n").pop()}`
|
|
214
|
+
);
|
|
215
|
+
console.log(
|
|
216
|
+
color.dim(
|
|
217
|
+
` Run manually: claude plugin marketplace add --scope ${plugin.scope} ${plugin.marketplaceSource}`
|
|
218
|
+
)
|
|
219
|
+
);
|
|
220
|
+
console.log(
|
|
221
|
+
color.dim(
|
|
222
|
+
` Then: claude plugin install --scope ${plugin.scope} ${plugin.installSpec}`
|
|
223
|
+
)
|
|
224
|
+
);
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
// Beta.12: direct MCPs migrated from plugin.json bundling. See cli/registry.js
|
|
229
|
+
// for the rationale. Context7 now uses Upstash's official plugin instead.
|
|
183
230
|
log.blank();
|
|
184
231
|
log.info("Registering required MCP servers (user-level)...");
|
|
185
232
|
const existingUserMcps = readUserMcpConfig();
|
|
@@ -193,7 +240,7 @@ export async function install(args = []) {
|
|
|
193
240
|
}
|
|
194
241
|
const r = await run(
|
|
195
242
|
"claude",
|
|
196
|
-
["mcp", "add", mcp.name, "--", mcp.command, ...mcp.args],
|
|
243
|
+
["mcp", "add", "--scope", "user", mcp.name, "--", mcp.command, ...mcp.args],
|
|
197
244
|
{ silent: true }
|
|
198
245
|
);
|
|
199
246
|
if (r.code === 0) {
|
|
@@ -205,7 +252,7 @@ export async function install(args = []) {
|
|
|
205
252
|
` ${mcp.name.padEnd(22)} registration failed: ${r.stderr.trim().split("\n").pop()}`
|
|
206
253
|
);
|
|
207
254
|
log.info(
|
|
208
|
-
` Run manually: claude mcp add ${mcp.name} -- ${mcp.command} ${mcp.args.join(" ")}`
|
|
255
|
+
` Run manually: claude mcp add --scope user ${mcp.name} -- ${mcp.command} ${mcp.args.join(" ")}`
|
|
209
256
|
);
|
|
210
257
|
}
|
|
211
258
|
}
|
|
@@ -251,10 +298,10 @@ export async function install(args = []) {
|
|
|
251
298
|
console.log(` ${color.cyan("▸")} Installing ${color.bold(rec.name)}...`);
|
|
252
299
|
|
|
253
300
|
// 1. Add marketplace (if needed)
|
|
254
|
-
if (rec.
|
|
301
|
+
if (rec.marketplaceSource) {
|
|
255
302
|
const ma = await run(
|
|
256
303
|
"claude",
|
|
257
|
-
["plugin", "marketplace", "add", rec.
|
|
304
|
+
["plugin", "marketplace", "add", "--scope", rec.scope, rec.marketplaceSource],
|
|
258
305
|
{ silent: true }
|
|
259
306
|
);
|
|
260
307
|
if (ma.code !== 0 && !ma.stderr.includes("already")) {
|
|
@@ -264,7 +311,7 @@ export async function install(args = []) {
|
|
|
264
311
|
}
|
|
265
312
|
|
|
266
313
|
// 2. Install
|
|
267
|
-
const ir = await run("claude", ["plugin", "install", rec.installSpec], {
|
|
314
|
+
const ir = await run("claude", ["plugin", "install", "--scope", rec.scope, rec.installSpec], {
|
|
268
315
|
silent: true,
|
|
269
316
|
});
|
|
270
317
|
if (ir.code === 0) {
|
|
@@ -298,7 +345,7 @@ export async function install(args = []) {
|
|
|
298
345
|
);
|
|
299
346
|
console.log(
|
|
300
347
|
color.dim(
|
|
301
|
-
` Run manually: claude plugin install ${rec.installSpec}`
|
|
348
|
+
` Run manually: claude plugin install --scope ${rec.scope} ${rec.installSpec}`
|
|
302
349
|
)
|
|
303
350
|
);
|
|
304
351
|
}
|
package/cli/registry.js
CHANGED
|
@@ -9,74 +9,89 @@
|
|
|
9
9
|
* or removing a plugin is a one-file change.
|
|
10
10
|
*
|
|
11
11
|
* Every consumer pulls what it needs via property access:
|
|
12
|
-
* - install.js →
|
|
13
|
-
* - uninstall.js → uninstallSpec
|
|
14
|
-
* - upgrade.js →
|
|
15
|
-
* - doctor.js →
|
|
12
|
+
* - install.js → marketplaceSource + installSpec + hint (+ optional postInstall)
|
|
13
|
+
* - uninstall.js → uninstallSpec + uninstallArgs + marketplaceId
|
|
14
|
+
* - upgrade.js → updateSpec + marketplaceId
|
|
15
|
+
* - doctor.js → id + installSpec (for health checks and recovery hints)
|
|
16
16
|
*/
|
|
17
17
|
|
|
18
18
|
export const RECOMMENDED_PLUGINS = [
|
|
19
19
|
{
|
|
20
20
|
name: "pua",
|
|
21
|
-
|
|
22
|
-
|
|
21
|
+
id: "pua@pua-skills",
|
|
22
|
+
marketplaceSource: "tanweai/pua",
|
|
23
|
+
marketplaceId: "pua-skills",
|
|
23
24
|
installSpec: "pua@pua-skills",
|
|
24
25
|
uninstallSpec: "pua@pua-skills",
|
|
26
|
+
updateSpec: "pua@pua-skills",
|
|
27
|
+
scope: "user",
|
|
25
28
|
hint: "no-give-up + three red lines",
|
|
26
29
|
},
|
|
27
30
|
{
|
|
28
31
|
name: "claude-mem",
|
|
29
|
-
|
|
32
|
+
id: "claude-mem@thedotmack",
|
|
33
|
+
marketplaceSource: "thedotmack/claude-mem",
|
|
30
34
|
marketplaceId: "thedotmack",
|
|
31
35
|
installSpec: "claude-mem@thedotmack",
|
|
32
36
|
uninstallSpec: "claude-mem@thedotmack",
|
|
37
|
+
updateSpec: "claude-mem@thedotmack",
|
|
38
|
+
uninstallArgs: ["--keep-data"],
|
|
39
|
+
scope: "user",
|
|
33
40
|
hint: "automatic cross-session memory",
|
|
34
41
|
postInstall: "claude-mem-runtimes",
|
|
35
42
|
},
|
|
36
43
|
{
|
|
37
44
|
name: "frontend-design",
|
|
38
45
|
// Already in default marketplace claude-plugins-official, no add needed
|
|
39
|
-
|
|
46
|
+
id: "frontend-design@claude-plugins-official",
|
|
47
|
+
marketplaceSource: null,
|
|
40
48
|
marketplaceId: "claude-plugins-official",
|
|
41
49
|
installSpec: "frontend-design@claude-plugins-official",
|
|
42
50
|
uninstallSpec: "frontend-design@claude-plugins-official",
|
|
51
|
+
updateSpec: "frontend-design@claude-plugins-official",
|
|
52
|
+
scope: "user",
|
|
43
53
|
hint: "Anthropic official UI skill",
|
|
44
54
|
},
|
|
45
55
|
{
|
|
46
56
|
name: "chrome-devtools-mcp",
|
|
47
|
-
|
|
57
|
+
id: "chrome-devtools-mcp@chrome-devtools-plugins",
|
|
58
|
+
marketplaceSource: "ChromeDevTools/chrome-devtools-mcp",
|
|
48
59
|
marketplaceId: "chrome-devtools-plugins",
|
|
49
60
|
installSpec: "chrome-devtools-mcp@chrome-devtools-plugins",
|
|
50
61
|
uninstallSpec: "chrome-devtools-mcp@chrome-devtools-plugins",
|
|
62
|
+
updateSpec: "chrome-devtools-mcp@chrome-devtools-plugins",
|
|
63
|
+
scope: "user",
|
|
51
64
|
hint: "Chrome DevTools + Puppeteer (Google official)",
|
|
52
65
|
},
|
|
53
66
|
];
|
|
54
67
|
|
|
68
|
+
export const REQUIRED_PLUGINS = [
|
|
69
|
+
{
|
|
70
|
+
name: "context7-plugin",
|
|
71
|
+
id: "context7-plugin@context7-marketplace",
|
|
72
|
+
marketplaceSource: "upstash/context7",
|
|
73
|
+
marketplaceId: "context7-marketplace",
|
|
74
|
+
installSpec: "context7-plugin@context7-marketplace",
|
|
75
|
+
uninstallSpec: "context7-plugin@context7-marketplace",
|
|
76
|
+
updateSpec: "context7-plugin@context7-marketplace",
|
|
77
|
+
scope: "user",
|
|
78
|
+
hint: "official Context7 plugin (MCP + skill + docs-researcher agent + /context7:docs)",
|
|
79
|
+
},
|
|
80
|
+
];
|
|
81
|
+
|
|
55
82
|
/**
|
|
56
|
-
*
|
|
57
|
-
*
|
|
58
|
-
* structured reasoning in L2). Starting beta.12 these are registered at
|
|
83
|
+
* MCP servers that curdx-flow depends on for its core discipline rules and
|
|
84
|
+
* still registers directly. Starting beta.12 these are registered at
|
|
59
85
|
* USER-LEVEL via `claude mcp add` instead of plugin.json bundling, so:
|
|
60
86
|
*
|
|
61
|
-
* - Tool names stay standard (
|
|
87
|
+
* - Tool names stay standard (mcp__sequential-thinking__*)
|
|
62
88
|
* — matching every agent's and knowledge doc's hardcoded references.
|
|
63
|
-
*
|
|
64
|
-
*
|
|
65
|
-
*
|
|
66
|
-
*
|
|
67
|
-
* — the DX pitfall that bit every early adopter with a pre-existing
|
|
68
|
-
* `claude mcp add context7 …` history.
|
|
89
|
+
*
|
|
90
|
+
* Context7 is installed via Upstash's official Claude Code plugin instead
|
|
91
|
+
* of direct `claude mcp add`: context7-plugin@context7-marketplace includes
|
|
92
|
+
* the MCP server, skill, docs-researcher agent, and /context7:docs command.
|
|
69
93
|
*/
|
|
70
94
|
export const BUNDLED_MCPS = [
|
|
71
|
-
{
|
|
72
|
-
name: "context7",
|
|
73
|
-
command: "npx",
|
|
74
|
-
args: ["-y", "@upstash/context7-mcp@latest"],
|
|
75
|
-
purpose: "library / framework docs lookup (L2 Mandatory Tool)",
|
|
76
|
-
// Respect any user-level entry with a custom `--api-key` or differing
|
|
77
|
-
// package spec — don't overwrite it on subsequent install runs.
|
|
78
|
-
preserveExisting: true,
|
|
79
|
-
},
|
|
80
95
|
{
|
|
81
96
|
name: "sequential-thinking",
|
|
82
97
|
command: "npx",
|
|
@@ -92,9 +107,8 @@ export const BUNDLED_MCPS = [
|
|
|
92
107
|
*/
|
|
93
108
|
export const MARKETPLACES_TO_REFRESH = [
|
|
94
109
|
"curdx-flow-marketplace",
|
|
95
|
-
...
|
|
96
|
-
|
|
97
|
-
.map((p) => p.marketplaceId),
|
|
110
|
+
...REQUIRED_PLUGINS.map((p) => p.marketplaceId),
|
|
111
|
+
...RECOMMENDED_PLUGINS.map((p) => p.marketplaceId),
|
|
98
112
|
];
|
|
99
113
|
|
|
100
114
|
/**
|
|
@@ -103,5 +117,6 @@ export const MARKETPLACES_TO_REFRESH = [
|
|
|
103
117
|
*/
|
|
104
118
|
export const PLUGINS_TO_UPDATE = [
|
|
105
119
|
"curdx-flow@curdx-flow-marketplace",
|
|
106
|
-
...
|
|
120
|
+
...REQUIRED_PLUGINS.map((p) => p.updateSpec),
|
|
121
|
+
...RECOMMENDED_PLUGINS.map((p) => p.updateSpec),
|
|
107
122
|
];
|
package/cli/uninstall.js
CHANGED
|
@@ -16,14 +16,24 @@ import {
|
|
|
16
16
|
listPlugins,
|
|
17
17
|
} from "./utils.js";
|
|
18
18
|
import { removeGlobalProtocols, GLOBAL_CLAUDE_MD } from "./protocols.js";
|
|
19
|
-
import { RECOMMENDED_PLUGINS, BUNDLED_MCPS } from "./registry.js";
|
|
19
|
+
import { REQUIRED_PLUGINS, RECOMMENDED_PLUGINS, BUNDLED_MCPS } from "./registry.js";
|
|
20
20
|
|
|
21
21
|
const HOME = homedir();
|
|
22
22
|
|
|
23
23
|
// Pull uninstall-relevant subset from the single registry. See registry.js.
|
|
24
|
-
const RECOMMENDED = RECOMMENDED_PLUGINS.map(({ name, uninstallSpec }) => ({
|
|
24
|
+
const RECOMMENDED = RECOMMENDED_PLUGINS.map(({ name, uninstallSpec, uninstallArgs, marketplaceId, scope }) => ({
|
|
25
25
|
name,
|
|
26
26
|
uninstallSpec,
|
|
27
|
+
uninstallArgs: uninstallArgs || [],
|
|
28
|
+
marketplaceId,
|
|
29
|
+
scope,
|
|
30
|
+
}));
|
|
31
|
+
const REQUIRED = REQUIRED_PLUGINS.map(({ name, uninstallSpec, uninstallArgs, marketplaceId, scope }) => ({
|
|
32
|
+
name,
|
|
33
|
+
uninstallSpec,
|
|
34
|
+
uninstallArgs: uninstallArgs || [],
|
|
35
|
+
marketplaceId,
|
|
36
|
+
scope,
|
|
27
37
|
}));
|
|
28
38
|
|
|
29
39
|
// Symlinks created by install.js (only cleaned with --purge)
|
|
@@ -68,7 +78,7 @@ export async function uninstall(args = []) {
|
|
|
68
78
|
} else {
|
|
69
79
|
const r = await run(
|
|
70
80
|
"claude",
|
|
71
|
-
["plugin", "uninstall", "curdx-flow@curdx-flow-marketplace"],
|
|
81
|
+
["plugin", "uninstall", "--scope", "user", "curdx-flow@curdx-flow-marketplace"],
|
|
72
82
|
{ silent: true }
|
|
73
83
|
);
|
|
74
84
|
if (r.code === 0) {
|
|
@@ -117,7 +127,7 @@ export async function uninstall(args = []) {
|
|
|
117
127
|
console.log(` ${color.cyan("▸")} Uninstalling ${color.bold(rec.name)}...`);
|
|
118
128
|
const r = await run(
|
|
119
129
|
"claude",
|
|
120
|
-
["plugin", "uninstall", rec.uninstallSpec],
|
|
130
|
+
["plugin", "uninstall", "--scope", rec.scope, ...rec.uninstallArgs, rec.uninstallSpec],
|
|
121
131
|
{ silent: true }
|
|
122
132
|
);
|
|
123
133
|
if (r.code === 0) {
|
|
@@ -163,9 +173,39 @@ export async function uninstall(args = []) {
|
|
|
163
173
|
}
|
|
164
174
|
}
|
|
165
175
|
|
|
176
|
+
// ---------- Step 4.75: uninstall required companion plugins ----------
|
|
177
|
+
log.blank();
|
|
178
|
+
log.info("Required companion plugins");
|
|
179
|
+
if (yes) {
|
|
180
|
+
log.info(
|
|
181
|
+
color.dim("--yes mode: keeping required companion plugins (use --purge to remove them)")
|
|
182
|
+
);
|
|
183
|
+
} else {
|
|
184
|
+
const removeRequired = await confirm(
|
|
185
|
+
`Remove required companion plugins (${REQUIRED.map((p) => p.name).join(", ")})? ${color.dim("(keeps shared tools available if other workflows depend on them)")}`,
|
|
186
|
+
false
|
|
187
|
+
);
|
|
188
|
+
if (removeRequired) {
|
|
189
|
+
for (const plugin of REQUIRED) {
|
|
190
|
+
const r = await run(
|
|
191
|
+
"claude",
|
|
192
|
+
["plugin", "uninstall", "--scope", plugin.scope, ...plugin.uninstallArgs, plugin.uninstallSpec],
|
|
193
|
+
{ silent: true }
|
|
194
|
+
);
|
|
195
|
+
if (r.code === 0) {
|
|
196
|
+
log.ok(` ${plugin.name.padEnd(22)} uninstalled`);
|
|
197
|
+
} else {
|
|
198
|
+
log.info(` ${plugin.name.padEnd(22)} ${color.dim("not present or already removed")}`);
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
} else {
|
|
202
|
+
log.info("Keeping required companion plugins");
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
|
|
166
206
|
// ---------- Step 5: cleanup symlinks (only with --purge) ----------
|
|
167
207
|
log.blank();
|
|
168
|
-
log.step(3, 4, "Runtime symlinks");
|
|
208
|
+
log.step(3, 4, "Runtime symlinks and marketplaces");
|
|
169
209
|
if (!purge) {
|
|
170
210
|
log.info(
|
|
171
211
|
color.dim("Keeping ~/.local/bin/bun, ~/.local/bin/uv (use --purge to remove)")
|
|
@@ -174,6 +214,27 @@ export async function uninstall(args = []) {
|
|
|
174
214
|
color.dim("Reason: these bun/uv binaries may be used by other tools — confirm before deleting")
|
|
175
215
|
);
|
|
176
216
|
} else {
|
|
217
|
+
const marketplaceIds = [
|
|
218
|
+
...new Set(
|
|
219
|
+
RECOMMENDED
|
|
220
|
+
.concat(REQUIRED)
|
|
221
|
+
.map((r) => r.marketplaceId)
|
|
222
|
+
.filter((id) => id && id !== "claude-plugins-official")
|
|
223
|
+
),
|
|
224
|
+
];
|
|
225
|
+
for (const marketplaceId of marketplaceIds) {
|
|
226
|
+
const r = await run(
|
|
227
|
+
"claude",
|
|
228
|
+
["plugin", "marketplace", "remove", marketplaceId],
|
|
229
|
+
{ silent: true }
|
|
230
|
+
);
|
|
231
|
+
if (r.code === 0) {
|
|
232
|
+
log.ok(`Removed marketplace ${marketplaceId}`);
|
|
233
|
+
} else if (!r.stderr.includes("not found")) {
|
|
234
|
+
log.warn(`Failed to remove marketplace ${marketplaceId}: ${r.stderr.trim().split("\n").pop()}`);
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
|
|
177
238
|
for (const link of MANAGED_SYMLINKS) {
|
|
178
239
|
if (!existsSync(link) && !isBrokenSymlink(link)) {
|
|
179
240
|
continue;
|
package/cli/upgrade.js
CHANGED
|
@@ -47,7 +47,7 @@ export async function upgrade(args = []) {
|
|
|
47
47
|
continue;
|
|
48
48
|
}
|
|
49
49
|
|
|
50
|
-
const r = await run("claude", ["plugin", "update", spec], { silent: true });
|
|
50
|
+
const r = await run("claude", ["plugin", "update", "--scope", "user", spec], { silent: true });
|
|
51
51
|
if (r.code === 0) {
|
|
52
52
|
const updated = r.stdout.includes("updated from");
|
|
53
53
|
if (updated) {
|
package/cli/utils.js
CHANGED
|
@@ -172,7 +172,7 @@ export function claudeVersion() {
|
|
|
172
172
|
* 2.1.117+). Falls back to parsing the human-readable stream-text output
|
|
173
173
|
* for older CLI versions, but warns that parser is brittle.
|
|
174
174
|
*
|
|
175
|
-
* Returns array of { name, version, status }.
|
|
175
|
+
* Returns array of { id, name, marketplaceId, version, status, scope }.
|
|
176
176
|
*/
|
|
177
177
|
export function listPlugins() {
|
|
178
178
|
// Preferred: structured JSON output.
|
|
@@ -182,9 +182,12 @@ export function listPlugins() {
|
|
|
182
182
|
const arr = JSON.parse(j.stdout);
|
|
183
183
|
return arr.map((p) => ({
|
|
184
184
|
// id has form "name@marketplace" — name is stable for dedup/lookup.
|
|
185
|
+
id: String(p.id || ""),
|
|
185
186
|
name: String(p.id || "").split("@")[0],
|
|
187
|
+
marketplaceId: String(p.id || "").split("@")[1] || undefined,
|
|
186
188
|
version: p.version,
|
|
187
189
|
status: p.enabled === false ? "disabled" : "enabled",
|
|
190
|
+
scope: p.scope,
|
|
188
191
|
raw: JSON.stringify(p),
|
|
189
192
|
}));
|
|
190
193
|
} catch {
|
|
@@ -203,18 +206,35 @@ export function listPlugins() {
|
|
|
203
206
|
const blocks = res.stdout.split(/\n\s*❯\s*/).slice(1);
|
|
204
207
|
for (const block of blocks) {
|
|
205
208
|
const lines = block.split("\n");
|
|
206
|
-
const
|
|
209
|
+
const id = lines[0].trim();
|
|
210
|
+
const name = id.split("@")[0];
|
|
207
211
|
const version = (block.match(/Version:\s*(\S+)/) || [])[1];
|
|
208
212
|
const status = block.includes("✔")
|
|
209
213
|
? "enabled"
|
|
210
214
|
: block.includes("✘")
|
|
211
215
|
? "failed"
|
|
212
216
|
: "unknown";
|
|
213
|
-
plugins.push({ name, version, status, raw: block });
|
|
217
|
+
plugins.push({ id, name, marketplaceId: id.split("@")[1], version, status, raw: block });
|
|
214
218
|
}
|
|
215
219
|
return plugins;
|
|
216
220
|
}
|
|
217
221
|
|
|
222
|
+
/**
|
|
223
|
+
* List configured Claude Code plugin marketplaces.
|
|
224
|
+
* Returns array of { name, source, repo, path } when `--json` is supported.
|
|
225
|
+
*/
|
|
226
|
+
export function listPluginMarketplaces() {
|
|
227
|
+
const j = runSync("claude", ["plugin", "marketplace", "list", "--json"]);
|
|
228
|
+
if (j.code === 0 && j.stdout.trim().startsWith("[")) {
|
|
229
|
+
try {
|
|
230
|
+
return JSON.parse(j.stdout);
|
|
231
|
+
} catch {
|
|
232
|
+
return [];
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
return [];
|
|
236
|
+
}
|
|
237
|
+
|
|
218
238
|
/**
|
|
219
239
|
* Read the user-level MCP registrations from ~/.claude.json. These are the
|
|
220
240
|
* MCPs the user added manually via `claude mcp add …` — distinct from
|