@xqyz/xq-cli 0.3.19 → 0.3.21

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
@@ -10,7 +10,27 @@
10
10
 
11
11
  ## [Unreleased]
12
12
 
13
- ## [xq-cli 0.3.19 / WorkBuddy Plugin 0.10.38 / SkillHub 1.0.20] - 2026-09-02(发布准备中)
13
+ ## [xq-cli 0.3.21 / WorkBuddy Plugin 0.10.40 / SkillHub 1.0.20] - 2026-09-02(npm 发布准备,SkillHub 不变)
14
+
15
+ ### WorkBuddy 供应方式原任务恢复
16
+
17
+ - 新增只读 `outline wait`:只轮询既有 `cid/uuid`,不会再次调用 `preSet` 或 `multiBiddingChoice`,使 WorkBuddy 在供应方式提交后与 Web 端一样继续原解析任务。
18
+ - WorkBuddy 在 `/proxy/savebidderRole` 返回成功后直接进入原任务等待,不再把短暂滞后的 `bidderIdentity_judgement` 镜像当作提交失败,也不会再次提交供应方式。
19
+ - 当评分正文已经存在时,本地忽略矛盾的 `score_judgement=0` 镜像,避免相同招标文件在 WorkBuddy 中误弹“补充评分细则”。
20
+ - 新增旧镜像持续不变、无第二次触发型 outline、已有评分正文三类回归测试。
21
+ - SkillHub 指令与交互协议未改变,继续使用 `1.0.20`,不生成或自动上传新版本。
22
+
23
+ ## [xq-cli 0.3.20 / WorkBuddy Plugin 0.10.39 / SkillHub 1.0.20] - 2026-09-02(npm 发布准备,SkillHub 不变)
24
+
25
+ ### 供应方式并发回写根因修复
26
+
27
+ - 根据生产完整链路日志定位到后端多个异步解析线程使用旧快照覆盖用户已提交的 `bidderIdentity` 状态;修复采用持久化 `bidderRole` 标记和带条件的原子降级写入,避免依赖单机 `synchronized` 或轮询时序。
28
+ - `savebidderRole` 现在一次写入用户选择、顶层兼容状态和嵌套状态;晚到解析结果仍可更新其他字段,但无权把已确认供应方式从 `1` 回滚到 `0`。
29
+ - xq-cli 在显式收到已提交的 `--bidder-role` 后继续同一任务时,会忽略该字段的旧解析快照;WorkBuddy 同步携带选择并保留回归诊断,不再重新展示供应方式卡。
30
+ - 新增后端并发写保护、供应方式持久化、CLI 旧状态容错和 WorkBuddy 续跑参数回归测试。
31
+ - SkillHub 指令与交互协议未改变,继续使用 `1.0.20`,不生成或自动上传新版本。
32
+
33
+ ## [xq-cli 0.3.19 / WorkBuddy Plugin 0.10.38 / SkillHub 1.0.20] - 2026-09-02(npm 已发布,SkillHub 待手动上传)
14
34
 
15
35
  ### 供应方式提交闭环与诊断
16
36
 
@@ -18,7 +38,8 @@
18
38
  - WorkBuddy 将“选择已接收”“接口返回成功”“后台权威状态已生效”拆为三个阶段;只有 `bidderIdentity_judgement` 不再为 `0` 后才继续大纲。
19
39
  - 接口返回成功但状态在限定窗口内未改变时,以 `BIDDER_ROLE_NOT_APPLIED` 阻塞并保留完整诊断;提交进程异常退出则返回 `BIDDER_ROLE_SUBMIT_RESULT_UNKNOWN`,不自动重复提交。
20
40
  - MCP App 不再提前显示“已提交并继续下一步”,改为提示正在提交后台并核验是否生效。
21
- - Plugin 固定依赖 `@xqyz/xq-cli@0.3.19`,MCP/Connector 基线同步到 `0.10.38`;SkillHub `1.0.20` 仅生成待手动上传包。
41
+ - Plugin 固定依赖 `@xqyz/xq-cli@0.3.19`,MCP/Connector 基线同步到 `0.10.38`;Plugin `latest` 与 `workbuddy-stable` 均指向 `0.10.38`。
42
+ - 本机 WorkBuddy 已写入精确 Plugin `0.10.38` 并同步 Skill `1.0.20`,MCP 启动握手通过;SkillHub `1.0.20` 校验包已生成,仍由发布人手动上传。
22
43
 
23
44
  ## [xq-cli 0.3.18 / WorkBuddy Plugin 0.10.37 / SkillHub 1.0.19] - 2026-09-02(npm 已发布,SkillHub 待手动上传)
24
45
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xqyz/xq-cli",
3
- "version": "0.3.19",
3
+ "version": "0.3.21",
4
4
  "description": "CLI for xique bid-book task workflows",
5
5
  "type": "module",
6
6
  "scripts": {
package/src/cli.mjs CHANGED
@@ -429,6 +429,8 @@ async function main() {
429
429
  await handleOutlineView(parsed, state);
430
430
  } else if (isOutlineAction(parsed, ['update', 'edit'])) {
431
431
  await handleOutlineUpdate(parsed, state);
432
+ } else if (isOutlineAction(parsed, ['wait', 'resume'])) {
433
+ await handleOutlineWait(parsed, state);
432
434
  } else {
433
435
  await handleOutline(parsed, state);
434
436
  }
@@ -502,6 +504,7 @@ Commands
502
504
  basis-select Save the parsing-stage outline basis for the current task
503
505
  supplement Submit missing requirement / bill / score / directory content and continue the same task
504
506
  outline Pre-set task params and trigger outline generation
507
+ outline wait Wait for the current outline without triggering parsing again
505
508
  outline view View the current outline without triggering generation
506
509
  outline update Update the current outline from an edited JSON file
507
510
  directory view View the complete generated directory tree without triggering generation
@@ -551,6 +554,7 @@ Examples
551
554
  xq-cli outline --cid <cid> --wait --ignore-missing-bill
552
555
  xq-cli outline --cid <cid> --wait --reference-type 0
553
556
  xq-cli outline --cid <cid> --page-scope 3 --writing-quality flagship --theme-style 4 --table-quantity rich --plan-mode quick --wait
557
+ xq-cli outline wait --cid <cid> --uuid <uuid> --bidder-role 2 --wait
554
558
  xq-cli outline view --cid <cid>
555
559
  xq-cli outline view --cid <cid> --out D:\\output\\outline.json
556
560
  xq-cli outline update --cid <cid> --file D:\\output\\outline.json --dry-run --json
@@ -2045,6 +2049,37 @@ async function handleOutline(args, state) {
2045
2049
  return withGenerationLock(cid, 'generation', () => handleOutlineUnlocked(args, state));
2046
2050
  }
2047
2051
 
2052
+ async function handleOutlineWait(args, state) {
2053
+ const cid = resolveCid(args, state);
2054
+ return withGenerationLock(cid, 'generation', () => handleOutlineWaitUnlocked(args, state));
2055
+ }
2056
+
2057
+ async function handleOutlineWaitUnlocked(args, state) {
2058
+ validateOutlineReferenceTypeOption(args);
2059
+ const client = createAuthorizedClient(args, state);
2060
+ const cid = resolveCid(args, state);
2061
+ const snapshot = await hydrateTaskSnapshot(client, state, cid);
2062
+ const uuid = stringOption(args, 'uuid', snapshot.uuid);
2063
+ const planMode = enumOption(args, 'planMode', PLAN_MODE_OPTIONS, snapshot.planMode ?? 0);
2064
+ const waitResult = Number(planMode) === 1
2065
+ ? await waitForPlanningAnalysisReady(client, state, cid, uuid, args)
2066
+ : await waitForOutlineReady(client, state, cid, uuid, args);
2067
+ const resultPayload = {
2068
+ command: 'outline-wait',
2069
+ cid,
2070
+ uuid,
2071
+ planMode,
2072
+ triggerMode: 'existing-task',
2073
+ waitResult,
2074
+ };
2075
+ outputResult(args, resultPayload, [
2076
+ `outline wait completed: cid=${cid}`,
2077
+ 'triggerMode: existing-task (preSet and multiBiddingChoice were not called)',
2078
+ `phase: ${waitResult?.phase || 'unknown'}`,
2079
+ ]);
2080
+ return resultPayload;
2081
+ }
2082
+
2048
2083
  async function handleOutlineUnlocked(args, state) {
2049
2084
  validateOutlineReferenceTypeOption(args);
2050
2085
  const client = createAuthorizedClient(args, state);
@@ -3347,7 +3382,7 @@ async function waitForOutlineReady(client, state, cid, uuid, args, initialSubmit
3347
3382
  progressLabel: `unsupported_parse_state=${String(parseStatus.success)}`,
3348
3383
  };
3349
3384
  }
3350
- const issues = collectParseIssues(parseStatus);
3385
+ const issues = collectContinuationParseIssues(parseStatus, args);
3351
3386
  const unsupportedIssues = collectUnsupportedParseIssues(parseStatus);
3352
3387
 
3353
3388
  if (parseSuccess === 4 && unsupportedIssues.length > 0) {
@@ -3561,7 +3596,7 @@ async function waitForPlanningAnalysisReady(client, state, cid, uuid, args) {
3561
3596
  '[TASK_PARSE_NOT_ADVANCED] 规划模式仍处于等待标包/EPC选择(success=2),未进入解析阶段,已停止等待。',
3562
3597
  );
3563
3598
  }
3564
- const issues = collectParseIssues(parseStatus);
3599
+ const issues = collectContinuationParseIssues(parseStatus, args);
3565
3600
  const unsupportedIssues = collectUnsupportedParseIssues(parseStatus);
3566
3601
  if (parseSuccess === 4 && unsupportedIssues.length > 0) {
3567
3602
  return {
@@ -4231,12 +4266,47 @@ function collectParseIssues(result) {
4231
4266
  ];
4232
4267
 
4233
4268
  return issueKeys.filter(key => {
4269
+ if (key === 'score_judgement' && hasMeaningfulScoringContent(result)) {
4270
+ return false;
4271
+ }
4234
4272
  const directValue = result[key];
4235
4273
  const nestedValue = result.judgement_status?.[key.replace('_judgement', '')];
4236
4274
  return isFalsyStatus(directValue ?? nestedValue ?? '1');
4237
4275
  });
4238
4276
  }
4239
4277
 
4278
+ function hasMeaningfulScoringContent(result = {}) {
4279
+ return [
4280
+ result.rating,
4281
+ result.ratingContent,
4282
+ result.rating_content,
4283
+ result.scoreContent,
4284
+ result.score_content,
4285
+ ].some(value => meaningfulTextLength(value) >= 20);
4286
+ }
4287
+
4288
+ function meaningfulTextLength(value) {
4289
+ if (typeof value !== 'string') return 0;
4290
+ return value
4291
+ .replace(/<[^>]*>/g, ' ')
4292
+ .replace(/&(?:nbsp|ensp|emsp|#160);/gi, ' ')
4293
+ .replace(/\s+/g, '')
4294
+ .trim()
4295
+ .length;
4296
+ }
4297
+
4298
+ function collectContinuationParseIssues(result, args = {}) {
4299
+ const issues = collectParseIssues(result);
4300
+ const bidderRole = Number(args.bidderRole);
4301
+ if (![1, 2].includes(bidderRole)) {
4302
+ return issues;
4303
+ }
4304
+ // A bidder role reaches this command only after the dedicated submission
4305
+ // endpoint has accepted an explicit user selection. Concurrent parser
4306
+ // snapshots may still expose the old zero flag; do not reopen that gate.
4307
+ return issues.filter(issue => issue !== 'bidderIdentity_judgement');
4308
+ }
4309
+
4240
4310
  function isFalsyStatus(value) {
4241
4311
  if (value === false || value === 0) return true;
4242
4312
  const normalized = String(value ?? '').trim().toLowerCase();