@curdx/flow 7.1.5 → 7.1.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -2,6 +2,20 @@
2
2
 
3
3
  All notable changes to `@curdx/flow` are documented here. Format follows [Keep a Changelog](https://keepachangelog.com/) and the project follows [Semantic Versioning](https://semver.org/).
4
4
 
5
+ ## 7.1.6 — 2026-05-06
6
+
7
+ ### Changed
8
+
9
+ - **Managed-block body is now English-only; bilingual rendering removed.** `src/runner/claudeMd.ts` previously emitted a Simplified-Chinese body when the installer ran with `--lang zh` (or `CURDX_FLOW_LANG=zh`). The block is injected into `~/.claude/CLAUDE.md` and consumed by Claude as system-prompt context, not by humans, so a Chinese body actually contradicts the very `Language Policy` rule the block itself injects ("Tool and model interaction must be in English"). The body, including section headings, is now always English. The `Language Policy` section is still gated to `zh` mode — that section is what tells Claude to reply to the *user* in Simplified Chinese. Result: one source of truth for the body text, lower per-session token footprint, and one less language-drift risk between zh/en branches. The added test in `tests/runner/claudeMd.test.ts` asserts the rendered block contains no CJK characters even in `zh` mode.
10
+ - **Section headings stripped of bilingual suffixes.** Headings such as `## Tool Combination Patterns(组合工作流)` are now plain `## Tool Combination Patterns`. The parenthetical Chinese suffix added no signal for Claude and cost tokens on every session injection.
11
+
12
+ ### Fixed
13
+
14
+ - **Removed redundant `npx @curdx/flow` install/update/uninstall hint from the injected block.** The line `Run \`npx @curdx/flow\` to install / update / uninstall.` was marketing copy aimed at human readers, not guidance for Claude. It has been stripped from `renderBlock` so it no longer ends up in the system prompt.
15
+ - **`/claude-mem:mem-search` is referenced once instead of three times.** It previously appeared as the first step of *Starting a new feature*, the first step of *Debugging and repeated failures*, and the fourth item of the *Decision Tree*. It is now only referenced in the *Decision Tree*, where it belongs as a general "have we seen this before?" question. Removes ~30 tokens of duplicated guidance per session.
16
+ - **Aligned the "skip" rule for pua with the "stuck" rule.** `Skip Rules` told Claude not to reach for `/pua:pua` first, while `Tool Combination Patterns` recommended `/pua:pua-loop` only after multiple stuck attempts. The two rules are about the same tool but disagreed on its name, which read as a self-contradiction inside one block. `Skip Rules` now references `/pua:pua-loop` explicitly.
17
+ - **Fixed a Chinese-only grammar bug in the "still stuck" line.** When both `pua` and `sequential-thinking` were installed, the joined sentence rendered as `两轮以上仍卡住,再 使用 sequential-thinking MCP 拆假设,或 再进入 \`/pua:pua-loop\` 迭代。` — duplicate "再" and a stray space. The bug is fully eliminated as a side effect of moving to an English-only body; the contributing pattern (verb prefix in the template plus verb prefix in joined items) was also removed in `buildCombinationPatterns`.
18
+
5
19
  ## 7.1.5 — 2026-05-06
6
20
 
7
21
  ### Fixed
package/dist/index.mjs CHANGED
@@ -851,110 +851,61 @@ var BLOCK_RE = /<!-- BEGIN @curdx\/flow v\d+[^>]*-->[\s\S]*?<!-- END @curdx\/flo
851
851
  function claudeMdPath() {
852
852
  return path4.join(os2.homedir(), ".claude", "CLAUDE.md");
853
853
  }
854
- function isZh() {
855
- return getLang() === "zh";
856
- }
857
854
  function buildCombinationPatterns(ids) {
858
855
  const has = (k) => ids.has(k);
859
856
  const out = [
860
- isZh() ? "\u6309\u573A\u666F\u4E32\u8054\uFF0C\u4F18\u5148\u6309\u80FD\u529B\u8FB9\u754C\u8C03\u7528\uFF1Aslash command\u3001MCP\u3001\u63D2\u4EF6 skill \u4E0D\u8981\u6DF7\u5199\u3002" : "Combine tools by capability. Keep slash commands, MCP tools, and plugin skills distinct.",
857
+ "Combine tools by capability. Keep slash commands, MCP tools, and plugin skills distinct.",
861
858
  ""
862
859
  ];
863
- if (has("claude-mem") || has("context7") || has("curdx-flow")) {
864
- out.push(isZh() ? "- **\u63A5\u5230\u65B0\u9700\u6C42 / \u65B0 feature**" : "- **Starting a new feature**");
860
+ if (has("context7") || has("curdx-flow") || has("claude-mem")) {
861
+ out.push("- **Starting a new feature**");
865
862
  let step = 1;
866
- if (has("claude-mem")) {
867
- out.push(
868
- isZh() ? ` ${step++}. \u5148\u7528 \`/claude-mem:mem-search\` \u67E5\u5386\u53F2\uFF0C\u786E\u8BA4\u4E4B\u524D\u6709\u6CA1\u6709\u7C7B\u4F3C\u5B9E\u73B0\u3001\u8E29\u5751\u6216\u51B3\u7B56\u3002` : ` ${step++}. Start with \`/claude-mem:mem-search\` to check whether this work, decision, or pitfall already exists in memory.`
869
- );
870
- }
871
863
  if (has("context7")) {
872
- out.push(
873
- isZh() ? ` ${step++}. \u6D89\u53CA\u5916\u90E8\u5E93\u3001SDK\u3001\u6846\u67B6\u6216 API \u65F6\uFF0C\u4F7F\u7528 Context7 MCP \u62C9\u5B98\u65B9\u6700\u65B0\u6587\u6863\u3002` : ` ${step++}. If external libraries, SDKs, frameworks, or APIs are involved, use the Context7 MCP to pull current official docs.`
874
- );
864
+ out.push(` ${step++}. If external libraries, SDKs, frameworks, or APIs are involved, use the Context7 MCP to pull current official docs.`);
875
865
  }
876
866
  const planners = [];
877
- if (has("claude-mem")) {
878
- planners.push(
879
- isZh() ? "`/claude-mem:make-plan` \u4EA7\u51FA\u5206\u9636\u6BB5\u8BA1\u5212" : "`/claude-mem:make-plan` for a phased plan"
880
- );
881
- }
882
- if (has("curdx-flow")) {
883
- planners.push(
884
- isZh() ? "`/curdx-flow:new` \u6216\u76F8\u5173 spec flow \u8D77\u5B8C\u6574\u89C4\u683C" : "`/curdx-flow:new` or the spec flow for a full specification"
885
- );
886
- }
867
+ if (has("claude-mem")) planners.push("`/claude-mem:make-plan` for a phased plan");
868
+ if (has("curdx-flow")) planners.push("`/curdx-flow:new` or the spec flow for a full specification");
887
869
  if (planners.length > 0) {
888
- out.push(
889
- isZh() ? ` ${step++}. \u591A\u6B65\u9AA4\u3001\u9AD8\u4E0D\u786E\u5B9A\u6027\u3001\u8DE8\u6A21\u5757\u65F6\uFF0C\u518D\u8FDB\u5165 ${planners.join("\uFF0C\u6216 ")}\u3002` : ` ${step++}. Only move into ${planners.join(" or ")} when the work is multi-step, cross-cutting, or uncertain.`
890
- );
870
+ out.push(` ${step++}. Only move into ${planners.join(" or ")} when the work is multi-step, cross-cutting, or uncertain.`);
891
871
  }
892
- out.push(
893
- isZh() ? ` ${step++}. \u7B80\u5355\u3001\u8303\u56F4\u660E\u786E\u7684\u4E00\u6B21\u6027\u6539\u52A8\uFF0C\u76F4\u63A5\u505A\uFF0C\u4E0D\u8981\u5148\u628A\u6D41\u7A0B\u62C9\u6EE1\u3002` : ` ${step++}. For small, clear one-shot changes, implement directly instead of forcing the full workflow.`
894
- );
872
+ out.push(` ${step++}. For small, clear one-shot changes, implement directly instead of forcing the full workflow.`);
895
873
  out.push("");
896
874
  }
897
875
  const stuckLines = [];
898
876
  let s = 1;
899
- if (has("claude-mem")) {
900
- stuckLines.push(
901
- isZh() ? ` ${s++}. \u5148\u7528 \`/claude-mem:mem-search\` \u67E5\u8BB0\u5FC6\uFF0C\u786E\u8BA4\u662F\u4E0D\u662F\u4EE5\u524D\u89E3\u8FC7\u540C\u7C7B bug\u3002` : ` ${s++}. Check \`/claude-mem:mem-search\` first to see whether the same bug was solved before.`
902
- );
903
- }
904
877
  if (has("chrome-devtools-mcp")) {
905
- stuckLines.push(
906
- isZh() ? ` ${s++}. \u6D4F\u89C8\u5668\u4FA7\u95EE\u9898\u4F7F\u7528 Chrome DevTools MCP\uFF1A\u770B network\u3001console\u3001performance\u3001DOM snapshot\u3002` : ` ${s++}. For browser-side issues, use the Chrome DevTools MCP for network, console, performance, and DOM snapshots.`
907
- );
878
+ stuckLines.push(` ${s++}. For browser-side issues, use the Chrome DevTools MCP for network, console, performance, and DOM snapshots.`);
908
879
  }
909
880
  if (has("context7")) {
910
- stuckLines.push(
911
- isZh() ? ` ${s++}. \u5982\u679C\u6000\u7591\u662F\u5E93 / API \u884C\u4E3A\u53D8\u5316\uFF0C\u4F7F\u7528 Context7 MCP \u67E5\u5B98\u65B9\u6587\u6863\uFF0C\u4E0D\u8981\u51ED\u8BB0\u5FC6\u3002` : ` ${s++}. If the issue may come from library or API behavior, use the Context7 MCP instead of relying on memory.`
912
- );
881
+ stuckLines.push(` ${s++}. If the issue may come from library or API behavior, use the Context7 MCP instead of relying on memory.`);
913
882
  }
914
883
  const stillStuck = [];
915
- if (has("sequential-thinking")) {
916
- stillStuck.push(
917
- isZh() ? "\u4F7F\u7528 sequential-thinking MCP \u62C6\u5047\u8BBE" : "use the sequential-thinking MCP to break down hypotheses"
918
- );
919
- }
920
- if (has("pua")) {
921
- stillStuck.push(
922
- isZh() ? "\u518D\u8FDB\u5165 `/pua:pua-loop` \u8FED\u4EE3" : "then enter `/pua:pua-loop` for structured retries"
923
- );
924
- }
884
+ if (has("sequential-thinking")) stillStuck.push("switch to the sequential-thinking MCP to break down hypotheses");
885
+ if (has("pua")) stillStuck.push("enter `/pua:pua-loop` for structured retries");
925
886
  if (stillStuck.length > 0) {
926
- stuckLines.push(
927
- isZh() ? ` ${s++}. \u4E24\u8F6E\u4EE5\u4E0A\u4ECD\u5361\u4F4F\uFF0C\u518D ${stillStuck.join("\uFF0C\u6216 ")}\u3002` : ` ${s++}. If you are still stuck after multiple attempts, ${stillStuck.join(" or ")}.`
928
- );
887
+ stuckLines.push(` ${s++}. If you are still stuck after multiple attempts, ${stillStuck.join(" or ")}.`);
929
888
  }
930
889
  if (stuckLines.length > 0) {
931
- out.push(isZh() ? "- **\u9047\u5230 bug / \u8FDE\u7EED\u5361\u4F4F**" : "- **Debugging and repeated failures**", ...stuckLines, "");
890
+ out.push("- **Debugging and repeated failures**", ...stuckLines, "");
932
891
  }
933
892
  if (has("frontend-design") || has("chrome-devtools-mcp")) {
934
- out.push(isZh() ? "- **\u505A UI / \u524D\u7AEF\u9875\u9762**" : "- **UI and frontend work**");
893
+ out.push("- **UI and frontend work**");
935
894
  if (has("frontend-design")) {
936
- out.push(
937
- isZh() ? " - \u4F18\u5148\u4F7F\u7528 `frontend-design` \u63D2\u4EF6\u7684 UI skills\uFF1B\u82E5\u672A\u81EA\u52A8\u89E6\u53D1\uFF0C\u518D\u663E\u5F0F\u8C03\u7528\u5BF9\u5E94 skill\u3002" : " - Prioritize the `frontend-design` plugin skills for UI work; if they do not trigger automatically, invoke the relevant skill explicitly."
938
- );
895
+ out.push(" - Prioritize the `frontend-design` plugin skills for UI work; if they do not trigger automatically, invoke the relevant skill explicitly.");
939
896
  }
940
897
  if (has("chrome-devtools-mcp")) {
941
- out.push(
942
- isZh() ? " - \u6E32\u67D3\u5F02\u5E38\u3001\u4EA4\u4E92\u95EE\u9898\u6216\u89C6\u89C9\u56DE\u5F52\uFF0C\u4F7F\u7528 Chrome DevTools MCP \u9A8C\u8BC1\uFF0C\u4E0D\u8981\u53EA\u9760\u8089\u773C\u731C\u3002" : " - For rendering issues, interaction bugs, or visual regressions, verify with the Chrome DevTools MCP instead of relying on visual guesswork alone."
943
- );
898
+ out.push(" - For rendering issues, interaction bugs, or visual regressions, verify with the Chrome DevTools MCP instead of relying on visual guesswork alone.");
944
899
  }
945
900
  out.push("");
946
901
  }
947
902
  if (has("pua") || has("curdx-flow")) {
948
- out.push(isZh() ? "- **\u5927\u578B / \u8DE8\u6A21\u5757 / \u591A agent \u534F\u4F5C**" : "- **Large, cross-cutting, or multi-agent work**");
903
+ out.push("- **Large, cross-cutting, or multi-agent work**");
949
904
  if (has("pua")) {
950
- out.push(
951
- isZh() ? " - \u9700\u8981\u5E76\u884C\u62C6\u89E3\u4E0E\u56E2\u961F\u534F\u4F5C\u65F6\uFF0C\u7528 `/pua:p9`\uFF1B\u66F4\u9AD8\u5C42\u6218\u7565\u89C4\u5212\u518D\u8003\u8651 `/pua:p10`\u3002" : " - Use `/pua:p9` for parallel task decomposition and team coordination; reserve `/pua:p10` for higher-level strategy work."
952
- );
905
+ out.push(" - Use `/pua:p9` for parallel task decomposition and team coordination; reserve `/pua:p10` for higher-level strategy work.");
953
906
  }
954
907
  if (has("curdx-flow")) {
955
- out.push(
956
- isZh() ? " - \u4E00\u4E2A\u5927\u529F\u80FD\u9700\u8981\u62C6\u6210\u591A\u4E2A\u76F8\u4E92\u4F9D\u8D56\u7684\u89C4\u683C\u65F6\uFF0C\u4F7F\u7528 `/curdx-flow:triage`\u3002" : " - Use `/curdx-flow:triage` when one large feature needs to be split into multiple dependent specs."
957
- );
908
+ out.push(" - Use `/curdx-flow:triage` when one large feature needs to be split into multiple dependent specs.");
958
909
  }
959
910
  }
960
911
  while (out.length > 0 && out[out.length - 1] === "") out.pop();
@@ -963,46 +914,32 @@ function buildCombinationPatterns(ids) {
963
914
  function buildSkipRules(ids) {
964
915
  const has = (k) => ids.has(k);
965
916
  const out = [];
966
- out.push(
967
- isZh() ? "- \u4E00\u884C\u6539\u52A8\u3001typo\u3001\u7EAF\u91CD\u547D\u540D\u53D8\u91CF\uFF1A\u4E0D\u8981\u5148 plan\uFF0C\u4E0D\u8981\u5148\u5F00 spec\uFF0C\u76F4\u63A5\u6539\u3002" : "- For one-line changes, typos, or pure renames, skip planning and spec flow. Just make the edit."
968
- );
917
+ out.push("- For one-line changes, typos, or pure renames, skip planning and spec flow. Just make the edit.");
969
918
  const skips = [];
970
- if (has("pua")) skips.push("`/pua:pua`");
919
+ if (has("pua")) skips.push("`/pua:pua-loop`");
971
920
  if (has("sequential-thinking")) skips.push("`sequential-thinking`");
972
921
  if (skips.length > 0) {
973
- out.push(
974
- isZh() ? `- \u5DF2\u77E5\u786E\u5B9A\u7684 fix\uFF1A\u4E0D\u8981\u5148\u4E0A ${skips.join("\u3001")}\u3002` : `- For a known, deterministic fix, do not reach for ${skips.join(" or ")} first.`
975
- );
922
+ out.push(`- For a known, deterministic fix, do not reach for ${skips.join(" or ")} first.`);
976
923
  }
977
- out.push(
978
- isZh() ? "- \u7EAF\u6982\u5FF5\u89E3\u91CA\u9898\u53EF\u4EE5\u76F4\u63A5\u7B54\uFF1B\u5982\u679C\u662F\u5728\u95EE\u5F53\u524D\u4ED3\u5E93\u91CC\u7684\u4EE3\u7801\u542B\u4E49\uFF0C\u5148\u8BFB\u76F8\u5173\u6587\u4EF6\u518D\u89E3\u91CA\u3002" : "- Answer pure conceptual explanation questions directly. If the question is about code in this repository, read the relevant files first."
979
- );
924
+ out.push("- Answer pure conceptual explanation questions directly. If the question is about code in this repository, read the relevant files first.");
980
925
  if (has("curdx-flow")) {
981
- out.push(
982
- isZh() ? "- \u5355\u6587\u4EF6\u5C40\u90E8\u91CD\u6784\u6216\u5F88\u5C0F\u8303\u56F4\u7684\u6574\u7406\uFF1A\u901A\u5E38\u4E0D\u8981\u8FDB\u5165 curdx-flow spec \u6D41\u3002" : "- For a single-file refactor or a very local cleanup, usually do not enter the curdx-flow spec workflow."
983
- );
926
+ out.push("- For a single-file refactor or a very local cleanup, usually do not enter the curdx-flow spec workflow.");
984
927
  }
985
928
  return out;
986
929
  }
987
930
  function buildDecisionTree(ids) {
988
931
  const has = (k) => ids.has(k);
989
932
  const out = [];
990
- out.push(isZh() ? "1. \u80FD 1\u20132 \u6B65\u641E\u5B9A\uFF1F\u2192 \u76F4\u63A5\u505A\u3002" : "1. Can it be finished in 1-2 steps? -> Do it directly.");
991
- out.push(
992
- isZh() ? "2. \u591A\u6B65\u9AA4\u4F46\u8DEF\u5F84\u6E05\u6670\uFF1F\u2192 \u62C6\u6210\u7B80\u77ED\u4EFB\u52A1\u5217\u8868\u9010\u4E2A\u63A8\u8FDB\uFF0C\u4E0D\u8981\u9ED8\u8BA4\u8FDB\u5165\u5B8C\u6574 spec \u6D41\u3002" : "2. Is it multi-step but still clear? -> Break it into a short task list and execute without defaulting to the full spec flow."
993
- );
933
+ out.push("1. Can it be finished in 1-2 steps? -> Do it directly.");
934
+ out.push("2. Is it multi-step but still clear? -> Break it into a short task list and execute without defaulting to the full spec flow.");
994
935
  const planners = [];
995
936
  if (has("curdx-flow")) planners.push("`/curdx-flow:new`");
996
937
  if (has("claude-mem")) planners.push("`/claude-mem:make-plan`");
997
938
  if (planners.length > 0) {
998
- out.push(
999
- isZh() ? `3. \u9700\u6C42\u6A21\u7CCA\u3001\u8DE8\u6A21\u5757\u3001\u9700\u8981\u5206\u9636\u6BB5\u4EA4\u4ED8\uFF1F\u2192 ${planners.join(" \u6216 ")}\u3002` : `3. Is the request ambiguous, cross-cutting, or phase-based? -> ${planners.join(" or ")}.`
1000
- );
939
+ out.push(`3. Is the request ambiguous, cross-cutting, or phase-based? -> ${planners.join(" or ")}.`);
1001
940
  }
1002
941
  if (has("claude-mem")) {
1003
- out.push(
1004
- isZh() ? "4. \u8FD9\u7C7B\u6D3B\u4EE5\u524D\u53EF\u80FD\u505A\u8FC7\uFF1F\u2192 \u5148 `/claude-mem:mem-search`\u3002" : "4. Might this work have been done before? -> Start with `/claude-mem:mem-search`."
1005
- );
942
+ out.push("4. Might this work have been done before? -> Start with `/claude-mem:mem-search`.");
1006
943
  }
1007
944
  return out;
1008
945
  }
@@ -1016,23 +953,17 @@ function buildLanguagePolicy() {
1016
953
  function renderBlock(items) {
1017
954
  const installedIds = new Set(items.map((i) => i.id));
1018
955
  const sections = [
1019
- ["## Language Policy\uFF08\u8BED\u8A00\u89C4\u5219\uFF09", buildLanguagePolicy()],
1020
- [
1021
- isZh() ? "## Tool Combination Patterns\uFF08\u7EC4\u5408\u5DE5\u4F5C\u6D41\uFF09" : "## Tool Combination Patterns",
1022
- buildCombinationPatterns(installedIds)
1023
- ],
1024
- [isZh() ? "## Skip Rules\uFF08\u9632\u8FC7\u5EA6\u5DE5\u5177\u5316\uFF09" : "## Skip Rules", buildSkipRules(installedIds)],
1025
- [isZh() ? "## Decision Tree\uFF08\u9047\u5230\u6A21\u7CCA\u8BF7\u6C42\u65F6\uFF09" : "## Decision Tree", buildDecisionTree(installedIds)]
956
+ ["## Language Policy", buildLanguagePolicy()],
957
+ ["## Tool Combination Patterns", buildCombinationPatterns(installedIds)],
958
+ ["## Skip Rules", buildSkipRules(installedIds)],
959
+ ["## Decision Tree", buildDecisionTree(installedIds)]
1026
960
  ];
1027
961
  const lines = [BEGIN_MARKER];
1028
962
  for (const [heading, body] of sections) {
1029
963
  if (body.length === 0) continue;
1030
964
  lines.push(heading, "", ...body, "");
1031
965
  }
1032
- lines.push(
1033
- isZh() ? "\u8FD0\u884C `npx @curdx/flow` \u4EE5 install / update / uninstall\u3002" : "Run `npx @curdx/flow` to install / update / uninstall.",
1034
- END_MARKER
1035
- );
966
+ lines.push(END_MARKER);
1036
967
  return lines.join("\n");
1037
968
  }
1038
969
  function withEol(s, eol) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@curdx/flow",
3
- "version": "7.1.5",
3
+ "version": "7.1.6",
4
4
  "description": "Interactive installer for Claude Code plugins and MCP servers",
5
5
  "type": "module",
6
6
  "bin": "./dist/index.mjs",