@curdx/flow 2.1.0 → 2.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/.claude-plugin/marketplace.json +25 -2
- package/.claude-plugin/plugin.json +10 -1
- package/CHANGELOG.md +32 -0
- package/README.md +3 -0
- package/README.zh.md +3 -0
- package/agent-preamble/preamble.md +2 -2
- package/agents/flow-qa-engineer.md +16 -15
- package/agents/flow-ui-researcher.md +2 -2
- package/agents/flow-verifier.md +3 -3
- package/bin/curdx-flow +5 -0
- package/cli/README.md +10 -9
- package/cli/install-companions.js +4 -1
- package/cli/install-required-plugins.js +18 -5
- package/cli/install-self-update.js +2 -91
- package/cli/install.js +12 -1
- package/cli/lib/claude.js +42 -11
- package/cli/lib/doctor-report.js +47 -8
- package/cli/lib/semver.js +95 -0
- package/cli/utils.js +1 -0
- package/hooks/scripts/quick-mode-guard.sh +6 -7
- package/knowledge/execution-strategies.md +5 -5
- package/knowledge/planning-reviews.md +2 -2
- package/knowledge/wave-execution.md +17 -17
- package/package.json +3 -2
- package/schemas/agent-frontmatter.schema.json +66 -0
- package/schemas/config.schema.json +23 -3
- package/schemas/gate-frontmatter.schema.json +30 -0
- package/schemas/hooks.schema.json +83 -0
- package/schemas/plugin-manifest.schema.json +66 -0
- package/schemas/skill-frontmatter.schema.json +72 -0
- package/schemas/spec-state.schema.json +6 -1
- package/skills/brownfield-index/SKILL.md +2 -1
- package/skills/browser-qa/SKILL.md +5 -4
- package/skills/debug/SKILL.md +1 -0
- package/skills/epic/SKILL.md +2 -1
- package/skills/fast/SKILL.md +2 -1
- package/skills/help/SKILL.md +1 -0
- package/skills/implement/SKILL.md +6 -5
- package/skills/implement/references/wave-execution.md +9 -9
- package/skills/init/SKILL.md +1 -0
- package/skills/review/SKILL.md +2 -1
- package/skills/security-audit/SKILL.md +2 -1
- package/skills/spec/SKILL.md +4 -3
- package/skills/start/SKILL.md +2 -1
- package/skills/ui-sketch/SKILL.md +2 -1
- package/skills/verify/SKILL.md +2 -1
- package/templates/CONTEXT.md.tmpl +1 -1
- package/templates/PROJECT.md.tmpl +1 -1
- package/templates/config.json.tmpl +6 -6
- package/templates/progress.md.tmpl +2 -2
|
@@ -6,20 +6,43 @@
|
|
|
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.
|
|
9
|
+
"version": "2.2.0"
|
|
10
10
|
},
|
|
11
|
+
"allowCrossMarketplaceDependenciesOn": [
|
|
12
|
+
"context7-marketplace"
|
|
13
|
+
],
|
|
11
14
|
"plugins": [
|
|
12
15
|
{
|
|
13
16
|
"name": "curdx-flow",
|
|
14
17
|
"source": "./",
|
|
15
18
|
"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.",
|
|
19
|
+
"version": "2.2.0",
|
|
20
|
+
"author": {
|
|
21
|
+
"name": "wdx",
|
|
22
|
+
"email": "bydongxin@gmail.com"
|
|
23
|
+
},
|
|
24
|
+
"homepage": "https://github.com/curdx/curdx-flow",
|
|
25
|
+
"repository": "https://github.com/curdx/curdx-flow",
|
|
26
|
+
"license": "MIT",
|
|
16
27
|
"keywords": [
|
|
17
28
|
"workflow",
|
|
18
29
|
"spec-driven",
|
|
19
30
|
"ai-engineering",
|
|
20
31
|
"orchestration"
|
|
21
32
|
],
|
|
22
|
-
"
|
|
33
|
+
"tags": [
|
|
34
|
+
"claude-code",
|
|
35
|
+
"spec-driven",
|
|
36
|
+
"verification",
|
|
37
|
+
"workflow"
|
|
38
|
+
],
|
|
39
|
+
"category": "productivity",
|
|
40
|
+
"dependencies": [
|
|
41
|
+
{
|
|
42
|
+
"name": "context7-plugin",
|
|
43
|
+
"marketplace": "context7-marketplace"
|
|
44
|
+
}
|
|
45
|
+
]
|
|
23
46
|
}
|
|
24
47
|
]
|
|
25
48
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "curdx-flow",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.2.0",
|
|
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",
|
|
@@ -16,5 +16,14 @@
|
|
|
16
16
|
"orchestration",
|
|
17
17
|
"karpathy",
|
|
18
18
|
"claude-code"
|
|
19
|
+
],
|
|
20
|
+
"skills": "./skills/",
|
|
21
|
+
"agents": "./agents/",
|
|
22
|
+
"hooks": "./hooks/hooks.json",
|
|
23
|
+
"dependencies": [
|
|
24
|
+
{
|
|
25
|
+
"name": "context7-plugin",
|
|
26
|
+
"marketplace": "context7-marketplace"
|
|
27
|
+
}
|
|
19
28
|
]
|
|
20
29
|
}
|
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,38 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to CurdX-Flow will be documented here.
|
|
4
4
|
|
|
5
|
+
## [2.2.0] - 2026-04-24
|
|
6
|
+
|
|
7
|
+
### Added
|
|
8
|
+
|
|
9
|
+
- **Plugin manifest dependency declaration** — `.claude-plugin/plugin.json` declares `skills/`, `agents/`, `hooks/hooks.json`, and a cross-marketplace dependency on `context7-plugin`. `marketplace.json` opts into `allowCrossMarketplaceDependenciesOn: ["context7-marketplace"]` and mirrors the dependency on the plugin entry. Claude Code resolves the dependency during install.
|
|
10
|
+
- **Plugin `bin/` PATH wrapper** — new `bin/curdx-flow` POSIX `sh` wrapper exposes the CLI on the Claude Code plugin `bin/` PATH, so the in-session Bash tool can invoke `curdx-flow` without `npx`.
|
|
11
|
+
- **Extension contract layer** — five JSON schemas (`plugin-manifest`, `skill-frontmatter`, `agent-frontmatter`, `gate-frontmatter`, `hooks`) plus `scripts/validate-plugin-contracts.mjs` and four contract test suites enforce structural correctness in CI. `prepublishOnly` runs the validator before `npm test`.
|
|
12
|
+
- **`addRequiredPluginMarketplaces`** — split out of `installRequiredPlugins`. The install flow now pre-registers required companion marketplaces immediately after `curdx-flow-marketplace`, so the cross-marketplace dependency resolves cleanly during the curdx-flow plugin install step.
|
|
13
|
+
- **Doctor: stale Claude Code warning + plugin error surfacing** — `curdx-flow doctor` warns when the local `claude` CLI is below `2.1.110` (the minimum version with modern plugin dependency resolution and `bin/` PATH support), and surfaces per-plugin load errors propagated from `claude plugin list --json`.
|
|
14
|
+
- **`cli/lib/semver.js`** — extracted from `install-self-update.js`. Adds `isVersionAtLeast` for the doctor warning. The updater re-exports for backward compat.
|
|
15
|
+
|
|
16
|
+
### Changed
|
|
17
|
+
|
|
18
|
+
- **Skill spec alignment** — primary slash workflows (`init`, `start`, `spec`, `implement`, `verify`, `review`, `fast`, `debug`, `help`) gain `disable-model-invocation: true` so the model cannot auto-trigger them outside an explicit slash. Specialty skills (`epic`, `browser-qa`, `ui-sketch`, `security-audit`, `brownfield-index`) split trigger phrases into `when_to_use` and keep `description` short.
|
|
19
|
+
- **`Task` → `Agent` tool naming** — every `allowed-tools`, `Task(...)` call site, and prose mention in `skills/`, `knowledge/`, and `templates/` updated to the current Claude Code `Agent` tool nomenclature.
|
|
20
|
+
- **chrome-devtools MCP namespace** — `agents/flow-{qa-engineer,ui-researcher,verifier}.md` and `skills/{browser-qa,verify}` updated to `mcp__chrome_devtools__*` (underscore namespace) and the renamed tool surface (`new_page`, `navigate_page`, `take_screenshot`, `list_console_messages`, `list_network_requests`, `take_snapshot`, `lighthouse_audit`).
|
|
21
|
+
- **Doctor: duplicate-MCP section** renamed from "Legacy plugin-bundled MCPs still present" to "Duplicate MCP registrations", with ownership-aware remediation (the official Context7 plugin gets `claude mcp remove --scope user` guidance, the migrated curdx-flow MCPs get the `claude plugin update` guidance).
|
|
22
|
+
- **Templates** — `templates/{CONTEXT,PROJECT,progress,config}.md.tmpl` reference the current `/curdx-flow:*` slash names; `templates/config.json.tmpl` removes the retired `sketch` / `autonomous` modes and the retired `simplicity-gate` / `hard-gate` gates, and adds `wave_fail_policy`.
|
|
23
|
+
- **`schemas/config.schema.json`** — new `gateName` definition enumerates the eight real gates; `gates.always_on / standard_mode / enterprise_mode` switch to `$ref` on it. Added `execution.wave_fail_policy`.
|
|
24
|
+
- **`schemas/spec-state.schema.json`** — added `quickMode` boolean (the hook-controlled no-ask flag); fixed `goal` description to reference `/curdx-flow:start`.
|
|
25
|
+
- **`hooks/scripts/quick-mode-guard.sh`** — stop treating the retired `mode=autonomous` as quick mode. Only `quickMode=true` denies `AskUserQuestion`. Regression test added.
|
|
26
|
+
- **`agent-preamble/preamble.md`** — drops the "commands" wording and documents the quick-mode `AskUserQuestion` behavior.
|
|
27
|
+
|
|
28
|
+
### Removed
|
|
29
|
+
|
|
30
|
+
- **`CLAUDE.md`** — blanked. The L1 mind baseline now lives in `hooks/scripts/inject-karpathy.sh` (injected on every SessionStart) and the protocol section that the installer writes to `~/.claude/CLAUDE.md`. The repo file added drift surface without adding signal. The matching `language-rule-scan` allow-list entry is removed.
|
|
31
|
+
- **`mode=autonomous`** — fully retired. The hook no longer treats it as quick mode, the templates no longer advertise it, and `schema-drift.test.js` regression-locks both.
|
|
32
|
+
|
|
33
|
+
### Required
|
|
34
|
+
|
|
35
|
+
- **Claude Code 2.1.110+** — the plugin dependency resolution and the plugin `bin/` PATH support require this baseline. The installer warns if your local `claude` is older.
|
|
36
|
+
|
|
5
37
|
## [2.0.21] - 2026-04-23
|
|
6
38
|
|
|
7
39
|
### Fixed
|
package/README.md
CHANGED
|
@@ -24,6 +24,8 @@ Not a framework. Not a methodology library. A discipline layer.
|
|
|
24
24
|
|
|
25
25
|
## Install (one command)
|
|
26
26
|
|
|
27
|
+
Requires Claude Code v2.1.110+ (latest recommended). The installer checks your local `claude` binary and `curdx-flow doctor` warns if the version is too old for modern plugin dependency handling.
|
|
28
|
+
|
|
27
29
|
```bash
|
|
28
30
|
npx @curdx/flow install --all
|
|
29
31
|
```
|
|
@@ -101,6 +103,7 @@ npm i -g @curdx/flow@^1.1
|
|
|
101
103
|
- **5 hook events** that enforce the discipline without user action
|
|
102
104
|
- **Required docs/reasoning tools** — Context7 official plugin (MCP + skill + docs agent) and sequential-thinking MCP
|
|
103
105
|
- **4 recommended companion plugins** — pua, claude-mem, frontend-design, chrome-devtools-mcp
|
|
106
|
+
- **Plugin PATH helper** — `curdx-flow` is available inside Claude Code Bash sessions via the plugin `bin/` directory on modern Claude Code
|
|
104
107
|
- **Offline-capable install** — the npm package ships the full plugin body; zero GitHub round-trips during install
|
|
105
108
|
|
|
106
109
|
## Documentation
|
package/README.zh.md
CHANGED
|
@@ -31,6 +31,7 @@ CurdX-Flow 是 Claude Code 之上的一层薄**纪律层**,只做三件事,
|
|
|
31
31
|
- **10 个知识文档** — 规格驱动 / POC-First / 原子提交 / 执行策略 / ...
|
|
32
32
|
- **4 个 hook 事件** — SessionStart / SessionStart(startup|clear|compact) / Stop / PreToolUse
|
|
33
33
|
- **必需文档/推理工具 + 4 个推荐插件** — Context7 官方插件 / sequential-thinking MCP + pua / claude-mem / frontend-design / chrome-devtools-mcp
|
|
34
|
+
- **插件 PATH helper** — 在新版 Claude Code 的 Bash 工具里可直接调用 `curdx-flow`
|
|
34
35
|
- **优雅降级** — 依赖缺失时进入 fallback 模式并清晰告知
|
|
35
36
|
|
|
36
37
|
## 为什么用
|
|
@@ -47,6 +48,8 @@ CurdX-Flow 是 Claude Code 之上的一层薄**纪律层**,只做三件事,
|
|
|
47
48
|
|
|
48
49
|
## 安装
|
|
49
50
|
|
|
51
|
+
需要 Claude Code v2.1.110+(推荐最新版)。安装器会检查本机 `claude`,`curdx-flow doctor` 会在版本过旧、无法完整支持新版插件依赖解析时给出警告。
|
|
52
|
+
|
|
50
53
|
```bash
|
|
51
54
|
npx @curdx/flow install --all
|
|
52
55
|
```
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# CurdX-Flow Agent Shared Preamble
|
|
2
2
|
|
|
3
|
-
> All `flow-*` agents
|
|
3
|
+
> All `flow-*` agents inherit this file via `@${CLAUDE_PLUGIN_ROOT}/agent-preamble/preamble.md`.
|
|
4
4
|
> This is a behavioral baseline, not a suggestion. Violation counts as agent failure.
|
|
5
5
|
|
|
6
6
|
---
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
|
|
10
10
|
### 1. Think Before Coding
|
|
11
11
|
- State your assumptions before starting
|
|
12
|
-
- When
|
|
12
|
+
- When uncertainty changes product/business direction, use AskUserQuestion unless the current mode forbids it; in quick mode, state the best assumption and continue
|
|
13
13
|
- Offer multiple interpretations and let the user choose
|
|
14
14
|
- Never skip "understanding" and jump straight to "implementation"
|
|
15
15
|
|
|
@@ -34,19 +34,20 @@ Output: `.flow/specs/<name>/qa-report.md`.
|
|
|
34
34
|
|
|
35
35
|
## Core Tool: chrome-devtools MCP
|
|
36
36
|
|
|
37
|
-
What you can do via `
|
|
37
|
+
What you can do via `mcp__chrome_devtools__*`:
|
|
38
38
|
|
|
39
39
|
### Navigation and Interaction
|
|
40
|
-
- `
|
|
41
|
-
- `click` / `
|
|
42
|
-
- `
|
|
43
|
-
- `wait_for` — wait for
|
|
40
|
+
- `new_page` / `navigate_page` — open or change URL
|
|
41
|
+
- `click` / `type_text` / `fill` — interact
|
|
42
|
+
- `take_screenshot` — take screenshot
|
|
43
|
+
- `wait_for` — wait for visible text
|
|
44
44
|
|
|
45
45
|
### Diagnostics
|
|
46
|
-
- `
|
|
47
|
-
- `
|
|
46
|
+
- `list_console_messages` — capture console errors
|
|
47
|
+
- `list_network_requests` — list of network requests (including failed)
|
|
48
48
|
- `performance_start_trace` / `performance_stop_trace` — performance trace
|
|
49
|
-
- `
|
|
49
|
+
- `take_snapshot` — accessibility tree snapshot
|
|
50
|
+
- `lighthouse_audit` — accessibility, SEO, and best-practice audit
|
|
50
51
|
|
|
51
52
|
---
|
|
52
53
|
|
|
@@ -78,17 +79,17 @@ Read from `design.md`:
|
|
|
78
79
|
For each core AC, run through it in the browser:
|
|
79
80
|
|
|
80
81
|
```
|
|
81
|
-
|
|
82
|
+
mcp__chrome_devtools__navigate_page → localhost:3000
|
|
82
83
|
click → login button
|
|
83
84
|
fill → email / password
|
|
84
85
|
click → submit
|
|
85
86
|
wait_for → redirect to dashboard
|
|
86
|
-
|
|
87
|
+
mcp__chrome_devtools__take_screenshot
|
|
87
88
|
```
|
|
88
89
|
|
|
89
90
|
Capture:
|
|
90
|
-
- Console errors (
|
|
91
|
-
- Network failures (non-2xx in
|
|
91
|
+
- Console errors (`list_console_messages`)
|
|
92
|
+
- Network failures (non-2xx in `list_network_requests`)
|
|
92
93
|
- Performance data (e.g. LCP, INP)
|
|
93
94
|
- Final URL / page state
|
|
94
95
|
|
|
@@ -122,7 +123,7 @@ Capture:
|
|
|
122
123
|
### Step 5: Accessibility Review
|
|
123
124
|
|
|
124
125
|
```
|
|
125
|
-
|
|
126
|
+
mcp__chrome_devtools__take_snapshot
|
|
126
127
|
```
|
|
127
128
|
|
|
128
129
|
Check:
|
|
@@ -134,9 +135,9 @@ Check:
|
|
|
134
135
|
### Step 6: Performance Review
|
|
135
136
|
|
|
136
137
|
```
|
|
137
|
-
|
|
138
|
+
mcp__chrome_devtools__performance_start_trace
|
|
138
139
|
# run through user flow
|
|
139
|
-
|
|
140
|
+
mcp__chrome_devtools__performance_stop_trace
|
|
140
141
|
```
|
|
141
142
|
|
|
142
143
|
Check:
|
|
@@ -62,8 +62,8 @@ WebSearch: "<competitor> <feature> screenshot"
|
|
|
62
62
|
|
|
63
63
|
If chrome-devtools MCP is available:
|
|
64
64
|
```
|
|
65
|
-
|
|
66
|
-
|
|
65
|
+
mcp__chrome_devtools__navigate_page → <competitor URL>
|
|
66
|
+
mcp__chrome_devtools__take_screenshot → save to .flow/specs/<name>/ui-research/refs/
|
|
67
67
|
```
|
|
68
68
|
|
|
69
69
|
### Step 4: Classify with sequential-thinking
|
package/agents/flow-verifier.md
CHANGED
|
@@ -124,11 +124,11 @@ Code inspection + unit tests are **insufficient** evidence for a UI-facing AC. A
|
|
|
124
124
|
For every UI-facing AC:
|
|
125
125
|
|
|
126
126
|
```
|
|
127
|
-
1. Check chrome-devtools MCP availability (
|
|
127
|
+
1. Check chrome-devtools MCP availability (`mcp__chrome_devtools__*`).
|
|
128
128
|
2. If available:
|
|
129
129
|
- Start the app (dev server or served build) in the current repo.
|
|
130
|
-
- Drive the flow described in the AC: click /
|
|
131
|
-
- Capture
|
|
130
|
+
- Drive the flow described in the AC: `click` / `type_text` / `fill` / `navigate_page`.
|
|
131
|
+
- Capture evidence with `take_screenshot`, `list_console_messages`, and `list_network_requests`.
|
|
132
132
|
- Compare observed behavior against the AC text.
|
|
133
133
|
- Verdict: verified | partial | failed, with the screenshot as evidence.
|
|
134
134
|
3. If chrome-devtools MCP is NOT available:
|
package/bin/curdx-flow
ADDED
package/cli/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# curdx-flow CLI
|
|
2
2
|
|
|
3
|
-
One-shot installer + external diagnostics + external init.
|
|
3
|
+
One-shot installer + external diagnostics + external init. Requires Node.js 18+ and Claude Code v2.1.110+.
|
|
4
4
|
|
|
5
5
|
## Quick Start
|
|
6
6
|
|
|
@@ -25,10 +25,11 @@ npx @curdx/flow upgrade
|
|
|
25
25
|
Steps:
|
|
26
26
|
1. Verify the `claude` CLI is installed
|
|
27
27
|
2. `claude plugin marketplace add --scope user curdx/curdx-flow`
|
|
28
|
-
3.
|
|
29
|
-
4.
|
|
30
|
-
5.
|
|
31
|
-
6.
|
|
28
|
+
3. Pre-register required companion marketplaces so plugin dependencies can resolve
|
|
29
|
+
4. `claude plugin install --scope user curdx-flow@curdx-flow-marketplace`
|
|
30
|
+
5. Install required companion plugin: **context7-plugin@context7-marketplace**
|
|
31
|
+
6. Register required user-level MCP: **sequential-thinking**
|
|
32
|
+
7. Interactively (or automatically) install recommended plugins: **pua**, **claude-mem**, **frontend-design**, **chrome-devtools-mcp**
|
|
32
33
|
|
|
33
34
|
| Flag | Purpose |
|
|
34
35
|
|------|---------|
|
|
@@ -66,12 +67,12 @@ The **core workflow** still lives inside Claude Code (`/curdx-flow:*` commands).
|
|
|
66
67
|
- **External diagnostics**: check health without entering Claude Code
|
|
67
68
|
- **Batch deployment**: write a script for your team — `for host in ...; do ssh $host npx github:curdx/curdx-flow install; done`
|
|
68
69
|
|
|
69
|
-
##
|
|
70
|
+
## Small-dependency design
|
|
70
71
|
|
|
71
72
|
- Pure ES Modules (Node 18+)
|
|
72
|
-
-
|
|
73
|
-
- No `
|
|
74
|
-
- Fast `npx`
|
|
73
|
+
- Tiny runtime dependency surface (`@clack/prompts`, `picocolors`)
|
|
74
|
+
- No `commander` / `inquirer` / `execa`
|
|
75
|
+
- Fast `npx` and offline-capable plugin install
|
|
75
76
|
|
|
76
77
|
## Local development
|
|
77
78
|
|
|
@@ -11,6 +11,9 @@
|
|
|
11
11
|
* install-context7-config.js — context7 API-key prompt (private to required)
|
|
12
12
|
*/
|
|
13
13
|
|
|
14
|
-
export {
|
|
14
|
+
export {
|
|
15
|
+
addRequiredPluginMarketplaces,
|
|
16
|
+
installRequiredPlugins,
|
|
17
|
+
} from "./install-required-plugins.js";
|
|
15
18
|
export { registerBundledMcps } from "./install-bundled-mcps.js";
|
|
16
19
|
export { installRecommendedPlugins } from "./install-recommended-plugins.js";
|
|
@@ -8,15 +8,28 @@ import { REQUIRED_PLUGINS } from "./registry.js";
|
|
|
8
8
|
import { color, log, resultLastLine } from "./utils.js";
|
|
9
9
|
import { installContext7Config } from "./install-context7-config.js";
|
|
10
10
|
|
|
11
|
-
export async function
|
|
12
|
-
log.blank();
|
|
13
|
-
log.info("Installing required Claude Code plugins...");
|
|
11
|
+
export async function addRequiredPluginMarketplaces({ logWarnings = true } = {}) {
|
|
14
12
|
for (const plugin of REQUIRED_PLUGINS) {
|
|
15
|
-
console.log(` ${color.cyan("▸")} Installing ${color.bold(plugin.name)}...`);
|
|
16
13
|
const ma = await addPluginMarketplace(plugin);
|
|
17
|
-
if (ma.code !== 0 && !ma.stderr.includes("already")) {
|
|
14
|
+
if (ma.code !== 0 && !ma.stderr.includes("already") && logWarnings) {
|
|
18
15
|
log.warn(` marketplace add warning: ${ma.stderr.trim().split("\n")[0]}`);
|
|
19
16
|
}
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export async function installRequiredPlugins({
|
|
21
|
+
yes,
|
|
22
|
+
language,
|
|
23
|
+
config,
|
|
24
|
+
skipMarketplaceAdd = false,
|
|
25
|
+
}) {
|
|
26
|
+
log.blank();
|
|
27
|
+
log.info("Installing required Claude Code plugins...");
|
|
28
|
+
if (!skipMarketplaceAdd) {
|
|
29
|
+
await addRequiredPluginMarketplaces();
|
|
30
|
+
}
|
|
31
|
+
for (const plugin of REQUIRED_PLUGINS) {
|
|
32
|
+
console.log(` ${color.cyan("▸")} Installing ${color.bold(plugin.name)}...`);
|
|
20
33
|
|
|
21
34
|
const ir = await installPlugin(plugin);
|
|
22
35
|
if (ir.code === 0) {
|
|
@@ -2,98 +2,9 @@ import { existsSync } from "node:fs";
|
|
|
2
2
|
import { join } from "node:path";
|
|
3
3
|
|
|
4
4
|
import { log, run, runSync, VERSION } from "./utils.js";
|
|
5
|
+
import { compareVersions, isVersionNewer } from "./lib/semver.js";
|
|
5
6
|
|
|
6
|
-
|
|
7
|
-
return /^\d+$/.test(token) ? Number(token) : token;
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
function parseVersion(version) {
|
|
11
|
-
const normalized = String(version || "").trim().replace(/^v/i, "");
|
|
12
|
-
const [coreRaw = "0", prereleaseRaw] = normalized.split("-", 2);
|
|
13
|
-
const core = coreRaw.split(".").map((part) => Number.parseInt(part, 10) || 0);
|
|
14
|
-
const prerelease = prereleaseRaw
|
|
15
|
-
? prereleaseRaw.split(/[.-]/).filter(Boolean).map(normalizeVersionToken)
|
|
16
|
-
: [];
|
|
17
|
-
|
|
18
|
-
return { core, prerelease };
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
function compareIdentifier(left, right) {
|
|
22
|
-
if (left === right) {
|
|
23
|
-
return 0;
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
const leftIsNumber = typeof left === "number";
|
|
27
|
-
const rightIsNumber = typeof right === "number";
|
|
28
|
-
|
|
29
|
-
if (leftIsNumber && rightIsNumber) {
|
|
30
|
-
return left > right ? 1 : -1;
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
if (leftIsNumber) {
|
|
34
|
-
return -1;
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
if (rightIsNumber) {
|
|
38
|
-
return 1;
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
return left > right ? 1 : -1;
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
export function compareVersions(leftVersion, rightVersion) {
|
|
45
|
-
const left = parseVersion(leftVersion);
|
|
46
|
-
const right = parseVersion(rightVersion);
|
|
47
|
-
const coreLength = Math.max(left.core.length, right.core.length);
|
|
48
|
-
|
|
49
|
-
for (let index = 0; index < coreLength; index += 1) {
|
|
50
|
-
const leftPart = left.core[index] ?? 0;
|
|
51
|
-
const rightPart = right.core[index] ?? 0;
|
|
52
|
-
if (leftPart !== rightPart) {
|
|
53
|
-
return leftPart > rightPart ? 1 : -1;
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
const leftHasPrerelease = left.prerelease.length > 0;
|
|
58
|
-
const rightHasPrerelease = right.prerelease.length > 0;
|
|
59
|
-
|
|
60
|
-
if (!leftHasPrerelease && !rightHasPrerelease) {
|
|
61
|
-
return 0;
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
if (!leftHasPrerelease) {
|
|
65
|
-
return 1;
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
if (!rightHasPrerelease) {
|
|
69
|
-
return -1;
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
const prereleaseLength = Math.max(left.prerelease.length, right.prerelease.length);
|
|
73
|
-
for (let index = 0; index < prereleaseLength; index += 1) {
|
|
74
|
-
const leftPart = left.prerelease[index];
|
|
75
|
-
const rightPart = right.prerelease[index];
|
|
76
|
-
|
|
77
|
-
if (leftPart === undefined) {
|
|
78
|
-
return -1;
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
if (rightPart === undefined) {
|
|
82
|
-
return 1;
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
const comparison = compareIdentifier(leftPart, rightPart);
|
|
86
|
-
if (comparison !== 0) {
|
|
87
|
-
return comparison;
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
return 0;
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
export function isVersionNewer(latestVersion, currentVersion) {
|
|
95
|
-
return compareVersions(latestVersion, currentVersion) > 0;
|
|
96
|
-
}
|
|
7
|
+
export { compareVersions, isVersionNewer };
|
|
97
8
|
|
|
98
9
|
/**
|
|
99
10
|
* Check for CLI updates and auto-update if available.
|
package/cli/install.js
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
5
|
import {
|
|
6
|
+
addRequiredPluginMarketplaces,
|
|
6
7
|
installRequiredPlugins,
|
|
7
8
|
registerBundledMcps,
|
|
8
9
|
} from "./install-companions.js";
|
|
@@ -60,6 +61,11 @@ export async function install(args = []) {
|
|
|
60
61
|
|
|
61
62
|
await addCurdxMarketplace(context);
|
|
62
63
|
|
|
64
|
+
// Claude Code resolves plugin dependencies during install. Register
|
|
65
|
+
// required companion marketplaces before installing curdx-flow so its
|
|
66
|
+
// cross-marketplace dependency on Context7 can be satisfied immediately.
|
|
67
|
+
await addRequiredPluginMarketplaces({ logWarnings: false });
|
|
68
|
+
|
|
63
69
|
// ---------- Step 3: Install curdx-flow plugin ----------
|
|
64
70
|
// Read the version the marketplace is shipping so we can decide whether an
|
|
65
71
|
// already-installed plugin needs an update (same name but stale version
|
|
@@ -68,7 +74,12 @@ export async function install(args = []) {
|
|
|
68
74
|
await installCurdxFlowPlugin({ prevCurdxFlow, shippedVersion });
|
|
69
75
|
|
|
70
76
|
// ---------- Step 3.5: Install required plugins + register user-level MCPs ----------
|
|
71
|
-
await installRequiredPlugins({
|
|
77
|
+
await installRequiredPlugins({
|
|
78
|
+
yes: context.yes,
|
|
79
|
+
language,
|
|
80
|
+
config,
|
|
81
|
+
skipMarketplaceAdd: true,
|
|
82
|
+
});
|
|
72
83
|
|
|
73
84
|
// Beta.12: direct MCPs migrated from plugin.json bundling. See cli/registry.js
|
|
74
85
|
// for the rationale. Context7 now uses Upstash's official plugin instead.
|
package/cli/lib/claude.js
CHANGED
|
@@ -14,20 +14,51 @@ export function claudeVersion() {
|
|
|
14
14
|
return m ? m[1] : res.stdout.trim().split("\n")[0];
|
|
15
15
|
}
|
|
16
16
|
|
|
17
|
+
function normalizePluginError(error) {
|
|
18
|
+
if (typeof error === "string") {
|
|
19
|
+
return error;
|
|
20
|
+
}
|
|
21
|
+
if (error && typeof error === "object") {
|
|
22
|
+
return error.message || error.code || JSON.stringify(error);
|
|
23
|
+
}
|
|
24
|
+
return String(error);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export function parsePluginListJson(output) {
|
|
28
|
+
const trimmed = String(output || "").trim();
|
|
29
|
+
if (!trimmed.startsWith("[")) {
|
|
30
|
+
return null;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const arr = JSON.parse(trimmed);
|
|
34
|
+
return arr.map((p) => {
|
|
35
|
+
const id = String(p.id || p.name || "");
|
|
36
|
+
const errors = Array.isArray(p.errors)
|
|
37
|
+
? p.errors.map(normalizePluginError).filter(Boolean)
|
|
38
|
+
: [];
|
|
39
|
+
const enabled = p.enabled !== false;
|
|
40
|
+
|
|
41
|
+
return {
|
|
42
|
+
id,
|
|
43
|
+
name: id.split("@")[0],
|
|
44
|
+
marketplaceId: id.split("@")[1] || undefined,
|
|
45
|
+
version: p.version,
|
|
46
|
+
status: errors.length > 0 ? "failed" : enabled ? "enabled" : "disabled",
|
|
47
|
+
scope: p.scope,
|
|
48
|
+
errors,
|
|
49
|
+
raw: JSON.stringify(p),
|
|
50
|
+
};
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
|
|
17
54
|
export function listPlugins() {
|
|
18
55
|
const j = runSync("claude", ["plugin", "list", "--json"]);
|
|
19
|
-
if (j.code === 0
|
|
56
|
+
if (j.code === 0) {
|
|
20
57
|
try {
|
|
21
|
-
const
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
marketplaceId: String(p.id || "").split("@")[1] || undefined,
|
|
26
|
-
version: p.version,
|
|
27
|
-
status: p.enabled === false ? "disabled" : "enabled",
|
|
28
|
-
scope: p.scope,
|
|
29
|
-
raw: JSON.stringify(p),
|
|
30
|
-
}));
|
|
58
|
+
const plugins = parsePluginListJson(j.stdout);
|
|
59
|
+
if (plugins) {
|
|
60
|
+
return plugins;
|
|
61
|
+
}
|
|
31
62
|
} catch {
|
|
32
63
|
// JSON parse failed; fall through to legacy text parser.
|
|
33
64
|
}
|
package/cli/lib/doctor-report.js
CHANGED
|
@@ -4,6 +4,13 @@ import {
|
|
|
4
4
|
findPluginByRegistryEntry,
|
|
5
5
|
hasMarketplace,
|
|
6
6
|
} from "./claude.js";
|
|
7
|
+
import { isVersionAtLeast } from "./semver.js";
|
|
8
|
+
|
|
9
|
+
export const MIN_CLAUDE_VERSION = "2.1.110";
|
|
10
|
+
|
|
11
|
+
function pluginErrorDetails(plugin) {
|
|
12
|
+
return Array.isArray(plugin?.errors) ? plugin.errors : [];
|
|
13
|
+
}
|
|
7
14
|
|
|
8
15
|
export function buildDoctorReport({
|
|
9
16
|
claudeVersionValue,
|
|
@@ -37,6 +44,17 @@ export function buildDoctorReport({
|
|
|
37
44
|
|
|
38
45
|
if (claudeVersionValue) {
|
|
39
46
|
pushLine(lines, "ok", `claude CLI ${claudeVersionValue}`);
|
|
47
|
+
if (!isVersionAtLeast(claudeVersionValue, MIN_CLAUDE_VERSION)) {
|
|
48
|
+
pushLine(
|
|
49
|
+
lines,
|
|
50
|
+
"warn",
|
|
51
|
+
`claude CLI ${claudeVersionValue} below recommended ${MIN_CLAUDE_VERSION}`,
|
|
52
|
+
[
|
|
53
|
+
"curdx-flow uses modern Claude Code plugin dependency resolution and plugin bin/ PATH support",
|
|
54
|
+
"run: claude update",
|
|
55
|
+
]
|
|
56
|
+
);
|
|
57
|
+
}
|
|
40
58
|
} else {
|
|
41
59
|
pushLine(lines, "err", "claude CLI not found (install Claude Code)");
|
|
42
60
|
}
|
|
@@ -47,7 +65,12 @@ export function buildDoctorReport({
|
|
|
47
65
|
if (curdx.status === "enabled") {
|
|
48
66
|
pushLine(lines, "ok", `curdx-flow v${curdx.version} (enabled)`);
|
|
49
67
|
} else {
|
|
50
|
-
pushLine(
|
|
68
|
+
pushLine(
|
|
69
|
+
lines,
|
|
70
|
+
"err",
|
|
71
|
+
`curdx-flow v${curdx.version} (${curdx.status})`,
|
|
72
|
+
pluginErrorDetails(curdx)
|
|
73
|
+
);
|
|
51
74
|
}
|
|
52
75
|
} else {
|
|
53
76
|
pushLine(lines, "warn", "curdx-flow not installed → run curdx-flow install");
|
|
@@ -67,7 +90,12 @@ export function buildDoctorReport({
|
|
|
67
90
|
if (plugin && plugin.status === "enabled") {
|
|
68
91
|
pushSectionLine(requiredSection, "ok", `${entry.name.padEnd(22)} v${plugin.version || "unknown"}`);
|
|
69
92
|
} else if (plugin && plugin.status === "failed") {
|
|
70
|
-
pushSectionLine(
|
|
93
|
+
pushSectionLine(
|
|
94
|
+
requiredSection,
|
|
95
|
+
"err",
|
|
96
|
+
`${entry.name.padEnd(22)} load failed`,
|
|
97
|
+
pluginErrorDetails(plugin)
|
|
98
|
+
);
|
|
71
99
|
} else {
|
|
72
100
|
pushSectionLine(
|
|
73
101
|
requiredSection,
|
|
@@ -120,7 +148,12 @@ export function buildDoctorReport({
|
|
|
120
148
|
pushSectionLine(recommendedSection, "ok", `${entry.name.padEnd(22)} v${plugin.version}`);
|
|
121
149
|
if (entry.postInstall === "claude-mem-runtimes") claudeMemEnabled = true;
|
|
122
150
|
} else if (plugin && plugin.status === "failed") {
|
|
123
|
-
pushSectionLine(
|
|
151
|
+
pushSectionLine(
|
|
152
|
+
recommendedSection,
|
|
153
|
+
"err",
|
|
154
|
+
`${entry.name.padEnd(22)} load failed`,
|
|
155
|
+
pluginErrorDetails(plugin)
|
|
156
|
+
);
|
|
124
157
|
} else {
|
|
125
158
|
pushSectionLine(
|
|
126
159
|
recommendedSection,
|
|
@@ -133,16 +166,22 @@ export function buildDoctorReport({
|
|
|
133
166
|
|
|
134
167
|
const duplicates = findDuplicateMcps(mcps, userMcpConfig);
|
|
135
168
|
if (duplicates.length > 0) {
|
|
136
|
-
const duplicateSection = createSection("
|
|
169
|
+
const duplicateSection = createSection("Duplicate MCP registrations:");
|
|
137
170
|
for (const duplicate of duplicates) {
|
|
171
|
+
const details = duplicate.pluginEntry.plugin === "curdx-flow"
|
|
172
|
+
? [
|
|
173
|
+
"migration: claude plugin update curdx-flow@curdx-flow-marketplace",
|
|
174
|
+
"then restart Claude Code",
|
|
175
|
+
]
|
|
176
|
+
: [
|
|
177
|
+
`remove the duplicate user-level server if plugin:${duplicate.pluginEntry.plugin} should own it`,
|
|
178
|
+
`run: claude mcp remove --scope user ${duplicate.name}`,
|
|
179
|
+
];
|
|
138
180
|
pushSectionLine(
|
|
139
181
|
duplicateSection,
|
|
140
182
|
"warn",
|
|
141
183
|
`${duplicate.name.padEnd(22)} both user-level AND plugin:${duplicate.pluginEntry.plugin} active`,
|
|
142
|
-
|
|
143
|
-
"migration: claude plugin update curdx-flow@curdx-flow-marketplace",
|
|
144
|
-
"then restart Claude Code",
|
|
145
|
-
]
|
|
184
|
+
details
|
|
146
185
|
);
|
|
147
186
|
}
|
|
148
187
|
}
|