@curdx/flow 2.0.0-beta.8 → 2.0.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.
@@ -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.8"
9
+ "version": "2.0.0-beta.18"
10
10
  },
11
11
  "plugins": [
12
12
  {
@@ -1,13 +1,13 @@
1
1
  {
2
2
  "name": "curdx-flow",
3
- "version": "2.0.0-beta.8",
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",
7
7
  "email": "bydongxin@gmail.com"
8
8
  },
9
- "homepage": "https://github.com/wdx/curdx-flow",
10
- "repository": "https://github.com/wdx/curdx-flow",
9
+ "homepage": "https://github.com/curdx/curdx-flow",
10
+ "repository": "https://github.com/curdx/curdx-flow",
11
11
  "license": "MIT",
12
12
  "keywords": [
13
13
  "workflow",
@@ -16,21 +16,5 @@
16
16
  "orchestration",
17
17
  "karpathy",
18
18
  "claude-code"
19
- ],
20
- "mcpServers": {
21
- "context7": {
22
- "command": "npx",
23
- "args": [
24
- "-y",
25
- "@upstash/context7-mcp@latest"
26
- ]
27
- },
28
- "sequential-thinking": {
29
- "command": "npx",
30
- "args": [
31
- "-y",
32
- "@modelcontextprotocol/server-sequential-thinking"
33
- ]
34
- }
35
- }
19
+ ]
36
20
  }
package/CHANGELOG.md CHANGED
@@ -2,6 +2,107 @@
2
2
 
3
3
  All notable changes to CurDX-Flow will be documented here.
4
4
 
5
+ ## [Unreleased]
6
+
7
+ ### Fixed
8
+
9
+ - `hooks/hooks.json` + `hooks/scripts/inject-karpathy.sh` — migrated the L1 baseline injector from the `InstructionsLoaded` event to `SessionStart` with `matcher: "startup|clear|compact"`. Per the Claude Code hooks docs (code.claude.com/docs/en/hooks), `InstructionsLoaded` is an observability-only event: its hook schema has no `hookSpecificOutput` field, so the injector's `{"hookSpecificOutput":{"hookEventName":"InstructionsLoaded","additionalContext":…}}` payload was rejected at session boot with `Hook JSON output validation failed — (root): Invalid input`, producing the `SessionStart:startup hook error` banner on every Claude Code launch. The `startup|clear|compact` matcher preserves the original "baseline survives compaction" intent. Also updated `docs/architecture.md` and `README.zh.md` hook-event inventories.
10
+
11
+ ### BREAKING
12
+
13
+ - **`context7` and `sequential-thinking` moved from plugin-bundled MCPs to user-level MCPs.** Previously `.claude-plugin/plugin.json` declared both in `mcpServers`, so Claude Code auto-registered them as `plugin:curdx-flow:context7` and `plugin:curdx-flow:sequential-thinking` when the plugin installed. This is no longer the case — the `mcpServers` block is gone. Instead, `curdx-flow install` now runs `claude mcp add context7 …` + `claude mcp add sequential-thinking …` against the user's `~/.claude.json`, which keeps them as standard user-level registrations named `context7` and `sequential-thinking`.
14
+
15
+ **Why**: every early adopter who ran `claude mcp add context7 -- npx -y @upstash/context7-mcp --api-key ctx7sk-…` before installing curdx-flow ended up with *both* entries side-by-side — doubling MCP processes, making API-key routing unpredictable (random between free-tier plugin copy and paid user copy), and forcing an awkward env-var migration path. User-level registration matches the way every other MCP in the ecosystem is installed, keeps tool names standard (`mcp__context7__*` instead of `mcp__plugin_curdx-flow_context7__*`), and lets users put an `--api-key` directly in their own entry without any env-var indirection. Every agent / knowledge doc / command in the repo already calls the standard tool-name form, so this is a **zero-change-for-callers** migration.
16
+
17
+ **Migration for existing users**:
18
+
19
+ ```bash
20
+ # Upgrade the plugin — this removes the plugin-bundled MCPs and
21
+ # registers them at user-level (preserving any existing user-level entry
22
+ # with a custom --api-key).
23
+ npx @curdx/flow@latest upgrade
24
+
25
+ # Restart Claude Code so the plugin manifest reloads.
26
+ ```
27
+
28
+ After upgrade, `claude mcp list` will show `context7` and
29
+ `sequential-thinking` (user-level) but NOT `plugin:curdx-flow:context7` etc.
30
+ `npx @curdx/flow doctor` confirms with "user-level (standard)" in the
31
+ "MCP Servers" section.
32
+
33
+ ### Added
34
+
35
+ - `cli/registry.js` exports a new `BUNDLED_MCPS` constant — the source-of-truth list of MCPs that `install` registers at user-level and `uninstall` optionally removes.
36
+ - `cli/install.js` Step 3.5 — registers the required MCPs via `claude mcp add`. Detects pre-existing user-level entries (e.g. your hand-configured context7 with an API key) and preserves them instead of overwriting.
37
+ - `cli/uninstall.js` Step 4.5 — asks whether to `claude mcp remove` the registered MCPs (default: no, because other tools may depend on them).
38
+ - `cli/doctor.js` — now reports "user-level (standard)" for each required MCP when it's registered the right way, and "(legacy)" with a migration hint when the old plugin-bundled form is still active.
39
+
40
+ ### Changed
41
+
42
+ - The "Duplicate MCP" warning in `doctor` (added in beta.11) is now framed as "Legacy plugin-bundled MCPs still present" with a concrete migration command, since the new architecture reserves duplication strictly for the upgrade-transition window.
43
+ - `docs/getting-started.md` "Optional: use your paid context7 API key" section rewritten around the user-level path (add `--api-key` to your `claude mcp add` command) since env-var indirection is no longer the only way to route a key to the plugin copy.
44
+
45
+ ## [2.0.0-beta.11] - 2026-04-22
46
+
47
+ ### Added
48
+
49
+ - `cli/doctor.js` detects when a user-level MCP in `~/.claude.json` (typically `context7` added via `claude mcp add …`) duplicates a plugin-bundled MCP from `plugin.json` (`plugin:curdx-flow:context7`). Backed by `cli/utils.js:readUserMcpConfig()` + `findDuplicateMcps()` and 3 new tests in `test/utils.test.js`.
50
+ - `docs/getting-started.md` "Optional: use your paid context7 API key" section documenting the env-var path.
51
+
52
+ ## [2.0.0-beta.10] - 2026-04-21
53
+
54
+ ### Fixed
55
+
56
+ - `cli/utils.js` — `listMcps` rewritten to handle the actual `claude mcp list` format captured from claude 2.1.117. The previous regex matched only a single kebab-case token before the first colon, so `plugin:curdx-flow:context7: npx ...` parsed as name=`plugin` and silently dropped the rest. New parser exports `{ name, plugin, fullName, status, command }` and is fixture-tested in `test/utils.test.js`.
57
+ - `cli/doctor.js` — MCP presence check now accepts both standalone (`context7`) and plugin-bundled (`plugin:curdx-flow:context7`) registrations, with source in the status line. Fixes the same class of false-positive that bit `chrome-devtools` in beta.7. Also gated `--verbose` raw-plugin dump behind `claude` being on PATH, so it no longer runs a meaningless empty child when the CLI is absent.
58
+ - `cli/install.js` — step counter was `1/4`..`4/4` but there are five steps; `Step 5: inject global protocols` was emitted via bare `console.log` so the progress display lied. All five steps now use `log.step(N, 5, ...)`. Also switched `⏳ Installing` and `✅ Install complete` glyphs to the `▸` / `✓` symbols used elsewhere in the project's `log.*` helpers.
59
+ - `cli/uninstall.js` — same `⏳` → `▸` glyph normalisation.
60
+ - `cli/protocols.js` + `cli/utils.js` + `cli/uninstall.js` — switched from `process.env.HOME || ""` to `os.homedir()`. `$HOME` is only guaranteed in interactive login shells; CI containers, some `docker exec` invocations, and non-login spawns can leave it empty, which resolved `GLOBAL_CLAUDE_MD` to `/.claude/CLAUDE.md` and broke `ensureRuntimeInPath` runtime candidate lookup.
61
+ - `bin/curdx-flow.js` — `main()` was called unconditionally at module scope, so `import()` from a test would spawn the CLI, parse the test runner's argv, and `exit(1)`. Now guarded by the ESM direct-invocation idiom (`import.meta.url === pathToFileURL(process.argv[1]).href`). Added one real-import test.
62
+ - `.claude-plugin/plugin.json` — `homepage` and `repository` pointed at `github.com/wdx/curdx-flow` but the authoritative remote is `github.com/curdx/curdx-flow`. Plugin-marketplace UI clicks previously 404'd. Aligned to the real owner.
63
+ - `.github/workflows/npm-publish.yml` — bumped `actions/setup-node` from Node 20 to Node 22 LTS ahead of the 2026-06-02 Node 20 removal.
64
+ - `scripts/release.sh` — lockstep bump of `plugin.json` + `marketplace.json` now parses both files in memory before writing either, with `git checkout -- package.json` auto-revert on failure. Previously the second file could fail to write while the first was already disk-committed.
65
+
66
+ ### Changed
67
+
68
+ - `commands/start.md` — replaced the brittle `xargs`/`awk`/`sed` flag-parsing block with a model-directed parse that tolerates quoted strings in `$ARGUMENTS` (e.g. `my-feature "Fix user's login bug"` no longer hits `xargs: unmatched quote`).
69
+ - `commands/init.md` — Step 5 used to say "Run `npx @curdx/flow doctor`" but the slash command runs inside Claude Code where a spawned CLI's output doesn't render cleanly. Reframed as "verify inline + suggest the user run the CLI in a separate terminal if they want the full report."
70
+ - `commands/implement.md` — replaced `❌` emoji with `✗` to match the project's own `log.err` helper.
71
+ - `docs/architecture.md` — Hook System section and CLI layout were **entirely fabricated** (listed non-existent hooks `pre-spec.sh` / `post-implement.sh` / `pre-verify.sh` / `post-review.sh` / `on-gate-fail.sh` and bogus paths `cli/bin/flow` / `cli/commands/install.js` / `cli/lib/…`). Rewritten to match the real `hooks/scripts/` layout (4 hooks: session-start / inject-karpathy / quick-mode-guard / stop-watcher) and real `bin/` + `cli/` paths including `cli/registry.js` and the `test/` directory.
72
+
73
+ ### Added
74
+
75
+ - `test/utils.test.js` grew 5 fixture tests for the new `parseMcpList` against a captured `claude mcp list` output.
76
+ - `test/cli-entrypoints.test.js` — 5 smoke tests that each dynamically-import `cli/doctor.js` / `cli/install.js` / `cli/upgrade.js` / `cli/uninstall.js` and assert the expected top-level function exports. Guards against the "deleted an import but forgot the consumer" class of regression at CI time. Plus one test that imports `bin/curdx-flow.js` now that it no longer runs `main()` at module load.
77
+
78
+ ### Removed (docs compliance)
79
+
80
+ - Chinese trigger phrases from `skills/*/SKILL.md` descriptions (5 files). Keeping bilingual triggers violated the project's own language-separation rule (`CLAUDE.md`: "Documentation layer = English"). Semantic matching on the English phrases plus the model's own bilingual understanding covers the same invocation surface.
81
+
82
+ ## [2.0.0-beta.9] - 2026-04-21
83
+
84
+ ### Fixed (P0)
85
+
86
+ - `cli/utils.js` — `findRuntime()` referenced `existsSync` without importing it; any user whose `bun`/`uv` was not on PATH hit `ReferenceError` during install or doctor.
87
+ - `hooks/scripts/stop-watcher.sh` — `export STATE_FILE` was placed after the python heredoc, so the stop-hook execution strategy never activated. Moved the export before the heredoc.
88
+ - `package.json` — `skills/` directory was missing from `files[]`; the 5 bundled skills were stripped from the published tarball.
89
+ - `cli/uninstall.js` + `cli/upgrade.js` — `chrome-devtools-mcp` (added as a recommended plugin in beta.8) was missing from uninstall/upgrade lists, making it installable but uninstallable.
90
+ - `cli/protocols.js` — `injectGlobalProtocols()` returned action `"created"` on both ternary branches, silently collapsing the append-to-existing-file case. Atomic write + corrupted-block detection added.
91
+
92
+ ### Changed (structural)
93
+
94
+ - New `cli/registry.js` is the single source of truth for recommended plugins. `install.js`, `uninstall.js`, `upgrade.js`, and `doctor.js` all import from it.
95
+ - `commands/start.md` and `commands/spec.md` now produce `.state.json` files that match `schemas/spec-state.schema.json` (field names: `spec_name` / `created` / `updated` / `version`; initial phase is `research`, not the undefined `created`).
96
+ - All python heredocs inside hook scripts use quoted delimiters (`<<'PY'`) and read `STATE_FILE` via `os.environ`, closing a shell→python code-injection surface triggered by unusual spec names.
97
+
98
+ ### Added
99
+
100
+ - First real test suite — `node --test`-based, 18 regression tests covering `cli/protocols.js`, `cli/registry.js`, and `cli/utils.js`. Wired into `package.json` `scripts.test` and the CI `publish` job so a failing test blocks `npm publish`.
101
+
102
+ ### Removed
103
+
104
+ - `hooks/scripts/fail-tracker.sh` and its `PostToolUseFailure` registration — the counter was written but never read by any consumer (the intended pua escalation was never implemented). Can be reintroduced when the consumer exists.
105
+
5
106
  ## [2.0.0-beta.1] - 2026-04-20
6
107
 
7
108
  ### BREAKING — Major redesign: Discipline Layer, not meta-framework
package/README.md CHANGED
@@ -28,7 +28,9 @@ 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 3 auto-registered MCP servers, and three recommended companion plugins (pua, claude-mem, frontend-design).
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
+
33
+ **Note:** If you're running this command inside the `curdx-flow` project directory itself, use `npx --ignore-existing @curdx/flow install --all` to avoid npx trying to use the local package without dependencies installed.
32
34
 
33
35
  ## 9 slash commands, that's it
34
36
 
@@ -97,7 +99,8 @@ npm i -g @curdx/flow@^1.1
97
99
  - **8 composable gates** — Karpathy / Verification / TDD / Coverage / Adversarial / Edge-Case / Security / DevEx
98
100
  - **4 execution strategies** for `/curdx-flow:implement` (linear / subagent / stop-hook / wave, auto-routed)
99
101
  - **5 hook events** that enforce the discipline without user action
100
- - **3 auto-installed MCPs** — context7 (latest docs), sequential-thinking (structured reasoning), chrome-devtools (browser automation)
102
+ - **Required docs/reasoning tools** — Context7 official plugin (MCP + skill + docs agent) and sequential-thinking MCP
103
+ - **4 recommended companion plugins** — pua, claude-mem, frontend-design, chrome-devtools-mcp
101
104
  - **Offline-capable install** — the npm package ships the full plugin body; zero GitHub round-trips during install
102
105
 
103
106
  ## Documentation
@@ -123,7 +126,7 @@ CurDX-Flow v2 is a distillation, not an invention. Ideas we depend on:
123
126
  - [**superpowers**](https://github.com/obra/superpowers) — subagent discipline + TDD + two-stage review
124
127
  - [**pua**](https://github.com/tanweai/pua) — the three red lines
125
128
  - [**claude-mem**](https://github.com/thedotmack/claude-mem) — cross-session memory
126
- - **Anthropic official** — context7 (Upstash), sequential-thinking, chrome-devtools-mcp, frontend-design skill
129
+ - **MCP / plugin ecosystem** — context7 (Upstash), sequential-thinking, chrome-devtools-mcp (Chrome DevTools team), frontend-design skill
127
130
 
128
131
  ## License
129
132
 
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
- [![Version](https://img.shields.io/badge/version-1.1.0-blue)](./CHANGELOG.md)
6
+ [![Version](https://img.shields.io/npm/v/@curdx/flow.svg)](https://www.npmjs.com/package/@curdx/flow)
7
7
  [![License](https://img.shields.io/badge/license-MIT-green)](./LICENSE)
8
8
  [![Plugin](https://img.shields.io/badge/claude--code-plugin-purple)](https://code.claude.com)
9
9
  [![English](https://img.shields.io/badge/doc-English-red)](./README.md)
@@ -16,15 +16,15 @@ CurDX-Flow 是一个 Claude Code 插件,把 6 个验证过的 AI 工程工作
16
16
 
17
17
  **不重造轮子。编排好轮子。**
18
18
 
19
- ## 一览(v1.1.0
19
+ ## 一览(v2
20
20
 
21
- - **31 个命令** — 初始化 / 研究 / 设计 / 执行 / 验证 / 审查 / 调试 / QA / 安全 / ...
22
- - **24 个代理**15 职能 + 9 人格(Mary / John / Winston / Amelia / Rachel / David / Oliver / Serena / Emma)
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
- - **5 个 hook 事件** — SessionStart / InstructionsLoaded / Stop / PreToolUse / PostToolUseFailure
27
- - **3 个自动安装的 MCP**context7 / sequential-thinking / chrome-devtools
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,23 @@ CurDX-Flow 是一个 Claude Code 插件,把 6 个验证过的 AI 工程工作
41
41
 
42
42
  ## 安装
43
43
 
44
- ### 从 marketplace(发布后)
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
+ **注意:** 如果你在 `curdx-flow` 项目目录内运行此命令,请使用 `npx --ignore-existing @curdx/flow install --all` 避免 npx 尝试使用本地未安装依赖的包。
51
+
50
52
  ### 本地 dev
51
53
  ```bash
52
- git clone https://github.com/wdx/curdx-flow
54
+ git clone https://github.com/curdx/curdx-flow
53
55
  claude --plugin-dir ./curdx-flow
54
56
  ```
55
57
 
56
- 3 MCP 自动装。然后运行:
58
+ 安装后在 Claude Code 内运行:
57
59
 
58
60
  ```
59
- /curdx-flow:install-deps # 交互式安装 pua / claude-mem / frontend-design
60
- /curdx-flow:doctor # 验证健康
61
61
  /curdx-flow:init # 初始化你的项目
62
62
  ```
63
63
 
@@ -102,8 +102,8 @@ claude --plugin-dir ./curdx-flow
102
102
  | 文档 | 何时读 |
103
103
  |-----|------|
104
104
  | [`docs/getting-started.md`](./docs/getting-started.md) | 首次使用,5 分钟上手 |
105
- | [`docs/command-reference.md`](./docs/command-reference.md) | 31 个命令完整参考 |
106
- | [`docs/agent-reference.md`](./docs/agent-reference.md) | 24 个代理 + 人格 |
105
+ | [`docs/command-reference.md`](./docs/command-reference.md) | 9 个命令完整参考 |
106
+ | [`docs/agent-reference.md`](./docs/agent-reference.md) | 15 个内部代理 |
107
107
  | [`docs/workflows.md`](./docs/workflows.md) | 5 种典型场景(greenfield/brownfield/epic/fast/UI) |
108
108
  | [`docs/architecture.md`](./docs/architecture.md) | 内部设计(给扩展者) |
109
109
  | [`docs/ethos.md`](./docs/ethos.md) | 设计决策的理由 |
@@ -132,7 +132,7 @@ CurDX-Flow 是蒸馏,不是原创。深深致谢:
132
132
  - [**pua**](https://github.com/tanweai/pua) — 持续性 + 三条红线
133
133
  - [**claude-mem**](https://github.com/thedotmack/claude-mem) — 自动跨会话记忆
134
134
  - [**frontend-design skill**](https://claude.ai/skills/frontend-design) — distinctive UI(Anthropic 官方)
135
- - **context7**(Upstash)+ **sequential-thinking**(Anthropic)+ **chrome-devtools-mcp**(Chrome 团队)
135
+ - **Context7**(Upstash)+ **sequential-thinking**(Anthropic)+ **chrome-devtools-mcp**(Chrome 团队)
136
136
 
137
137
  ## 许可
138
138
 
@@ -140,7 +140,7 @@ MIT。见 [`LICENSE`](./LICENSE)。
140
140
 
141
141
  ## 贡献
142
142
 
143
- - Issues: https://github.com/wdx/curdx-flow/issues
143
+ - Issues: https://github.com/curdx/curdx-flow/issues
144
144
  - 欢迎 PR,但请先用 `/curdx-flow:spec` 自己走一遍(吃自己的狗粮)
145
145
  - 社区准则:友善 + 具体 + 不对着 LLM 发火
146
146
 
package/bin/curdx-flow.js CHANGED
@@ -20,6 +20,9 @@
20
20
  * for the full command/workflow reference)
21
21
  */
22
22
 
23
+ import { fileURLToPath } from "node:url";
24
+ import { realpathSync } from "node:fs";
25
+
23
26
  import { install } from "../cli/install.js";
24
27
  import { doctor } from "../cli/doctor.js";
25
28
  import { upgrade } from "../cli/upgrade.js";
@@ -128,4 +131,30 @@ async function main() {
128
131
  }
129
132
  }
130
133
 
131
- main();
134
+ // Only execute main() when invoked directly (`node bin/curdx-flow.js ...`
135
+ // or via the npm bin shim at node_modules/.bin/<name>). When the file is
136
+ // imported by tests or tooling, we want the module graph to load without
137
+ // side-effects.
138
+ //
139
+ // CRITICAL: compare RESOLVED real paths. npm installs the bin as a symlink
140
+ // (node_modules/.bin/curdx-flow → ../@curdx/flow/bin/curdx-flow.js), so
141
+ // process.argv[1] is the symlink path while import.meta.url resolves to
142
+ // the real file. Comparing them directly (the pre-beta.13 behavior)
143
+ // silently skipped main() for every single npx / global-install user,
144
+ // producing a completely broken CLI that exited with no output. Regression
145
+ // caught by user report + reproduced in CI via test/cli-entrypoints.test.js.
146
+ function isInvokedDirectly() {
147
+ if (!process.argv[1]) return false;
148
+ try {
149
+ return realpathSync(process.argv[1]) === fileURLToPath(import.meta.url);
150
+ } catch {
151
+ // argv[1] is not a real filesystem path (e.g. `node -e` eval forms or
152
+ // a worker pipe). Treat as "not invoked directly" — the caller is
153
+ // doing something non-standard.
154
+ return false;
155
+ }
156
+ }
157
+
158
+ if (isInvokedDirectly()) {
159
+ main();
160
+ }
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` (auto-installs 3 MCPs)
29
- 4. Interactively (or automatically) install recommended plugins: **pua**, **claude-mem**, **frontend-design**
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 / 3 MCPs / 3 recommended plugins / current directory `.flow/` state.
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` and `~/.local/bin/uv` symlinks created by install.
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,9 +10,13 @@ import {
10
10
  runSync,
11
11
  claudeVersion,
12
12
  listPlugins,
13
+ listPluginMarketplaces,
13
14
  listMcps,
14
15
  ensureClaudeMemRuntimes,
16
+ readUserMcpConfig,
17
+ findDuplicateMcps,
15
18
  } from "./utils.js";
19
+ import { BUNDLED_MCPS, REQUIRED_PLUGINS, RECOMMENDED_PLUGINS } from "./registry.js";
16
20
 
17
21
  export async function doctor(args = []) {
18
22
  const verbose = args.includes("--verbose") || args.includes("-v");
@@ -49,21 +53,58 @@ export async function doctor(args = []) {
49
53
  warnings++;
50
54
  }
51
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
+
52
82
  // ---------- MCPs ----------
53
- // Bundled by curdx-flow plugin via .claude-plugin/plugin.json mcpServers.
54
- // chrome-devtools is NOT here anymore — it was extracted into its own
55
- // recommended plugin (see below) to align with the "each MCP owned by one
56
- // plugin" model and avoid double-spawning the chrome-devtools-mcp process.
57
- console.log(`\n${color.bold("MCP Servers:")}`);
83
+ console.log(`\n${color.bold("MCP Servers (required by L2 mandatory tools):")}`);
58
84
  const mcps = cv ? listMcps() : [];
59
- const expectedMcps = ["context7", "sequential-thinking"];
60
- for (const m of expectedMcps) {
61
- const found = mcps.find((x) => x.name === m);
62
- if (found) {
63
- log.ok(`${m.padEnd(22)} ${color.dim("auto-loaded")}`);
85
+ for (const expected of BUNDLED_MCPS) {
86
+ const m = expected.name;
87
+ // Beta.12 onward: the required MCPs are registered at user-level
88
+ // (via `claude mcp add`), NOT plugin-bundled. Both still show up in
89
+ // `claude mcp list`. Accept a standalone user-level registration
90
+ // as the primary form, and warn if only a plugin-bundled copy exists
91
+ // (because that's the legacy layout that beta.11 had).
92
+ const userLevel = mcps.find((x) => x.name === m && x.plugin === null);
93
+ const pluginLevel = mcps.find((x) => x.name === m && x.plugin !== null);
94
+
95
+ if (userLevel) {
96
+ log.ok(`${m.padEnd(22)} ${color.dim("user-level (standard)")}`);
97
+ } else if (pluginLevel) {
98
+ log.warn(
99
+ `${m.padEnd(22)} registered via plugin:${pluginLevel.plugin} (legacy). ` +
100
+ `Run ${color.cyan("npx @curdx/flow install --all")} to migrate to user-level.`
101
+ );
102
+ warnings++;
64
103
  } else {
65
104
  if (curdx) {
66
- log.warn(`${m.padEnd(22)} not shown in claude mcp list (restart Claude Code may fix)`);
105
+ log.warn(
106
+ `${m.padEnd(22)} missing. Run: ${color.cyan(`claude mcp add --scope user ${m} -- ${expected.command} ${expected.args.join(" ")}`)}`
107
+ );
67
108
  warnings++;
68
109
  } else {
69
110
  log.info(`${m.padEnd(22)} waiting for curdx-flow install`);
@@ -71,29 +112,58 @@ export async function doctor(args = []) {
71
112
  }
72
113
  }
73
114
 
74
- // ---------- Recommended plugins ----------
115
+ // ---------- Recommended plugins (single registry; see cli/registry.js) ----------
75
116
  console.log(`\n${color.bold("Recommended plugins:")}`);
76
- const recommended = [
77
- { name: "pua", installCmd: "claude plugin install pua@pua-skills" },
78
- { name: "claude-mem", installCmd: "claude plugin install claude-mem@thedotmack" },
79
- { name: "frontend-design", installCmd: "claude plugin install frontend-design@claude-plugins-official" },
80
- { name: "chrome-devtools-mcp", installCmd: "claude plugin install chrome-devtools-mcp@chrome-devtools-plugins" },
81
- ];
82
117
  let claudeMemEnabled = false;
83
- for (const r of recommended) {
84
- const p = plugins.find((x) => x.name === r.name);
118
+ for (const r of RECOMMENDED_PLUGINS) {
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
+ }
85
126
  if (p && p.status === "enabled") {
86
127
  log.ok(`${r.name.padEnd(22)} ${color.dim(`v${p.version}`)}`);
87
- if (r.name === "claude-mem") claudeMemEnabled = true;
128
+ if (r.postInstall === "claude-mem-runtimes") claudeMemEnabled = true;
88
129
  } else if (p && p.status === "failed") {
89
130
  log.err(`${r.name.padEnd(22)} load failed`);
90
131
  errors++;
91
132
  } else {
92
- log.warn(`${r.name.padEnd(22)} not installed ${color.dim("(run: curdx-flow install --all)")}`);
133
+ log.warn(
134
+ `${r.name.padEnd(22)} not installed ${color.dim(`(run: claude plugin install --scope ${r.scope} ${r.installSpec})`)}`
135
+ );
93
136
  warnings++;
94
137
  }
95
138
  }
96
139
 
140
+ // ---------- Legacy plugin-bundled MCP residue (beta.11 and earlier) ----------
141
+ // Beta.12 moved context7 + sequential-thinking to user-level registration.
142
+ // If both a user-level and a plugin-bundled copy are visible, the user
143
+ // was likely installed from an older beta.7-beta.11 that still had
144
+ // mcpServers in plugin.json and a `claude mcp update` hasn't refreshed
145
+ // the plugin cache yet. Point them at the migration command.
146
+ if (cv) {
147
+ const userCfg = readUserMcpConfig();
148
+ const duplicates = findDuplicateMcps(mcps, userCfg);
149
+ if (duplicates.length > 0) {
150
+ console.log(`\n${color.bold("Legacy plugin-bundled MCPs still present:")}`);
151
+ for (const d of duplicates) {
152
+ log.warn(
153
+ `${d.name.padEnd(22)} both user-level AND plugin:${d.pluginEntry.plugin} active`
154
+ );
155
+ console.log(
156
+ color.dim(
157
+ ` → Migration: ${color.cyan(`claude plugin update curdx-flow@curdx-flow-marketplace`)}\n` +
158
+ ` then restart Claude Code. Beta.12+ removes plugin-bundled\n` +
159
+ ` versions in favor of the user-level entry you already have.`
160
+ )
161
+ );
162
+ warnings++;
163
+ }
164
+ }
165
+ }
166
+
97
167
  // ---------- Runtime PATH guards (only if claude-mem is installed) ----------
98
168
  if (claudeMemEnabled) {
99
169
  console.log(`\n${color.bold("Runtime (claude-mem dependencies):")}`);
@@ -152,7 +222,9 @@ export async function doctor(args = []) {
152
222
  console.log(color.green("Summary: all healthy ✓"));
153
223
  }
154
224
 
155
- if (verbose) {
225
+ if (verbose && cv) {
226
+ // Only call claude when it is actually on PATH; otherwise we spawn a
227
+ // child that fails silently and print a blank block.
156
228
  console.log(`\n${color.bold("Details:")}`);
157
229
  console.log(color.dim(` Plugins raw:`));
158
230
  console.log(runSync("claude", ["plugin", "list"]).stdout);