@curdx/flow 7.1.4 → 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 +20 -0
- package/dist/index.mjs +87 -103
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,26 @@
|
|
|
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
|
+
|
|
19
|
+
## 7.1.5 — 2026-05-06
|
|
20
|
+
|
|
21
|
+
### Fixed
|
|
22
|
+
|
|
23
|
+
- **Prevent local source checkouts from silently running a stale CLI bundle.** A repo-local execution path could still write the old `~/.claude/CLAUDE.md` block even after `7.1.4` was published correctly, because `.gitignore` excludes `dist/` and `npx @curdx/flow` run from the source checkout may execute the checkout's local `bin` (`dist/index.mjs`) instead of the freshly published npm tarball. If `src/` had newer changes but `dist/` had not been rebuilt, the user would unknowingly run old installer logic and keep re-injecting the pre-7.1.4 managed block. Added `src/runner/buildFreshness.ts` and wired it into `src/index.ts` startup so source checkouts now fail fast with a clear error when `src/**/*.ts` is newer than `dist/index.mjs`, instructing the user to run `npm run build` or execute the published package explicitly. New tests in `tests/runner/buildFreshness.test.ts` cover both stale and fresh build states.
|
|
24
|
+
|
|
5
25
|
## 7.1.4 — 2026-05-06
|
|
6
26
|
|
|
7
27
|
### Changed
|
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
|
-
|
|
857
|
+
"Combine tools by capability. Keep slash commands, MCP tools, and plugin skills distinct.",
|
|
861
858
|
""
|
|
862
859
|
];
|
|
863
|
-
if (has("
|
|
864
|
-
out.push(
|
|
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
|
-
|
|
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
|
-
|
|
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(
|
|
890
|
+
out.push("- **Debugging and repeated failures**", ...stuckLines, "");
|
|
932
891
|
}
|
|
933
892
|
if (has("frontend-design") || has("chrome-devtools-mcp")) {
|
|
934
|
-
out.push(
|
|
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(
|
|
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(
|
|
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
|
|
1020
|
-
[
|
|
1021
|
-
|
|
1022
|
-
|
|
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) {
|
|
@@ -1711,6 +1642,58 @@ var analyzeCmd = defineCommand({
|
|
|
1711
1642
|
});
|
|
1712
1643
|
var analyze_default = analyzeCmd;
|
|
1713
1644
|
|
|
1645
|
+
// src/runner/buildFreshness.ts
|
|
1646
|
+
import { readdirSync, statSync, existsSync as existsSync3 } from "fs";
|
|
1647
|
+
import path5 from "path";
|
|
1648
|
+
import { fileURLToPath } from "url";
|
|
1649
|
+
var SELF_PATH = fileURLToPath(import.meta.url);
|
|
1650
|
+
var RUNNER_DIR = path5.dirname(SELF_PATH);
|
|
1651
|
+
var PROJECT_ROOT = path5.resolve(RUNNER_DIR, "..", "..");
|
|
1652
|
+
var SRC_DIR = path5.join(PROJECT_ROOT, "src");
|
|
1653
|
+
var DIST_ENTRY = path5.join(PROJECT_ROOT, "dist", "index.mjs");
|
|
1654
|
+
var SKIP_DIRS = /* @__PURE__ */ new Set(["node_modules", "dist", ".git"]);
|
|
1655
|
+
function newestTsMtimeMs(dir) {
|
|
1656
|
+
let newest = 0;
|
|
1657
|
+
const entries = readdirSync(dir, { withFileTypes: true });
|
|
1658
|
+
for (const entry of entries) {
|
|
1659
|
+
if (SKIP_DIRS.has(entry.name)) continue;
|
|
1660
|
+
const full = path5.join(dir, entry.name);
|
|
1661
|
+
if (entry.isDirectory()) {
|
|
1662
|
+
newest = Math.max(newest, newestTsMtimeMs(full));
|
|
1663
|
+
continue;
|
|
1664
|
+
}
|
|
1665
|
+
if (!entry.isFile() || !entry.name.endsWith(".ts")) continue;
|
|
1666
|
+
newest = Math.max(newest, statSync(full).mtimeMs);
|
|
1667
|
+
}
|
|
1668
|
+
return newest;
|
|
1669
|
+
}
|
|
1670
|
+
function assertFreshLocalBuild() {
|
|
1671
|
+
assertFreshBuild({ projectRoot: PROJECT_ROOT, srcDir: SRC_DIR, distEntry: DIST_ENTRY });
|
|
1672
|
+
}
|
|
1673
|
+
function assertFreshBuild(opts) {
|
|
1674
|
+
const { projectRoot, srcDir, distEntry } = opts;
|
|
1675
|
+
if (!existsSync3(srcDir) || !existsSync3(distEntry)) return;
|
|
1676
|
+
let distMtimeMs = 0;
|
|
1677
|
+
let newestSrcMtimeMs = 0;
|
|
1678
|
+
try {
|
|
1679
|
+
distMtimeMs = statSync(distEntry).mtimeMs;
|
|
1680
|
+
newestSrcMtimeMs = newestTsMtimeMs(srcDir);
|
|
1681
|
+
} catch {
|
|
1682
|
+
return;
|
|
1683
|
+
}
|
|
1684
|
+
if (newestSrcMtimeMs <= distMtimeMs) return;
|
|
1685
|
+
const relDist = path5.relative(projectRoot, distEntry);
|
|
1686
|
+
const relSrc = path5.relative(projectRoot, srcDir);
|
|
1687
|
+
throw new Error(
|
|
1688
|
+
[
|
|
1689
|
+
"Local build is stale: source files are newer than the bundled CLI entry.",
|
|
1690
|
+
`Detected source checkout at ${projectRoot}.`,
|
|
1691
|
+
`Run \`npm run build\` to refresh ${relDist}, or execute the published package explicitly with \`npx --package @curdx/flow@$(node -p "require('./package.json').version") @curdx/flow\`.`,
|
|
1692
|
+
`This guard only applies when running from a local checkout that still contains ${relSrc}/.`
|
|
1693
|
+
].join(" ")
|
|
1694
|
+
);
|
|
1695
|
+
}
|
|
1696
|
+
|
|
1714
1697
|
// src/index.ts
|
|
1715
1698
|
function parseLang(v) {
|
|
1716
1699
|
return v === "zh" || v === "en" ? v : void 0;
|
|
@@ -1846,6 +1829,7 @@ async function runInteractive(argv2) {
|
|
|
1846
1829
|
}
|
|
1847
1830
|
var argv = process.argv.slice(2);
|
|
1848
1831
|
var first = firstNonFlag(argv);
|
|
1832
|
+
assertFreshLocalBuild();
|
|
1849
1833
|
if (first === void 0 || first !== void 0 && !SUBCOMMANDS.has(first) && first !== "--help" && first !== "-h") {
|
|
1850
1834
|
if (first === void 0) {
|
|
1851
1835
|
runInteractive(argv).catch((err) => {
|