@curdx/flow 2.0.0-beta.14 → 2.0.0-beta.16

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.14"
9
+ "version": "2.0.0-beta.16"
10
10
  },
11
11
  "plugins": [
12
12
  {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "curdx-flow",
3
- "version": "2.0.0-beta.14",
3
+ "version": "2.0.0-beta.16",
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/CHANGELOG.md CHANGED
@@ -4,6 +4,10 @@ All notable changes to CurDX-Flow will be documented here.
4
4
 
5
5
  ## [Unreleased]
6
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
+
7
11
  ### BREAKING
8
12
 
9
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`.
package/README.zh.md CHANGED
@@ -23,7 +23,7 @@ CurDX-Flow 是一个 Claude Code 插件,把 6 个验证过的 AI 工程工作
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
- - **4 个 hook 事件** — SessionStart / InstructionsLoaded / Stop / PreToolUse
26
+ - **3 个 hook 事件** — SessionStart / Stop / PreToolUse
27
27
  - **2 个自动安装的 MCP + 1 个推荐插件** — context7 / sequential-thinking(plugin.json 内置)+ chrome-devtools-mcp(recommended,beta.8 解耦)
28
28
  - **优雅降级** — 依赖缺失时进入 fallback 模式并清晰告知
29
29
 
package/cli/install.js CHANGED
@@ -58,6 +58,15 @@ export async function install(args = []) {
58
58
  }
59
59
  log.ok(`claude CLI found (${ver})`);
60
60
 
61
+ // Snapshot curdx-flow's pre-install version BEFORE Step 2 touches the
62
+ // marketplace. Step 2 does `claude plugin marketplace remove` + `add` to
63
+ // rebind to the current source, and the remove side-effect also drops
64
+ // any plugins installed from that marketplace. If we only call
65
+ // listPlugins() in Step 3, curdx-flow is already gone from the list and
66
+ // we can't tell a fresh install apart from an upgrade — the Step 3
67
+ // output then incorrectly says "installed" for both cases.
68
+ const prevCurdxFlow = listPlugins().find((p) => p.name === "curdx-flow");
69
+
61
70
  // ---------- Step 2: Add marketplace ----------
62
71
  log.blank();
63
72
  const marketplaceSource = useOffline ? PKG_ROOT : "curdx/curdx-flow";
@@ -105,25 +114,51 @@ export async function install(args = []) {
105
114
  // marketplace not local (online install) or unreadable — fall through
106
115
  }
107
116
 
108
- const installed = listPlugins();
109
- const already = installed.find((p) => p.name === "curdx-flow");
110
- if (already && shippedVersion && already.version !== shippedVersion) {
117
+ // Use the pre-Step-2 snapshot — by this point `claude plugin marketplace
118
+ // remove` has already evicted the plugin, so listPlugins() here would
119
+ // always return undefined for curdx-flow and we'd mis-report "installed"
120
+ // when we actually upgraded (the bug reported by @wdx's beta.14 log).
121
+ const already = prevCurdxFlow;
122
+
123
+ if (already && shippedVersion && already.version === shippedVersion) {
124
+ // Already on the exact version the tarball ships — no re-install needed.
125
+ // `claude plugin install` would succeed too but the fresh-install output
126
+ // is misleading here.
127
+ log.ok(
128
+ `curdx-flow already at v${already.version} ${color.dim("(no change)")}`
129
+ );
130
+ } else if (already && shippedVersion) {
131
+ // Existing install, different version — frame as an upgrade.
111
132
  log.info(
112
- `curdx-flow installed at v${already.version}, marketplace ships v${shippedVersion} — updating...`
133
+ `curdx-flow v${already.version} v${shippedVersion}, installing...`
113
134
  );
114
135
  const r = await run(
115
136
  "claude",
116
- ["plugin", "update", "curdx-flow@curdx-flow-marketplace"],
137
+ ["plugin", "install", "curdx-flow@curdx-flow-marketplace"],
117
138
  { silent: true }
118
139
  );
119
140
  if (r.code !== 0) {
120
- log.warn(`Update returned non-zero: ${r.stderr.trim() || r.stdout.trim()}`);
121
- log.info(`If the version stays on v${already.version}, run: claude plugin uninstall curdx-flow@curdx-flow-marketplace && retry`);
122
- } else {
123
- log.ok(`curdx-flow updated to v${shippedVersion}`);
141
+ log.err(`Install failed: ${r.stderr.trim() || r.stdout.trim()}`);
142
+ process.exit(1);
124
143
  }
144
+ log.ok(`curdx-flow upgraded to v${shippedVersion}`);
125
145
  } else if (already) {
126
- log.ok(`curdx-flow already installed (v${already.version}, ${already.status})`);
146
+ // shippedVersion unknown (e.g. online install) — best we can do is report
147
+ // the previous version and let `claude plugin install` idempotently
148
+ // re-register.
149
+ log.info(
150
+ `curdx-flow v${already.version} detected, re-registering...`
151
+ );
152
+ const r = await run(
153
+ "claude",
154
+ ["plugin", "install", "curdx-flow@curdx-flow-marketplace"],
155
+ { silent: true }
156
+ );
157
+ if (r.code !== 0) {
158
+ log.err(`Install failed: ${r.stderr.trim() || r.stdout.trim()}`);
159
+ process.exit(1);
160
+ }
161
+ log.ok(`curdx-flow re-registered`);
127
162
  } else {
128
163
  const r = await run(
129
164
  "claude",
@@ -134,7 +169,11 @@ export async function install(args = []) {
134
169
  log.err(`Install failed: ${r.stderr.trim() || r.stdout.trim()}`);
135
170
  process.exit(1);
136
171
  }
137
- log.ok("curdx-flow installed");
172
+ if (shippedVersion) {
173
+ log.ok(`curdx-flow v${shippedVersion} installed`);
174
+ } else {
175
+ log.ok("curdx-flow installed");
176
+ }
138
177
  }
139
178
 
140
179
  // ---------- Step 3.5: Register user-level MCPs (context7, sequential-thinking) ----------
package/hooks/hooks.json CHANGED
@@ -8,10 +8,9 @@
8
8
  "command": "${CLAUDE_PLUGIN_ROOT}/hooks/scripts/session-start.sh"
9
9
  }
10
10
  ]
11
- }
12
- ],
13
- "InstructionsLoaded": [
11
+ },
14
12
  {
13
+ "matcher": "startup|clear|compact",
15
14
  "hooks": [
16
15
  {
17
16
  "type": "command",
@@ -1,10 +1,13 @@
1
1
  #!/usr/bin/env bash
2
- # CurDX-Flow InstructionsLoaded Hook
2
+ # CurDX-Flow SessionStart baseline injection
3
3
  # Injects the L1 baseline (Karpathy 4 principles + mandatory tool rules + 3 red lines)
4
- # into every session after CLAUDE.md is loaded.
4
+ # as additionalContext at every session boot (startup, /clear, post-compact).
5
5
  #
6
- # This is what makes the baseline "always on" — it doesn't rely on CLAUDE.md being
7
- # present in every project, and it survives context compaction.
6
+ # Wired under SessionStart rather than InstructionsLoaded because Claude Code's
7
+ # InstructionsLoaded event is observability-only its hook schema rejects
8
+ # hookSpecificOutput / additionalContext. SessionStart with matcher
9
+ # "startup|clear|compact" gives the same "baseline is always on, even after
10
+ # compaction" property while staying within the supported schema.
8
11
 
9
12
  set -u
10
13
 
@@ -46,7 +49,7 @@ CONTEXT='## CurDX-Flow Mind Baseline (L1 — always on)
46
49
  # Emit JSON with safe encoding
47
50
  if command -v python3 >/dev/null 2>&1; then
48
51
  ESCAPED="$(printf '%s' "$CONTEXT" | python3 -c 'import json,sys; print(json.dumps(sys.stdin.read()))')"
49
- printf '{"hookSpecificOutput":{"hookEventName":"InstructionsLoaded","additionalContext":%s}}\n' "$ESCAPED"
52
+ printf '{"hookSpecificOutput":{"hookEventName":"SessionStart","additionalContext":%s}}\n' "$ESCAPED"
50
53
  fi
51
54
 
52
55
  exit 0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@curdx/flow",
3
- "version": "2.0.0-beta.14",
3
+ "version": "2.0.0-beta.16",
4
4
  "description": "CLI installer for CurDX-Flow — AI engineering workflow meta-framework for Claude Code",
5
5
  "type": "module",
6
6
  "bin": {