openfeel 1.3.2 → 1.3.3

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.
Files changed (54) hide show
  1. package/README.en.md +1 -0
  2. package/README.md +2 -1
  3. package/README.zh-CN.md +1 -0
  4. package/dist/commands/flow.d.ts.map +1 -1
  5. package/dist/commands/flow.js +98 -14
  6. package/dist/commands/flow.js.map +1 -1
  7. package/dist/commands/instructions.js +1 -1
  8. package/dist/commands/instructions.js.map +1 -1
  9. package/dist/commands/plan.d.ts.map +1 -1
  10. package/dist/commands/plan.js +9 -1
  11. package/dist/commands/plan.js.map +1 -1
  12. package/dist/core/archive/merge.js +8 -8
  13. package/dist/core/archive/merge.js.map +1 -1
  14. package/dist/core/artifact-graph/resolver.d.ts.map +1 -1
  15. package/dist/core/artifact-graph/resolver.js +8 -0
  16. package/dist/core/artifact-graph/resolver.js.map +1 -1
  17. package/dist/core/config.d.ts +12 -0
  18. package/dist/core/config.d.ts.map +1 -1
  19. package/dist/core/config.js +25 -3
  20. package/dist/core/config.js.map +1 -1
  21. package/dist/core/flow-manager.d.ts +26 -1
  22. package/dist/core/flow-manager.d.ts.map +1 -1
  23. package/dist/core/flow-manager.js +112 -9
  24. package/dist/core/flow-manager.js.map +1 -1
  25. package/dist/core/i18n-data/en.d.ts.map +1 -1
  26. package/dist/core/i18n-data/en.js +12 -0
  27. package/dist/core/i18n-data/en.js.map +1 -1
  28. package/dist/core/i18n-data/zh-CN.d.ts.map +1 -1
  29. package/dist/core/i18n-data/zh-CN.js +12 -0
  30. package/dist/core/i18n-data/zh-CN.js.map +1 -1
  31. package/dist/core/plan/stage.d.ts.map +1 -1
  32. package/dist/core/plan/stage.js +3 -1
  33. package/dist/core/plan/stage.js.map +1 -1
  34. package/dist/core/template-loader.d.ts.map +1 -1
  35. package/dist/core/template-loader.js +29 -18
  36. package/dist/core/template-loader.js.map +1 -1
  37. package/dist/core/update.d.ts.map +1 -1
  38. package/dist/core/update.js +16 -5
  39. package/dist/core/update.js.map +1 -1
  40. package/dist/core/view/entry.d.ts +13 -4
  41. package/dist/core/view/entry.d.ts.map +1 -1
  42. package/dist/core/view/entry.js +40 -16
  43. package/dist/core/view/entry.js.map +1 -1
  44. package/dist/core/workspace/structure.d.ts +21 -1
  45. package/dist/core/workspace/structure.d.ts.map +1 -1
  46. package/dist/core/workspace/structure.js +30 -11
  47. package/dist/core/workspace/structure.js.map +1 -1
  48. package/dsh/ADAPTER.md +14 -7
  49. package/dsh/README.md +3 -3
  50. package/dsh/lib/adapters/dashboard.js +2 -2
  51. package/dsh/lib/guard.js +78 -11
  52. package/dsh/lib/tools/shared.js +28 -14
  53. package/dsh/package.json +2 -2
  54. package/package.json +1 -1
package/README.en.md CHANGED
@@ -112,6 +112,7 @@ openfeel setup --tools dsh # 2. Deploy into DSH profiles (all by default; use
112
112
  - **Choose and switch**: `openfeel plan stage add <name> --workflow-mode full|fast|general` (new stage); `openfeel stage set <stage> --workflow-mode full|fast|general` (per-stage switch); `openfeel config set defaults.workflow_mode full|fast|general` (project-level default memory). **`general` is requirement-level only** (since v1.2.1; see [docs/VERSIONS.md](docs/VERSIONS.md)).
113
113
  - **Inspect (read-only)**: `openfeel flow mode [--stage <id>] [--json]` (current mode + source); `openfeel flow phases [--mode full|fast|general] [--stage <id>]` (view the transition table per mode).
114
114
  - **Auto-advance enabled by default**: new deployments default to `auto_advance: enabled` (since v1.1.7); to disable: `openfeel config set auto_advance disabled`.
115
+ - **v1.3.3 fixes (fast-mode structural gaps)**: fast chain gains a **rework edge** `review_passed → exec_running` (`flow advance --reason "<fix note>"` is audited); fast stages without ops can attach **stage-level REVs** via `flow review add --stage <id>`; the write guard now allows **new files in an uninitialized workspace** and `docs/**` / doc-note paths (source still requires `exec_running`); `plan stage add` now **focuses the stage atomically**. Stage-ID normalization and `plan/` directory mapping: see [docs/commands.md](docs/commands.md).
115
116
 
116
117
  Upgrade/migration notes: see [docs/GETTING_STARTED.md](docs/GETTING_STARTED.md) ("工作流模式(full / fast)与自动推进" section, Chinese) and [CHANGELOG.md](CHANGELOG.md) `[1.1.7]`.
117
118
 
package/README.md CHANGED
@@ -10,7 +10,7 @@
10
10
 
11
11
  OpenFeel 是一个 TypeScript CLI 工具,为 AI Agent 开发提供端到端的流程治理。核心理念:**「提示词瘦身,流程入工具」** —— Agent 不靠读长文本理解流程,而是通过 `flow.json` 获取当前状态和下一步指令。
12
12
 
13
- > 📦 **最新版本 v1.3.2**(DSH 激活与写入门禁) — 新增 `ctx.tools.guard` 写入门禁与非 OpenFeel 工作区 bootstrap:未初始化时默认 `enforce=always` 会拒绝文件写入,直到 `openfeel init`;`project` / `off` 可 opt-out。版本能力概览见 [docs/VERSIONS.md](docs/VERSIONS.md)。
13
+ > 📦 **最新版本 v1.3.3**(fast 模式结构性修复) — 修复反馈中 13 项结构性问题(12 次实测拒绝):审查条目可挂阶段(无 op 的 fast 场景)、fast 新增返工边 `review_passed → exec_running`、写入门禁按风险分层(空仓新增 / doc-note 放行)、`plan stage add` 原子聚焦、status.md 派生写、阶段 ID↔目录映射文档化。版本能力概览见 [docs/VERSIONS.md](docs/VERSIONS.md)。
14
14
 
15
15
  > 📌 本项目基于 [AI_Prompt](https://github.com/Liuary/AI_Prompt) 开发,参考了 [OpenSpec](https://github.com/Fission-AI/OpenSpec) 等工具的设计思路。
16
16
 
@@ -82,5 +82,6 @@ openfeel setup --tools dsh # 2. 部署到 DSH profile(默认全部;可 --
82
82
  - **选择与切换**:`openfeel plan stage add <name> --workflow-mode full|fast|general`(新建阶段);`openfeel stage set <stage> --workflow-mode full|fast|general`(单阶段切回);`openfeel config set defaults.workflow_mode full|fast|general`(项目默认记忆)。**`general` 仅需求级合法**(v1.2.1 起,见 [docs/VERSIONS.md](docs/VERSIONS.md))。
83
83
  - **查看(只读)**:`openfeel flow mode [--stage <id>] [--json]`(当前模式 + 来源);`openfeel flow phases [--mode full|fast|general] [--stage <id>]`(按模式查看转移表)。
84
84
  - **默认自动推进开启**:新部署默认 `auto_advance: enabled`(v1.1.7 起);如需关闭:`openfeel config set auto_advance disabled`。
85
+ - **v1.3.3 修复(fast 结构性缺口)**:fast 快线新增**返工边** `review_passed → exec_running`(`flow advance --reason` 落审计);无 op 的 fast 阶段可用 `flow review add --stage <id>` 挂**阶段级 REV**;写入门禁对**空仓新增文件**与 `docs/**` / doc-note 放行(源码仍要求 `exec_running`);`plan stage add` 后**原子聚焦**。阶段 ID 归一化与 `plan/` 目录映射见 [docs/commands.md](docs/commands.md)。
85
86
 
86
87
  完整中文文档:[README.zh-CN.md](README.zh-CN.md)(含 Claude Code / DSH 适配器、命令参考与核心概念)
package/README.zh-CN.md CHANGED
@@ -112,6 +112,7 @@ openfeel setup --tools dsh # 2. 部署到 DSH profile(默认全部;可 --
112
112
  - **选择与切换**:`openfeel plan stage add <name> --workflow-mode full|fast|general`(新建阶段);`openfeel stage set <stage> --workflow-mode full|fast|general`(单阶段切回);`openfeel config set defaults.workflow_mode full|fast|general`(项目默认记忆)。**`general` 仅需求级合法**(v1.2.1 起,见 [docs/VERSIONS.md](docs/VERSIONS.md))。
113
113
  - **查看(只读)**:`openfeel flow mode [--stage <id>] [--json]`(当前模式 + 来源);`openfeel flow phases [--mode full|fast|general] [--stage <id>]`(按模式查看转移表)。
114
114
  - **默认自动推进开启**:新部署默认 `auto_advance: enabled`(v1.1.7 起);如需关闭:`openfeel config set auto_advance disabled`。
115
+ - **v1.3.3 修复(fast 结构性缺口)**:fast 快线新增**返工边** `review_passed → exec_running`(`flow advance --reason "<整改原因>"` 落审计);无 op 的 fast 阶段可用 `flow review add --stage <id>` 挂**阶段级 REV**;写入门禁对**空仓新增文件**与 `docs/**` / doc-note 放行(源码仍要求 `exec_running`);`plan stage add` 后**原子聚焦**活跃阶段。阶段 ID 归一化与 `plan/` 目录映射见 [docs/commands.md](docs/commands.md)。
115
116
 
116
117
  升级迁移提示见 [docs/GETTING_STARTED.md](docs/GETTING_STARTED.md)「工作流模式(full / fast)与自动推进」小节与 [CHANGELOG.md](CHANGELOG.md) `[1.1.7]`。
117
118
 
@@ -1 +1 @@
1
- {"version":3,"file":"flow.d.ts","sourceRoot":"","sources":["../../src/commands/flow.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAsBpC,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAqgF1D;AAED;;;;;;GAMG;AACH,wBAAgB,wBAAwB,CAAC,aAAa,EAAE,OAAO,EAAE,iBAAiB,EAAE,OAAO,GAAG,QAAQ,GAAG,MAAM,CAE9G"}
1
+ {"version":3,"file":"flow.d.ts","sourceRoot":"","sources":["../../src/commands/flow.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAsBpC,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAslF1D;AAED;;;;;;GAMG;AACH,wBAAgB,wBAAwB,CAAC,aAAa,EAAE,OAAO,EAAE,iBAAiB,EAAE,OAAO,GAAG,QAAQ,GAAG,MAAM,CAE9G"}
@@ -4,7 +4,7 @@ import { resolve } from 'node:path';
4
4
  import { FlowManager, isFlowConcurrentError, normalizeAgentName, detectFillState, MODE_DERIVED_TIER, listStageBlockingOpenReviews, PHASE_PATH_MAX_DEPTH } from '../core/flow-manager.js';
5
5
  import { PipelinePhaseSchema, PIPELINE_PHASES, REVIEW_TIERS, TIER_PRUNED_PHASES, DEFAULT_EFFECTIVE_REVIEW_TIER } from '../core/pipeline-schema.js';
6
6
  import { validateStageId, suggestStageId, normalizeStageId, findStageStatusPath, isArchivedVersionOf } from '../core/plan/path.js';
7
- import { getConfigFieldLegalValues, setConfigValue } from '../core/config.js';
7
+ import { getConfigFieldLegalValues, setConfigValue, readExplicitConfigDefaults } from '../core/config.js';
8
8
  import { parse as parseYaml } from 'yaml';
9
9
  import { MetricsStore, PHASE_AGENT_MAP } from '../core/metrics.js';
10
10
  import { runtimeLog } from '../core/runtime-log.js';
@@ -160,9 +160,19 @@ export function registerFlowCommand(program) {
160
160
  const rawStage = data.pipeline.current.stage || '';
161
161
  // 双键回退(与 core resolveCurrentPhase / advanceStagePhase 同范式):归一化键优先,其次原始键
162
162
  const normalized = rawStage ? (normalizeStageId(rawStage) ?? rawStage) : '';
163
- const activeKey = rawStage
163
+ let activeKey = rawStage
164
164
  ? (data.stages[normalized] ? normalized : (data.stages[rawStage] ? rawStage : null))
165
165
  : null;
166
+ let derivedFromStages = false;
167
+ // v1.3.3 P1-1:current 为空时从 stages **只读派生**活跃阶段(tie-break:插入序首个非 done)。
168
+ // 不写盘、不改 current;命中时在 guards 末位追加派生提示。
169
+ if (!activeKey) {
170
+ const derived = Object.entries(data.stages).find(([, stage]) => stage.phase !== 'done');
171
+ if (derived) {
172
+ activeKey = derived[0];
173
+ derivedFromStages = true;
174
+ }
175
+ }
166
176
  let phase = null;
167
177
  let phaseLabel = null;
168
178
  let responsibleAgent = 'feel'; // 无活跃阶段显式约定
@@ -188,8 +198,9 @@ export function registerFlowCommand(program) {
188
198
  blockingReviews = listStageBlockingOpenReviews(data.reviews, activeKey).map((r) => ({
189
199
  id: r.id, title: r.title, priority: r.priority, op: r.op,
190
200
  }));
191
- // 守卫顺序契约固定(§12.1):auto-advance → blocking → 无目标 → 档位差
192
- if (mgr.resolveEffectiveConfig()['auto_advance']?.value === 'disabled') {
201
+ // 守卫顺序契约固定(§12.1):auto-advance → blocking → 无目标 → 档位差 → 配置组合 → 派生
202
+ const effectiveCfg = mgr.resolveEffectiveConfig();
203
+ if (effectiveCfg['auto_advance']?.value === 'disabled') {
193
204
  guards.push(t('flow.next.guardAutoAdvanceDisabled', lang));
194
205
  }
195
206
  if (blockingReviews.length > 0) {
@@ -201,6 +212,18 @@ export function registerFlowCommand(program) {
201
212
  if (reviewTier !== effectiveReviewTier) {
202
213
  guards.push(t('flow.next.guardTierDiffTmpl', lang, { display: reviewTier, effective: effectiveReviewTier }));
203
214
  }
215
+ // P1-4:execution_mode × auto_advance **显式**组合不一致 → 显式提示。
216
+ // 仅告警「auto 但关闭自动推进」这一**静默无效**组合;manual+enabled 是内置默认(裁定 E),不告警。
217
+ const explicitCfg = readExplicitConfigDefaults(process.cwd());
218
+ if (explicitCfg.execution_mode === 'auto' && explicitCfg.auto_advance === 'disabled') {
219
+ guards.push(t('flow.next.guardConfigComboTmpl', lang, {
220
+ mode: explicitCfg.execution_mode,
221
+ auto: explicitCfg.auto_advance,
222
+ }));
223
+ }
224
+ if (derivedFromStages) {
225
+ guards.push(t('flow.next.guardDerivedStage', lang));
226
+ }
204
227
  }
205
228
  else {
206
229
  guards.push(t('flow.next.guardNoActiveStage', lang));
@@ -840,6 +863,7 @@ export function registerFlowCommand(program) {
840
863
  .option('--force', '强制执行(跳过非法 phase 校验和阶段跳跃检查,但不可绕过 REV 阻塞检查)')
841
864
  .option('--dry-run', '仅验证不执行修改(预览输出)。与 --force 组合时跳过校验但仍不执行修改')
842
865
  .option('--review-tier <tier>', '审查档位(full / lean / skip;缺省=full(零裁剪);仅显式声明时裁剪审查关,不可绕过 REV 阻塞检查)')
866
+ .option('--reason <text>', '返工原因(仅 review_passed → exec_running 时写入审计日志 rework_reason)')
843
867
  .option('--json', '以 JSON 输出错误封装(仅失败时;成功输出保持人类文本)')
844
868
  .option('--quiet', '静默非错误输出(成功确认与 Git 警告均不打印)')
845
869
  .action((options) => {
@@ -885,6 +909,17 @@ export function registerFlowCommand(program) {
885
909
  }
886
910
  // REV-005:命令层归一化(短名 → 全名),与 plan.ts / flow.ts 其它 stage 解析点同范式
887
911
  const stageArg = normalizeStageId(options.stage) ?? options.stage;
912
+ // P1-4:双开关**显式**组合不一致 → 显式告警(仅「auto + auto_advance=disabled」这一静默无效组合;
913
+ // builtin / 模板默认 manual+enabled 不告警,避免每个新项目首推即噪声)
914
+ if (!options.quiet) {
915
+ const explicitCfg = readExplicitConfigDefaults(process.cwd());
916
+ if (explicitCfg.execution_mode === 'auto' && explicitCfg.auto_advance === 'disabled') {
917
+ info(t('flow.advance.configComboWarnTmpl', lang, {
918
+ mode: explicitCfg.execution_mode,
919
+ auto: explicitCfg.auto_advance,
920
+ }));
921
+ }
922
+ }
888
923
  // 自动修复 phase/status 不一致(在 validate() 前执行)
889
924
  if (options.stage) {
890
925
  // B1 修复:dry-run 走预览模式(不写内存/不写盘),仅在非 dry-run 时 save()
@@ -1068,11 +1103,26 @@ export function registerFlowCommand(program) {
1068
1103
  let prevStepDoneMs = 0; // 多步时上一步完成时刻(i>0 的窗口基线)
1069
1104
  const totalSteps = phasePath.length;
1070
1105
  let archivedAny = false;
1106
+ let reasonUsed = false;
1071
1107
  for (let i = 0; i < totalSteps; i++) {
1072
1108
  const next = phasePath[i];
1073
1109
  let archived = false;
1074
1110
  try {
1075
- archived = mgr.advanceStagePhase(stageArg, next, 'cli', explicitTier ? { reviewTier: explicitTier } : undefined);
1111
+ // 返工原因仅写入「review_passed → exec_running」这一步(P0-3)
1112
+ const fromPhaseStep = i > 0 ? phasePath[i - 1] : startPhase;
1113
+ const reworkReason = options.reason !== undefined
1114
+ && fromPhaseStep === 'review_passed'
1115
+ && next === 'exec_running'
1116
+ ? options.reason
1117
+ : undefined;
1118
+ if (reworkReason !== undefined)
1119
+ reasonUsed = true;
1120
+ const stepOpts = {};
1121
+ if (explicitTier)
1122
+ stepOpts.reviewTier = explicitTier;
1123
+ if (reworkReason !== undefined)
1124
+ stepOpts.reworkReason = reworkReason;
1125
+ archived = mgr.advanceStagePhase(stageArg, next, 'cli', Object.keys(stepOpts).length > 0 ? stepOpts : undefined);
1076
1126
  }
1077
1127
  catch (err) {
1078
1128
  const stderrLines = [];
@@ -1149,6 +1199,16 @@ export function registerFlowCommand(program) {
1149
1199
  // 成功确认(单步与多步统一);--quiet 完全静默
1150
1200
  if (!options.quiet) {
1151
1201
  console.log(t('flow.advance.okTmpl', lang, { stage: stageArg, to: options.to }));
1202
+ // P1-5:显式回显本次生效档位(展示档位 ≠ 生效档位时不再静默)
1203
+ console.log(t('flow.advance.reviewTierTmpl', lang, {
1204
+ tier: effTier,
1205
+ source: explicitTier
1206
+ ? t('flow.advance.reviewTierSourceExplicit', lang)
1207
+ : t('flow.advance.reviewTierSourceDefault', lang),
1208
+ }));
1209
+ if (options.reason !== undefined && !reasonUsed) {
1210
+ console.warn(t('flow.advance.reasonIgnoredTmpl', lang));
1211
+ }
1152
1212
  }
1153
1213
  // git 脏区检查(安全网):默认仅 --to done 时提示;--quiet / 非 done → 完全跳过(含跳过 git 子进程)
1154
1214
  if (!options.quiet && (options.to === 'done' || archivedAny)) {
@@ -1349,23 +1409,38 @@ export function registerFlowCommand(program) {
1349
1409
  const reviewCmd = flow
1350
1410
  .command('review')
1351
1411
  .description('管理审查条目');
1352
- // flow review add --op <id> [--title] [--auto-fix <detail>]
1412
+ // flow review add [--op <id> | --stage <id>] [--title] [--auto-fix <detail>]
1353
1413
  reviewCmd
1354
1414
  .command('add')
1355
- .description('添加审查条目')
1356
- .requiredOption('--op <id>', '操作 ID(如 stage-01.op-001)')
1415
+ .description('添加审查条目(op 级或阶段级;fast 无 op 时用 --stage)')
1416
+ .option('--op <id>', '操作 ID(如 stage-01.op-001);与 --stage 二选一')
1417
+ .option('--stage <id>', '阶段 ID(未提供 --op 时挂阶段级 REV;与 --op 二选一)')
1357
1418
  .option('--title <title>', '审查标题')
1358
- .option('--auto-fix <detail>', '自动修复说明,设置后跳过 scheme_pending 直接推进到 exec_running')
1419
+ .option('--auto-fix <detail>', '自动修复说明,设置后跳过 scheme_pending 直接推进到 exec_running(必须配 --op)')
1359
1420
  .option('--blocking [boolean]', '是否阻塞流水线(默认 true)', 'true')
1360
1421
  .action((options) => {
1361
1422
  const lang = getCliLang(process.cwd());
1423
+ // 目标二选一校验(P0-2:fast 无 op 时允许挂 stage)
1424
+ if (!options.op && !options.stage) {
1425
+ const message = t('flow.review.errorNoTarget', lang);
1426
+ console.error(message);
1427
+ process.exit(1);
1428
+ return;
1429
+ }
1430
+ if (options.autoFix && !options.op) {
1431
+ const message = t('flow.review.errorAutoFixNeedsOp', lang);
1432
+ console.error(message);
1433
+ process.exit(1);
1434
+ return;
1435
+ }
1362
1436
  // 单点实现:解析 / 校验 / REV ID 分配 / 写入均由 addReviewEntry 统一处理(T37/R4)
1363
1437
  const blocking = options.blocking !== undefined
1364
1438
  ? !(options.blocking === 'false' || options.blocking === false)
1365
1439
  : true;
1366
1440
  const result = addReviewEntry(process.cwd(), {
1367
1441
  opId: options.op,
1368
- title: options.title || t('flow.review.detail', lang) + `: ${options.op}`,
1442
+ stageId: options.stage,
1443
+ title: options.title || t('flow.review.detail', lang) + `: ${options.op ?? options.stage}`,
1369
1444
  priority: 'medium',
1370
1445
  autoFixDetail: options.autoFix,
1371
1446
  blocking,
@@ -1376,6 +1451,15 @@ export function registerFlowCommand(program) {
1376
1451
  case 'notLoaded':
1377
1452
  console.error(t('common.errorNoInit', lang));
1378
1453
  break;
1454
+ case 'invalidPriority':
1455
+ console.error(t('flow.review.errorInvalidPriorityTmpl', lang, { priority: result.error.priority }));
1456
+ break;
1457
+ case 'invalidTarget':
1458
+ console.error(t('flow.review.errorNoTarget', lang));
1459
+ break;
1460
+ case 'autoFixRequiresOp':
1461
+ console.error(t('flow.review.errorAutoFixNeedsOp', lang));
1462
+ break;
1379
1463
  case 'invalidOpId':
1380
1464
  console.error(t('common.invalidOpId', lang));
1381
1465
  break;
@@ -1385,9 +1469,6 @@ export function registerFlowCommand(program) {
1385
1469
  case 'opNotFound':
1386
1470
  console.error(t('flow.review.errorOpNotFoundTmpl', lang, { opId: result.error.opId, op: result.error.op, stage: result.error.stage }));
1387
1471
  break;
1388
- case 'invalidPriority':
1389
- console.error(t('flow.review.errorInvalidPriorityTmpl', lang, { priority: result.error.priority }));
1390
- break;
1391
1472
  }
1392
1473
  process.exit(1);
1393
1474
  return;
@@ -1400,9 +1481,12 @@ export function registerFlowCommand(program) {
1400
1481
  console.log(` ` + t('flow.review.detail', lang) + `: ${options.autoFix}`);
1401
1482
  console.log(t('flow.review.autoFixPhase', lang));
1402
1483
  }
1403
- else {
1484
+ else if (options.op) {
1404
1485
  console.log(t('flow.review.addedTmpl', lang, { label: blockingLabel, revId: review.id, op: options.op }) + (options.title ? ` — ${options.title}` : ''));
1405
1486
  }
1487
+ else {
1488
+ console.log(t('flow.review.addedStageTmpl', lang, { label: blockingLabel, revId: review.id, stage: options.stage }) + (options.title ? ` — ${options.title}` : ''));
1489
+ }
1406
1490
  });
1407
1491
  // flow review resolve <rev-id>
1408
1492
  reviewCmd