i18-fe-automator-beta 2.1.5 → 2.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.
@@ -2215,7 +2215,7 @@ async function selectProduct(zentaosid, cliOpts) {
2215
2215
  name: `#${p.id} ${p.name}`,
2216
2216
  value: p.id,
2217
2217
  })),
2218
- { name: "输入其他产品ID...", value: "__INPUT__" },
2218
+ { name: "输入其他产品-计划ID(如: https://zentao.hongxinshop.com/zentao/productplan-browse-29.html 中的29)", value: "__INPUT__" },
2219
2219
  ],
2220
2220
  },
2221
2221
  ]);
@@ -2226,11 +2226,11 @@ async function selectProduct(zentaosid, cliOpts) {
2226
2226
  }
2227
2227
  const { id } = await inquirer.prompt([
2228
2228
  {
2229
- message: "请输入产品ID",
2229
+ message: "请输入产品计划ID(如: https://zentao.hongxinshop.com/zentao/productplan-browse-29.html 中的29)",
2230
2230
  name: "id",
2231
2231
  type: "input",
2232
2232
  validate: (val) =>
2233
- /^\d+$/.test(String(val).trim()) || "请输入数字产品ID",
2233
+ /^\d+$/.test(String(val).trim()) || "请输入数字产品计划ID",
2234
2234
  },
2235
2235
  ]);
2236
2236
  return resolveProduct(zentaosid, id);
@@ -2854,7 +2854,7 @@ function writeZentaoTasks({ plan, scope, stories, storyDetails, products, failed
2854
2854
  }
2855
2855
  }
2856
2856
 
2857
- // 计划选取交互: 关键词过滤 > 命中1条自动选中 > rawlist选取
2857
+ // 计划选取交互: 关键词过滤(标题) > 命中1条自动选中 > rawlist选取
2858
2858
  async function selectPlan(plans, keyword) {
2859
2859
  if (keyword && String(keyword).trim()) {
2860
2860
  const kw = String(keyword).trim().toLowerCase();
@@ -2899,10 +2899,15 @@ async function loadPlanList(zentaosid, productID) {
2899
2899
  return plans;
2900
2900
  }
2901
2901
 
2902
- // plan动作: 选中计划后展示计划信息 + 关联需求列表(ID供detail用)
2902
+ // plan动作: 选中计划后展示计划信息 + 关联需求列表(ID供detail用); 返回关联需求(供调用方判断空计划)
2903
2903
  async function showPlanDetail(zentaosid, selected) {
2904
2904
  Loading$1.start("拉取计划详情中...");
2905
2905
  const { plan, stories } = await fetchPlanDetail(zentaosid, selected.id);
2906
+ // 0条需求时详情/工时统计均无意义, 一句话结束
2907
+ if (!stories.length) {
2908
+ Loading$1.succeed(`计划 #${plan.id} ${plan.title} 无关联需求`);
2909
+ return stories;
2910
+ }
2906
2911
  Loading$1.succeed("拉取计划详情成功");
2907
2912
  console.log(chalk.bold("\n计划信息"));
2908
2913
  console.table([
@@ -2957,6 +2962,7 @@ async function showPlanDetail(zentaosid, selected) {
2957
2962
  console.log(
2958
2963
  chalk.blue("提示: ID 供 fe-it-beta story detail <需求ID> 使用(也可传需求url)")
2959
2964
  );
2965
+ return stories;
2960
2966
  }
2961
2967
 
2962
2968
  // 导出动作(plan收尾交互使用): 按范围过滤需求, 拉详情并产出 .zentao-<计划名>/ + 工作区
@@ -3125,7 +3131,7 @@ async function storyMain(action, cliOpts) {
3125
3131
  if (!match) {
3126
3132
  console.log(
3127
3133
  chalk.red(
3128
- "请传入项目ID(从禅道项目需求页url复制), 如: fe-it-beta story task 2915 或 fe-it-beta story task https://zentao.hongxinshop.com/zentao/project-story-2915.html"
3134
+ "请传入项目-需求url的ID, 如: fe-it-beta story task 2915 或 fe-it-beta story task https://zentao.hongxinshop.com/zentao/project-story-2915.html"
3129
3135
  )
3130
3136
  );
3131
3137
  process.exit(1);
@@ -3725,7 +3731,7 @@ async function storyMain(action, cliOpts) {
3725
3731
  if (!match) {
3726
3732
  console.log(
3727
3733
  chalk.red(
3728
- "请传入项目ID(从禅道任务列表url复制), 如: fe-it-beta story close 2915 或 fe-it-beta story close https://zentao.hongxinshop.com/zentao/project-task-2915-myinvolved.html"
3734
+ "请传入项目-任务-由我参与url的ID, 如: fe-it-beta story close 2915 或 fe-it-beta story close https://zentao.hongxinshop.com/zentao/project-task-2915-myinvolved.html"
3729
3735
  )
3730
3736
  );
3731
3737
  process.exit(1);
@@ -3997,10 +4003,26 @@ async function storyMain(action, cliOpts) {
3997
4003
  // plan: 产品选择 -> 计划选取 -> 展示 -> 导出(--export直通一键场景, 不传则收尾交互)
3998
4004
  const session = await ensureZentaoSession(cliOpts);
3999
4005
  await session.request(async (zentaosid) => {
4000
- const product = await selectProduct(zentaosid, cliOpts);
4006
+ // 位置参数纯数字 = 产品计划ID(productplan-browse-<ID>.html 中的ID), 等同 --product 直通
4007
+ const nameArg = String(cliOpts.name || "").trim();
4008
+ const productIdArg = /^\d+$/.test(nameArg) ? nameArg : "";
4009
+ const product = await selectProduct(zentaosid, {
4010
+ ...cliOpts,
4011
+ product: cliOpts.product || productIdArg,
4012
+ });
4001
4013
  const plans = await loadPlanList(zentaosid, product.id);
4002
- const selected = await selectPlan(plans, cliOpts.name);
4003
- await showPlanDetail(zentaosid, selected);
4014
+ // 该产品无计划时后续选取/详情/导出均无意义, 提前结束
4015
+ if (!plans.length) {
4016
+ console.log(chalk.yellow(`产品 #${product.id} ${product.name} 下无计划, 结束`));
4017
+ return;
4018
+ }
4019
+ // 纯数字已用作产品ID, 非数字关键词仍按计划标题过滤
4020
+ const selected = await selectPlan(plans, productIdArg ? "" : cliOpts.name);
4021
+ const stories = await showPlanDetail(zentaosid, selected);
4022
+ // 空计划提示已在showPlanDetail输出, 直接结束不进导出询问
4023
+ if (!stories.length) {
4024
+ return;
4025
+ }
4004
4026
  // 导出范围: --export参数直通(一键场景) > 收尾交互(默认前端)
4005
4027
  let exportChoice;
4006
4028
  if (cliOpts.export !== undefined) {
@@ -4009,14 +4031,14 @@ async function storyMain(action, cliOpts) {
4009
4031
  } else {
4010
4032
  ({ exportChoice } = await inquirer.prompt([
4011
4033
  {
4012
- message: "是否导出需求文件(按计划生成 .zentao-<计划名>目录)?",
4034
+ message: `是否导出需求文件(按计划生成 .zentao-<计划名>目录, ${chalk.red("目前导出功能只适用于claude code批量建任务")})?`,
4013
4035
  name: "exportChoice",
4014
4036
  type: "list",
4015
4037
  choices: [
4016
- { name: "导出: 前端需求", value: "front" }, // 默认第一项
4038
+ { name: "否", value: "none" }, // 默认第一项
4039
+ { name: "导出: 前端需求", value: "front" },
4017
4040
  { name: "导出: 后端需求", value: "back" },
4018
4041
  { name: "导出: 全部需求", value: "all" },
4019
- { name: "否", value: "none" },
4020
4042
  ],
4021
4043
  },
4022
4044
  ]));
@@ -4042,7 +4064,7 @@ function registerStoryCommand(program, { markHandled, addHelpExample }) {
4042
4064
  .option("--username <name>", "禅道账号(story命令用)")
4043
4065
  .option("--password <pwd>", "禅道密码(story命令用, 日常建议用--cache)")
4044
4066
  .option("--cache", "凭据静默走缓存")
4045
- .option("--product <id>", "禅道产品ID(plan用, 不传则交互选择)")
4067
+ .option("--product <id>", "禅道产品计划ID(plan用, 即productplan-browse-<ID>.html中的ID, 不传则交互选择)")
4046
4068
  .option("--export <scope>", "plan导出范围: front|back|all(传了跳过收尾询问直接导出)")
4047
4069
  .option("--type <t>", "task任务类型: front|back(传了跳过类型询问与确认, 一键创建)")
4048
4070
  .option("--split", "task时创建父任务并自动拆分子任务(>8h, 每个8h末个补余); subtask时跳过确认直接拆")
@@ -4061,6 +4083,7 @@ function registerStoryCommand(program, { markHandled, addHelpExample }) {
4061
4083
  // help示例经 helpers 注入 index.js 的 helpExamples, 自动参与列对齐
4062
4084
  addHelpExample("fe-it-beta story login", "禅道登录, 缓存zentaosid");
4063
4085
  addHelpExample("fe-it-beta story plan --product 87", "选计划看列表, 交互选导出");
4086
+ addHelpExample("fe-it-beta story plan 29", "29=产品计划ID(productplan-browse-29.html), 拉计划列表再选取");
4064
4087
  addHelpExample("fe-it-beta story detail 需求ID", "按需求ID查看详情");
4065
4088
  addHelpExample("fe-it-beta story task 项目ID", "选项目勾选需求, 交互建禅道任务");
4066
4089
  addHelpExample(
@@ -2193,7 +2193,7 @@ async function selectProduct(zentaosid, cliOpts) {
2193
2193
  name: `#${p.id} ${p.name}`,
2194
2194
  value: p.id,
2195
2195
  })),
2196
- { name: "输入其他产品ID...", value: "__INPUT__" },
2196
+ { name: "输入其他产品-计划ID(如: https://zentao.hongxinshop.com/zentao/productplan-browse-29.html 中的29)", value: "__INPUT__" },
2197
2197
  ],
2198
2198
  },
2199
2199
  ]);
@@ -2204,11 +2204,11 @@ async function selectProduct(zentaosid, cliOpts) {
2204
2204
  }
2205
2205
  const { id } = await inquirer.prompt([
2206
2206
  {
2207
- message: "请输入产品ID",
2207
+ message: "请输入产品计划ID(如: https://zentao.hongxinshop.com/zentao/productplan-browse-29.html 中的29)",
2208
2208
  name: "id",
2209
2209
  type: "input",
2210
2210
  validate: (val) =>
2211
- /^\d+$/.test(String(val).trim()) || "请输入数字产品ID",
2211
+ /^\d+$/.test(String(val).trim()) || "请输入数字产品计划ID",
2212
2212
  },
2213
2213
  ]);
2214
2214
  return resolveProduct(zentaosid, id);
@@ -2832,7 +2832,7 @@ function writeZentaoTasks({ plan, scope, stories, storyDetails, products, failed
2832
2832
  }
2833
2833
  }
2834
2834
 
2835
- // 计划选取交互: 关键词过滤 > 命中1条自动选中 > rawlist选取
2835
+ // 计划选取交互: 关键词过滤(标题) > 命中1条自动选中 > rawlist选取
2836
2836
  async function selectPlan(plans, keyword) {
2837
2837
  if (keyword && String(keyword).trim()) {
2838
2838
  const kw = String(keyword).trim().toLowerCase();
@@ -2877,10 +2877,15 @@ async function loadPlanList(zentaosid, productID) {
2877
2877
  return plans;
2878
2878
  }
2879
2879
 
2880
- // plan动作: 选中计划后展示计划信息 + 关联需求列表(ID供detail用)
2880
+ // plan动作: 选中计划后展示计划信息 + 关联需求列表(ID供detail用); 返回关联需求(供调用方判断空计划)
2881
2881
  async function showPlanDetail(zentaosid, selected) {
2882
2882
  Loading.start("拉取计划详情中...");
2883
2883
  const { plan, stories } = await fetchPlanDetail(zentaosid, selected.id);
2884
+ // 0条需求时详情/工时统计均无意义, 一句话结束
2885
+ if (!stories.length) {
2886
+ Loading.succeed(`计划 #${plan.id} ${plan.title} 无关联需求`);
2887
+ return stories;
2888
+ }
2884
2889
  Loading.succeed("拉取计划详情成功");
2885
2890
  console.log(chalk.bold("\n计划信息"));
2886
2891
  console.table([
@@ -2935,6 +2940,7 @@ async function showPlanDetail(zentaosid, selected) {
2935
2940
  console.log(
2936
2941
  chalk.blue("提示: ID 供 fe-it-beta story detail <需求ID> 使用(也可传需求url)")
2937
2942
  );
2943
+ return stories;
2938
2944
  }
2939
2945
 
2940
2946
  // 导出动作(plan收尾交互使用): 按范围过滤需求, 拉详情并产出 .zentao-<计划名>/ + 工作区
@@ -3103,7 +3109,7 @@ async function storyMain(action, cliOpts) {
3103
3109
  if (!match) {
3104
3110
  console.log(
3105
3111
  chalk.red(
3106
- "请传入项目ID(从禅道项目需求页url复制), 如: fe-it-beta story task 2915 或 fe-it-beta story task https://zentao.hongxinshop.com/zentao/project-story-2915.html"
3112
+ "请传入项目-需求url的ID, 如: fe-it-beta story task 2915 或 fe-it-beta story task https://zentao.hongxinshop.com/zentao/project-story-2915.html"
3107
3113
  )
3108
3114
  );
3109
3115
  process.exit(1);
@@ -3703,7 +3709,7 @@ async function storyMain(action, cliOpts) {
3703
3709
  if (!match) {
3704
3710
  console.log(
3705
3711
  chalk.red(
3706
- "请传入项目ID(从禅道任务列表url复制), 如: fe-it-beta story close 2915 或 fe-it-beta story close https://zentao.hongxinshop.com/zentao/project-task-2915-myinvolved.html"
3712
+ "请传入项目-任务-由我参与url的ID, 如: fe-it-beta story close 2915 或 fe-it-beta story close https://zentao.hongxinshop.com/zentao/project-task-2915-myinvolved.html"
3707
3713
  )
3708
3714
  );
3709
3715
  process.exit(1);
@@ -3975,10 +3981,26 @@ async function storyMain(action, cliOpts) {
3975
3981
  // plan: 产品选择 -> 计划选取 -> 展示 -> 导出(--export直通一键场景, 不传则收尾交互)
3976
3982
  const session = await ensureZentaoSession(cliOpts);
3977
3983
  await session.request(async (zentaosid) => {
3978
- const product = await selectProduct(zentaosid, cliOpts);
3984
+ // 位置参数纯数字 = 产品计划ID(productplan-browse-<ID>.html 中的ID), 等同 --product 直通
3985
+ const nameArg = String(cliOpts.name || "").trim();
3986
+ const productIdArg = /^\d+$/.test(nameArg) ? nameArg : "";
3987
+ const product = await selectProduct(zentaosid, {
3988
+ ...cliOpts,
3989
+ product: cliOpts.product || productIdArg,
3990
+ });
3979
3991
  const plans = await loadPlanList(zentaosid, product.id);
3980
- const selected = await selectPlan(plans, cliOpts.name);
3981
- await showPlanDetail(zentaosid, selected);
3992
+ // 该产品无计划时后续选取/详情/导出均无意义, 提前结束
3993
+ if (!plans.length) {
3994
+ console.log(chalk.yellow(`产品 #${product.id} ${product.name} 下无计划, 结束`));
3995
+ return;
3996
+ }
3997
+ // 纯数字已用作产品ID, 非数字关键词仍按计划标题过滤
3998
+ const selected = await selectPlan(plans, productIdArg ? "" : cliOpts.name);
3999
+ const stories = await showPlanDetail(zentaosid, selected);
4000
+ // 空计划提示已在showPlanDetail输出, 直接结束不进导出询问
4001
+ if (!stories.length) {
4002
+ return;
4003
+ }
3982
4004
  // 导出范围: --export参数直通(一键场景) > 收尾交互(默认前端)
3983
4005
  let exportChoice;
3984
4006
  if (cliOpts.export !== undefined) {
@@ -3987,14 +4009,14 @@ async function storyMain(action, cliOpts) {
3987
4009
  } else {
3988
4010
  ({ exportChoice } = await inquirer.prompt([
3989
4011
  {
3990
- message: "是否导出需求文件(按计划生成 .zentao-<计划名>目录)?",
4012
+ message: `是否导出需求文件(按计划生成 .zentao-<计划名>目录, ${chalk.red("目前导出功能只适用于claude code批量建任务")})?`,
3991
4013
  name: "exportChoice",
3992
4014
  type: "list",
3993
4015
  choices: [
3994
- { name: "导出: 前端需求", value: "front" }, // 默认第一项
4016
+ { name: "否", value: "none" }, // 默认第一项
4017
+ { name: "导出: 前端需求", value: "front" },
3995
4018
  { name: "导出: 后端需求", value: "back" },
3996
4019
  { name: "导出: 全部需求", value: "all" },
3997
- { name: "否", value: "none" },
3998
4020
  ],
3999
4021
  },
4000
4022
  ]));
@@ -4020,7 +4042,7 @@ function registerStoryCommand(program, { markHandled, addHelpExample }) {
4020
4042
  .option("--username <name>", "禅道账号(story命令用)")
4021
4043
  .option("--password <pwd>", "禅道密码(story命令用, 日常建议用--cache)")
4022
4044
  .option("--cache", "凭据静默走缓存")
4023
- .option("--product <id>", "禅道产品ID(plan用, 不传则交互选择)")
4045
+ .option("--product <id>", "禅道产品计划ID(plan用, 即productplan-browse-<ID>.html中的ID, 不传则交互选择)")
4024
4046
  .option("--export <scope>", "plan导出范围: front|back|all(传了跳过收尾询问直接导出)")
4025
4047
  .option("--type <t>", "task任务类型: front|back(传了跳过类型询问与确认, 一键创建)")
4026
4048
  .option("--split", "task时创建父任务并自动拆分子任务(>8h, 每个8h末个补余); subtask时跳过确认直接拆")
@@ -4039,6 +4061,7 @@ function registerStoryCommand(program, { markHandled, addHelpExample }) {
4039
4061
  // help示例经 helpers 注入 index.js 的 helpExamples, 自动参与列对齐
4040
4062
  addHelpExample("fe-it-beta story login", "禅道登录, 缓存zentaosid");
4041
4063
  addHelpExample("fe-it-beta story plan --product 87", "选计划看列表, 交互选导出");
4064
+ addHelpExample("fe-it-beta story plan 29", "29=产品计划ID(productplan-browse-29.html), 拉计划列表再选取");
4042
4065
  addHelpExample("fe-it-beta story detail 需求ID", "按需求ID查看详情");
4043
4066
  addHelpExample("fe-it-beta story task 项目ID", "选项目勾选需求, 交互建禅道任务");
4044
4067
  addHelpExample(
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "i18-fe-automator-beta",
3
- "version": "2.1.5",
3
+ "version": "2.1.6",
4
4
  "description": "i18-fe-automator-beta内测版本,只用于key: ''替换、检测中文规则等",
5
5
  "type": "module",
6
6
  "bin": {