i18-fe-automator-beta 2.1.6 → 2.1.8

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.
@@ -1807,6 +1807,8 @@ const CACHE_KEY = "zentao"; // .cache/zentao: { account, password, zentaosid, pr
1807
1807
 
1808
1808
  // 需求列表展示与pull并发的上限
1809
1809
  const PLAN_STORY_TABLE_LIMIT = 50;
1810
+ // 需求列表表格中标题的最大展示长度(console.table单元格不换行, 超长截断)
1811
+ const STORY_TITLE_LIMIT = 30;
1810
1812
  const PULL_CONCURRENCY = 5;
1811
1813
 
1812
1814
  // 任务状态中文标签(close 命令展示用)
@@ -2752,8 +2754,8 @@ function writeCodeWorkspace(zentaoDir, planTitle, frontProjects, backProjects) {
2752
2754
  /**
2753
2755
  * plan --export 产出需求目录 .zentao-<计划名>/(产出契约见 .trae/documents/story-command-zentao.md 第4节):
2754
2756
  * tasks/manifest.json(对象结构: 顶部projects汇总全部涉及项目 + stories数组合并去重, 同storyID覆盖title/taskDir/projects, 按storyID数值排序)
2755
- * + 每条需求 tasks/<taskDir>/task.md(同名taskDir已由 exportPlanStories 先删除, 此处全新写入)
2756
- * plan.md 与 codex-*.md 是 workflow 的产物(重拉同名taskDir会连带删除, 确认关卡后不要再重导)
2757
+ * + 每条需求 tasks/<taskDir>/task.md(同名taskDir的task.md与images/已由 exportPlanStories 先删除, 此处全新写入)
2758
+ * plan.md 与 codex-*.md 是 workflow 的产物(重导时手术式刷新保留不覆盖, 但需求内容变了需AI自行判断是否重排)
2757
2759
  */
2758
2760
  function writeZentaoTasks({ plan, scope, stories, storyDetails, products, failed, storyImages, zentaoDir }) {
2759
2761
  const tasksDir = path.join(zentaoDir, "tasks");
@@ -2875,6 +2877,8 @@ async function selectPlan(plans, keyword) {
2875
2877
  message: "请选择计划",
2876
2878
  name: "plan",
2877
2879
  type: "rawlist",
2880
+ // 到列表边界停止, 不循环回第一条
2881
+ loop: false,
2878
2882
  choices: plans.map((p) => ({
2879
2883
  name: `#${p.id} ${p.title} [${p.begin} ~ ${p.end}] (${
2880
2884
  p.stories ?? 0
@@ -2950,7 +2954,11 @@ async function showPlanDetail(zentaosid, selected) {
2950
2954
  );
2951
2955
  return {
2952
2956
  ID: s.id,
2953
- 标题: s.title,
2957
+ // console.table单元格不换行, 超长标题截断(完整标题可用 story detail 查看)
2958
+ 标题:
2959
+ s.title.length > STORY_TITLE_LIMIT
2960
+ ? `${s.title.slice(0, STORY_TITLE_LIMIT)}...`
2961
+ : s.title,
2954
2962
  前端工时: isFront ? s.webestimate : "-",
2955
2963
  后端工时: Number(s.estimate) > 0 ? s.estimate : "-",
2956
2964
  poollist: s.poollist || "-",
@@ -2965,9 +2973,64 @@ async function showPlanDetail(zentaosid, selected) {
2965
2973
  return stories;
2966
2974
  }
2967
2975
 
2968
- // 导出动作(plan收尾交互使用): 按范围过滤需求, 拉详情并产出 .zentao-<计划名>/ + 工作区
2976
+ // 按导出范围过滤需求: front=有前端工时 | back=有后端工时 | all=全部
2977
+ function filterStoriesByScope(stories, scope) {
2978
+ return stories.filter((s) => {
2979
+ if (scope === "front") return Number(s.webestimate) > 0;
2980
+ if (scope === "back") return Number(s.estimate) > 0;
2981
+ return true;
2982
+ });
2983
+ }
2984
+
2985
+ // 导出前下一步: 列出范围内需求供勾选(「全选」与子项父子联动, 复用 CheckboxAllPrompt)
2986
+ // 返回勾选的需求数组; 未勾选任何项返回[](调用方据此跳过导出)
2987
+ async function selectExportStories(stories, scope) {
2988
+ const scoped = filterStoriesByScope(stories, scope);
2989
+ // 工时展示跟随导出范围: front只展示前端工时 / back只展示后端工时 / all全展示
2990
+ const hoursOf = (s) => {
2991
+ const w = Number(s.webestimate) > 0 ? `${s.webestimate}h` : "-";
2992
+ const e = Number(s.estimate) > 0 ? `${s.estimate}h` : "-";
2993
+ if (scope === "front") return `(前端${w})`;
2994
+ if (scope === "back") return `(后端${e})`;
2995
+ return `(前端${w}/后端${e})`;
2996
+ };
2997
+ // prompt 注册幂等, 与 upload.js 各自使用前注册
2998
+ inquirer.registerPrompt("checkbox-all", CheckboxAllPrompt);
2999
+ // 需求选项拼接序号, 与数字键选择对应(按2选第一个需求...); 「全选」不占序号, 固定首项无前缀
3000
+ const choices = [
3001
+ {
3002
+ name: `全选(共${scoped.length}条)`,
3003
+ value: SELECT_ALL,
3004
+ short: "全选",
3005
+ // 默认全选(与全部子项联动一致)
3006
+ checked: true,
3007
+ },
3008
+ ...scoped.map((s, index) => ({
3009
+ name: `${index + 1}. #${s.id} ${s.title} ${hoursOf(s)}`,
3010
+ value: String(s.id),
3011
+ short: `#${s.id}`,
3012
+ checked: true,
3013
+ })),
3014
+ ];
3015
+ const { picked } = await inquirer.prompt([
3016
+ {
3017
+ message: `请勾选要导出的需求(空格选择, 回车确认; 「全选」与单个需求联动)`,
3018
+ name: "picked",
3019
+ type: "checkbox-all",
3020
+ // 到列表边界停止, 不循环回第一条
3021
+ loop: false,
3022
+ choices,
3023
+ },
3024
+ ]);
3025
+ // 全选勾上时返回值含哨兵值, 一并忽略后按ID过滤
3026
+ const ids = new Set(picked.filter((v) => v !== SELECT_ALL));
3027
+ return scoped.filter((s) => ids.has(String(s.id)));
3028
+ }
3029
+
3030
+ // 导出动作(plan收尾交互使用): 按范围过滤需求(交互勾选时再按pickedIds收窄), 拉详情并产出 .zentao-<计划名>/ + 工作区
2969
3031
  // scope: front=有前端工时的需求 | back=有后端工时的需求 | all=全部
2970
- async function exportPlanStories(zentaosid, selected, scope) {
3032
+ // pickedIds: 交互勾选的需求ID集合(Set); 未传(--export直通一键场景)为范围内全量
3033
+ async function exportPlanStories(zentaosid, selected, scope, pickedIds) {
2971
3034
  const scopeLabel =
2972
3035
  scope === "front" ? "前端" : scope === "back" ? "后端" : "全部";
2973
3036
  Loading$1.start("拉取计划详情中...");
@@ -2975,12 +3038,10 @@ async function exportPlanStories(zentaosid, selected, scope) {
2975
3038
  zentaosid,
2976
3039
  selected.id
2977
3040
  );
2978
- // 按导出范围过滤需求
2979
- const stories = allStories.filter((s) => {
2980
- if (scope === "front") return Number(s.webestimate) > 0;
2981
- if (scope === "back") return Number(s.estimate) > 0;
2982
- return true;
2983
- });
3041
+ // 按导出范围过滤需求, 再按勾选ID收窄(pickedIds未传=范围内全量)
3042
+ const stories = filterStoriesByScope(allStories, scope).filter(
3043
+ (s) => !pickedIds || pickedIds.has(String(s.id))
3044
+ );
2984
3045
  Loading$1.succeed(
2985
3046
  `拉取计划详情成功(${scopeLabel}${stories.length}/${allStories.length}条)`
2986
3047
  );
@@ -3018,16 +3079,23 @@ async function exportPlanStories(zentaosid, selected, scope) {
3018
3079
  Loading$1.succeed(`拉取需求详情完成(${done}/${stories.length})`);
3019
3080
  // 需求目录: .zentao-<计划名>(多计划互不混目录)
3020
3081
  const zentaoDir = path.join(process.cwd(), zentaoDirNameOf(plan));
3021
- // 同名taskDir直接删除重导(重拉=全新快照, 连带清掉旧images与旧plan/codex报告; 失败条目保留旧数据)
3082
+ // 同名taskDir手术式刷新: 只删task.md与images/重写, 保留plan.md/codex报告等AI产物; 失败条目保留旧数据
3022
3083
  stories.forEach((story) => {
3023
3084
  if (failed.has(String(story.id))) return;
3024
3085
  const dir = path.join(zentaoDir, "tasks", taskDirOf(story));
3025
3086
  if (isExistPath(dir)) {
3026
- fs.rmSync(dir, { recursive: true, force: true });
3027
- console.log(chalk.yellow(`同名任务目录已删除重导: ${taskDirOf(story)}`));
3087
+ const taskMd = path.join(dir, "task.md");
3088
+ if (isExistPath(taskMd)) fs.rmSync(taskMd, { force: true });
3089
+ const imagesDir = path.join(dir, "images");
3090
+ if (isExistPath(imagesDir)) {
3091
+ fs.rmSync(imagesDir, { recursive: true, force: true });
3092
+ }
3093
+ console.log(
3094
+ chalk.yellow(`同名任务目录已刷新(保留plan/报告): ${taskDirOf(story)}`)
3095
+ );
3028
3096
  }
3029
3097
  });
3030
- // 图片本地化到 tasks/<taskDir>/images/(目录已删全量重下, 失败黄字不阻塞, 引用降级远程url)
3098
+ // 图片本地化到 tasks/<taskDir>/images/(images/已删全量重下, 失败黄字不阻塞, 引用降级远程url)
3031
3099
  const storyImages = await downloadStoryImages({
3032
3100
  stories,
3033
3101
  storyDetails,
@@ -3053,11 +3121,13 @@ async function exportPlanStories(zentaosid, selected, scope) {
3053
3121
  scope === "back" || scope === "all"
3054
3122
  ? collectPoolProjects(stories, "back")
3055
3123
  : [];
3056
- if (frontProjects.length || backProjects.length) {
3057
- writeCodeWorkspace(zentaoDir, plan.title, frontProjects, backProjects);
3058
- } else {
3059
- console.log(chalk.yellow("本范围无关联项目, 跳过工作区生成"));
3124
+ // 无pool时仍生成单目录工作区(只含需求目录自身, 打开仍能看manifest与task.md), 黄字提示补维护
3125
+ if (!frontProjects.length && !backProjects.length) {
3126
+ console.log(
3127
+ chalk.yellow("本范围需求均未维护poollist, 工作区仅含需求目录, 维护后重跑可补齐项目")
3128
+ );
3060
3129
  }
3130
+ writeCodeWorkspace(zentaoDir, plan.title, frontProjects, backProjects);
3061
3131
  }
3062
3132
 
3063
3133
  /**
@@ -4000,7 +4070,7 @@ async function storyMain(action, cliOpts) {
4000
4070
  });
4001
4071
  return;
4002
4072
  }
4003
- // plan: 产品选择 -> 计划选取 -> 展示 -> 导出(--export直通一键场景, 不传则收尾交互)
4073
+ // plan: 产品选择 -> 计划选取 -> 展示 -> 导出询问(交互场景勾选需求, --export直通一键场景)
4004
4074
  const session = await ensureZentaoSession(cliOpts);
4005
4075
  await session.request(async (zentaosid) => {
4006
4076
  // 位置参数纯数字 = 产品计划ID(productplan-browse-<ID>.html 中的ID), 等同 --product 直通
@@ -4044,7 +4114,17 @@ async function storyMain(action, cliOpts) {
4044
4114
  ]));
4045
4115
  }
4046
4116
  if (exportChoice !== "none") {
4047
- await exportPlanStories(zentaosid, selected, exportChoice);
4117
+ // 下一步(仅交互场景): 列出范围内需求勾选要导出的(「全选」与单个需求互斥); --export直通为范围内全量
4118
+ let pickedIds = null;
4119
+ if (cliOpts.export === undefined) {
4120
+ const pickedStories = await selectExportStories(stories, exportChoice);
4121
+ if (!pickedStories.length) {
4122
+ console.log(chalk.yellow("未勾选需求, 跳过导出"));
4123
+ return;
4124
+ }
4125
+ pickedIds = new Set(pickedStories.map((s) => String(s.id)));
4126
+ }
4127
+ await exportPlanStories(zentaosid, selected, exportChoice, pickedIds);
4048
4128
  }
4049
4129
  });
4050
4130
  }
@@ -1785,6 +1785,8 @@ const CACHE_KEY = "zentao"; // .cache/zentao: { account, password, zentaosid, pr
1785
1785
 
1786
1786
  // 需求列表展示与pull并发的上限
1787
1787
  const PLAN_STORY_TABLE_LIMIT = 50;
1788
+ // 需求列表表格中标题的最大展示长度(console.table单元格不换行, 超长截断)
1789
+ const STORY_TITLE_LIMIT = 30;
1788
1790
  const PULL_CONCURRENCY = 5;
1789
1791
 
1790
1792
  // 任务状态中文标签(close 命令展示用)
@@ -2730,8 +2732,8 @@ function writeCodeWorkspace(zentaoDir, planTitle, frontProjects, backProjects) {
2730
2732
  /**
2731
2733
  * plan --export 产出需求目录 .zentao-<计划名>/(产出契约见 .trae/documents/story-command-zentao.md 第4节):
2732
2734
  * tasks/manifest.json(对象结构: 顶部projects汇总全部涉及项目 + stories数组合并去重, 同storyID覆盖title/taskDir/projects, 按storyID数值排序)
2733
- * + 每条需求 tasks/<taskDir>/task.md(同名taskDir已由 exportPlanStories 先删除, 此处全新写入)
2734
- * plan.md 与 codex-*.md 是 workflow 的产物(重拉同名taskDir会连带删除, 确认关卡后不要再重导)
2735
+ * + 每条需求 tasks/<taskDir>/task.md(同名taskDir的task.md与images/已由 exportPlanStories 先删除, 此处全新写入)
2736
+ * plan.md 与 codex-*.md 是 workflow 的产物(重导时手术式刷新保留不覆盖, 但需求内容变了需AI自行判断是否重排)
2735
2737
  */
2736
2738
  function writeZentaoTasks({ plan, scope, stories, storyDetails, products, failed, storyImages, zentaoDir }) {
2737
2739
  const tasksDir = path.join(zentaoDir, "tasks");
@@ -2853,6 +2855,8 @@ async function selectPlan(plans, keyword) {
2853
2855
  message: "请选择计划",
2854
2856
  name: "plan",
2855
2857
  type: "rawlist",
2858
+ // 到列表边界停止, 不循环回第一条
2859
+ loop: false,
2856
2860
  choices: plans.map((p) => ({
2857
2861
  name: `#${p.id} ${p.title} [${p.begin} ~ ${p.end}] (${
2858
2862
  p.stories ?? 0
@@ -2928,7 +2932,11 @@ async function showPlanDetail(zentaosid, selected) {
2928
2932
  );
2929
2933
  return {
2930
2934
  ID: s.id,
2931
- 标题: s.title,
2935
+ // console.table单元格不换行, 超长标题截断(完整标题可用 story detail 查看)
2936
+ 标题:
2937
+ s.title.length > STORY_TITLE_LIMIT
2938
+ ? `${s.title.slice(0, STORY_TITLE_LIMIT)}...`
2939
+ : s.title,
2932
2940
  前端工时: isFront ? s.webestimate : "-",
2933
2941
  后端工时: Number(s.estimate) > 0 ? s.estimate : "-",
2934
2942
  poollist: s.poollist || "-",
@@ -2943,9 +2951,64 @@ async function showPlanDetail(zentaosid, selected) {
2943
2951
  return stories;
2944
2952
  }
2945
2953
 
2946
- // 导出动作(plan收尾交互使用): 按范围过滤需求, 拉详情并产出 .zentao-<计划名>/ + 工作区
2954
+ // 按导出范围过滤需求: front=有前端工时 | back=有后端工时 | all=全部
2955
+ function filterStoriesByScope(stories, scope) {
2956
+ return stories.filter((s) => {
2957
+ if (scope === "front") return Number(s.webestimate) > 0;
2958
+ if (scope === "back") return Number(s.estimate) > 0;
2959
+ return true;
2960
+ });
2961
+ }
2962
+
2963
+ // 导出前下一步: 列出范围内需求供勾选(「全选」与子项父子联动, 复用 CheckboxAllPrompt)
2964
+ // 返回勾选的需求数组; 未勾选任何项返回[](调用方据此跳过导出)
2965
+ async function selectExportStories(stories, scope) {
2966
+ const scoped = filterStoriesByScope(stories, scope);
2967
+ // 工时展示跟随导出范围: front只展示前端工时 / back只展示后端工时 / all全展示
2968
+ const hoursOf = (s) => {
2969
+ const w = Number(s.webestimate) > 0 ? `${s.webestimate}h` : "-";
2970
+ const e = Number(s.estimate) > 0 ? `${s.estimate}h` : "-";
2971
+ if (scope === "front") return `(前端${w})`;
2972
+ if (scope === "back") return `(后端${e})`;
2973
+ return `(前端${w}/后端${e})`;
2974
+ };
2975
+ // prompt 注册幂等, 与 upload.js 各自使用前注册
2976
+ inquirer.registerPrompt("checkbox-all", CheckboxAllPrompt);
2977
+ // 需求选项拼接序号, 与数字键选择对应(按2选第一个需求...); 「全选」不占序号, 固定首项无前缀
2978
+ const choices = [
2979
+ {
2980
+ name: `全选(共${scoped.length}条)`,
2981
+ value: SELECT_ALL,
2982
+ short: "全选",
2983
+ // 默认全选(与全部子项联动一致)
2984
+ checked: true,
2985
+ },
2986
+ ...scoped.map((s, index) => ({
2987
+ name: `${index + 1}. #${s.id} ${s.title} ${hoursOf(s)}`,
2988
+ value: String(s.id),
2989
+ short: `#${s.id}`,
2990
+ checked: true,
2991
+ })),
2992
+ ];
2993
+ const { picked } = await inquirer.prompt([
2994
+ {
2995
+ message: `请勾选要导出的需求(空格选择, 回车确认; 「全选」与单个需求联动)`,
2996
+ name: "picked",
2997
+ type: "checkbox-all",
2998
+ // 到列表边界停止, 不循环回第一条
2999
+ loop: false,
3000
+ choices,
3001
+ },
3002
+ ]);
3003
+ // 全选勾上时返回值含哨兵值, 一并忽略后按ID过滤
3004
+ const ids = new Set(picked.filter((v) => v !== SELECT_ALL));
3005
+ return scoped.filter((s) => ids.has(String(s.id)));
3006
+ }
3007
+
3008
+ // 导出动作(plan收尾交互使用): 按范围过滤需求(交互勾选时再按pickedIds收窄), 拉详情并产出 .zentao-<计划名>/ + 工作区
2947
3009
  // scope: front=有前端工时的需求 | back=有后端工时的需求 | all=全部
2948
- async function exportPlanStories(zentaosid, selected, scope) {
3010
+ // pickedIds: 交互勾选的需求ID集合(Set); 未传(--export直通一键场景)为范围内全量
3011
+ async function exportPlanStories(zentaosid, selected, scope, pickedIds) {
2949
3012
  const scopeLabel =
2950
3013
  scope === "front" ? "前端" : scope === "back" ? "后端" : "全部";
2951
3014
  Loading.start("拉取计划详情中...");
@@ -2953,12 +3016,10 @@ async function exportPlanStories(zentaosid, selected, scope) {
2953
3016
  zentaosid,
2954
3017
  selected.id
2955
3018
  );
2956
- // 按导出范围过滤需求
2957
- const stories = allStories.filter((s) => {
2958
- if (scope === "front") return Number(s.webestimate) > 0;
2959
- if (scope === "back") return Number(s.estimate) > 0;
2960
- return true;
2961
- });
3019
+ // 按导出范围过滤需求, 再按勾选ID收窄(pickedIds未传=范围内全量)
3020
+ const stories = filterStoriesByScope(allStories, scope).filter(
3021
+ (s) => !pickedIds || pickedIds.has(String(s.id))
3022
+ );
2962
3023
  Loading.succeed(
2963
3024
  `拉取计划详情成功(${scopeLabel}${stories.length}/${allStories.length}条)`
2964
3025
  );
@@ -2996,16 +3057,23 @@ async function exportPlanStories(zentaosid, selected, scope) {
2996
3057
  Loading.succeed(`拉取需求详情完成(${done}/${stories.length})`);
2997
3058
  // 需求目录: .zentao-<计划名>(多计划互不混目录)
2998
3059
  const zentaoDir = path.join(process.cwd(), zentaoDirNameOf(plan));
2999
- // 同名taskDir直接删除重导(重拉=全新快照, 连带清掉旧images与旧plan/codex报告; 失败条目保留旧数据)
3060
+ // 同名taskDir手术式刷新: 只删task.md与images/重写, 保留plan.md/codex报告等AI产物; 失败条目保留旧数据
3000
3061
  stories.forEach((story) => {
3001
3062
  if (failed.has(String(story.id))) return;
3002
3063
  const dir = path.join(zentaoDir, "tasks", taskDirOf(story));
3003
3064
  if (isExistPath(dir)) {
3004
- fs.rmSync(dir, { recursive: true, force: true });
3005
- console.log(chalk.yellow(`同名任务目录已删除重导: ${taskDirOf(story)}`));
3065
+ const taskMd = path.join(dir, "task.md");
3066
+ if (isExistPath(taskMd)) fs.rmSync(taskMd, { force: true });
3067
+ const imagesDir = path.join(dir, "images");
3068
+ if (isExistPath(imagesDir)) {
3069
+ fs.rmSync(imagesDir, { recursive: true, force: true });
3070
+ }
3071
+ console.log(
3072
+ chalk.yellow(`同名任务目录已刷新(保留plan/报告): ${taskDirOf(story)}`)
3073
+ );
3006
3074
  }
3007
3075
  });
3008
- // 图片本地化到 tasks/<taskDir>/images/(目录已删全量重下, 失败黄字不阻塞, 引用降级远程url)
3076
+ // 图片本地化到 tasks/<taskDir>/images/(images/已删全量重下, 失败黄字不阻塞, 引用降级远程url)
3009
3077
  const storyImages = await downloadStoryImages({
3010
3078
  stories,
3011
3079
  storyDetails,
@@ -3031,11 +3099,13 @@ async function exportPlanStories(zentaosid, selected, scope) {
3031
3099
  scope === "back" || scope === "all"
3032
3100
  ? collectPoolProjects(stories, "back")
3033
3101
  : [];
3034
- if (frontProjects.length || backProjects.length) {
3035
- writeCodeWorkspace(zentaoDir, plan.title, frontProjects, backProjects);
3036
- } else {
3037
- console.log(chalk.yellow("本范围无关联项目, 跳过工作区生成"));
3102
+ // 无pool时仍生成单目录工作区(只含需求目录自身, 打开仍能看manifest与task.md), 黄字提示补维护
3103
+ if (!frontProjects.length && !backProjects.length) {
3104
+ console.log(
3105
+ chalk.yellow("本范围需求均未维护poollist, 工作区仅含需求目录, 维护后重跑可补齐项目")
3106
+ );
3038
3107
  }
3108
+ writeCodeWorkspace(zentaoDir, plan.title, frontProjects, backProjects);
3039
3109
  }
3040
3110
 
3041
3111
  /**
@@ -3978,7 +4048,7 @@ async function storyMain(action, cliOpts) {
3978
4048
  });
3979
4049
  return;
3980
4050
  }
3981
- // plan: 产品选择 -> 计划选取 -> 展示 -> 导出(--export直通一键场景, 不传则收尾交互)
4051
+ // plan: 产品选择 -> 计划选取 -> 展示 -> 导出询问(交互场景勾选需求, --export直通一键场景)
3982
4052
  const session = await ensureZentaoSession(cliOpts);
3983
4053
  await session.request(async (zentaosid) => {
3984
4054
  // 位置参数纯数字 = 产品计划ID(productplan-browse-<ID>.html 中的ID), 等同 --product 直通
@@ -4022,7 +4092,17 @@ async function storyMain(action, cliOpts) {
4022
4092
  ]));
4023
4093
  }
4024
4094
  if (exportChoice !== "none") {
4025
- await exportPlanStories(zentaosid, selected, exportChoice);
4095
+ // 下一步(仅交互场景): 列出范围内需求勾选要导出的(「全选」与单个需求互斥); --export直通为范围内全量
4096
+ let pickedIds = null;
4097
+ if (cliOpts.export === undefined) {
4098
+ const pickedStories = await selectExportStories(stories, exportChoice);
4099
+ if (!pickedStories.length) {
4100
+ console.log(chalk.yellow("未勾选需求, 跳过导出"));
4101
+ return;
4102
+ }
4103
+ pickedIds = new Set(pickedStories.map((s) => String(s.id)));
4104
+ }
4105
+ await exportPlanStories(zentaosid, selected, exportChoice, pickedIds);
4026
4106
  }
4027
4107
  });
4028
4108
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "i18-fe-automator-beta",
3
- "version": "2.1.6",
3
+ "version": "2.1.8",
4
4
  "description": "i18-fe-automator-beta内测版本,只用于key: ''替换、检测中文规则等",
5
5
  "type": "module",
6
6
  "bin": {