prd-workflow-cli 1.4.1 → 2.0.0

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.
@@ -212,26 +212,40 @@ async function performR2Review(config, options = {}) {
212
212
  process.exit(1);
213
213
  }
214
214
 
215
+ // 检查必需文档(C1 已包含版本范围声明,C0 不再强制)
215
216
  // 检查必需文档
216
217
  const b3Path = path.join(iterationDir, 'B3_规划冻结归档.md');
217
- const c0Path = path.join(iterationDir, 'C0_版本范围声明.md');
218
- const c1Path = path.join(iterationDir, 'C1_版本需求清单.md');
218
+ const itDir = path.join(iterationDir, 'IT');
219
+ let hasItFiles = false;
219
220
 
220
- if (!await fs.pathExists(b3Path) || !await fs.pathExists(c0Path) || !await fs.pathExists(c1Path)) {
221
- console.log(chalk.red('✗ 缺少必需的 B3、C0 C1 文档'));
222
- console.log('请先创建:');
223
- console.log(' prd plan freeze (生成 B3)');
224
- console.log(' prd version create C0');
225
- console.log(' prd version create C1');
221
+ if (await fs.pathExists(itDir)) {
222
+ const files = await fs.readdir(itDir);
223
+ hasItFiles = files.some(f => f.startsWith('IT-'));
224
+ }
225
+
226
+ if (!await fs.pathExists(b3Path)) {
227
+ console.log(chalk.red('✗ 缺少 B3 规划冻结归档'));
228
+ console.log('请先执行: prd plan freeze');
229
+ if (process.env.PRD_TEST_MODE === 'true') {
230
+ throw new Error('缺少 B3 文档');
231
+ }
232
+ process.exit(1);
233
+ }
234
+
235
+ if (!hasItFiles) {
236
+ console.log(chalk.red('✗ 缺少 IT 用户故事文档'));
237
+ console.log('请先执行: prd it create "名称"');
226
238
  if (process.env.PRD_TEST_MODE === 'true') {
227
- throw new Error('缺少必需的 B3、C0 或 C1 文档');
239
+ throw new Error('缺少 IT 文档');
228
240
  }
229
241
  process.exit(1);
230
242
  }
231
243
 
244
+ const hasC0 = false; // 已废弃
245
+
232
246
  // ⭐ 支持预确认模式和非交互模式(用于测试)
233
247
  if (options.pmConfirmed) {
234
- console.log(chalk.green('✓ PM 已在对话中确认 C0C1 已填写完成,理解角色分工'));
248
+ console.log(chalk.green('✓ PM 已在对话中确认 IT 文档已填写完成,理解角色分工'));
235
249
  } else if (process.env.PRD_TEST_MODE === 'true') {
236
250
  // 测试模式:跳过交互式确认
237
251
  console.log(chalk.yellow('⚠️ 测试模式:跳过交互式确认'));
@@ -256,60 +270,70 @@ async function performR2Review(config, options = {}) {
256
270
 
257
271
  **审视对象**:
258
272
  - B3_规划冻结归档.md
259
- - C0_版本范围声明.md
260
- - C1_版本需求清单.md
273
+ - IT 用户故事 (IT-xxx-BIZ/DEV)
261
274
 
262
275
  ---
263
276
 
264
- ## 一、版本目标一致性
277
+ ## 一、业务场景闭环(以终为始)
265
278
 
266
279
  **审视标准**:
267
- - C0 的版本目标是否能在 B3 中找到明确对应
268
- - 是否存在规划外目标
280
+ - IT-BIZ 中描述的场景,是否服务于 B3 的核心问题
281
+ - IT 是否引入了 B3 没提到的新业务目标
269
282
 
270
283
  **审视结果**:
271
284
  <!-- AI 填写 -->
272
285
 
273
286
  ---
274
287
 
275
- ## 二、版本范围偏移检查
288
+ ## 二、规划范围一致性
276
289
 
277
290
  **审视标准**:
278
- - 版本包含/不包含是否符合 B3 边界
279
- - 是否存在隐性扩展或"顺手加戏"
291
+ - B2 中的每个需求项,是否都有对应的 IT
292
+ - IT 中是否包含 B2 没有的功能点(新增需求)
280
293
 
281
294
  **审视结果**:
282
295
  <!-- AI 填写 -->
283
296
 
284
297
  ---
285
298
 
286
- ## 三、规划覆盖完整性
299
+ ## 三、验收标准完整性
287
300
 
288
301
  **审视标准**:
289
- - B3 中的核心规划点是否在 C1 中得到体现
290
- - 是否存在规划被版本拆没的情况
302
+ - 每个 IT-BIZ 是否都有明确的"业务验收标准"
303
+ - 是否包含"体验验收"标准
291
304
 
292
305
  **审视结果**:
293
306
  <!-- AI 填写 -->
294
307
 
295
308
  ---
296
309
 
297
- ## 四、需求粒度成熟度
310
+ ## 四、细节与边界
298
311
 
299
312
  **审视标准**:
300
- - 每条需求是否达到版本级、可理解、可评估
301
- - 是否仍停留在规划语言
313
+ - 是否有状态流转图和异常处理
314
+ - IT-DEV 是否关联了 A2UI 原型
302
315
 
303
316
  **审视结果**:
304
317
  <!-- AI 填写 -->
305
318
 
306
319
  ---
307
320
 
308
- ## 五、进入执行准备度
321
+ ## 五、开发就绪状态
309
322
 
310
323
  **审视标准**:
311
- - 是否已不再需要产品侧做判断决策
312
- - 是否只剩实现与执行问题
324
+ - 研发团队拿到这个 IT-DEV,能否直接开始写代码
325
+ - 是否还需要问 PM "这里怎么做"
326
+
327
+ **审视结果**:
328
+ <!-- AI 填写 -->
329
+
330
+ ---
331
+
332
+ ## 六、用户视角审查
333
+
334
+ **审视标准**:
335
+ - 用户看到这个 IT,能立即理解它是干什么的吗
336
+ - 这个 IT 真的解决了 A2 中的用户反馈吗
313
337
 
314
338
  **审视结果**:
315
339
  <!-- AI 填写 -->
@@ -338,7 +362,7 @@ async function performR2Review(config, options = {}) {
338
362
  ⚠️ **重要提醒**:
339
363
  - 禁止讨论规划是否正确
340
364
  - 禁止提出新增需求
341
- - 只判断版本是否忠实执行了既定规划
365
+ - 只判断 IT 是否忠实执行了既定规划
342
366
  `;
343
367
 
344
368
  const r2Path = path.join(iterationDir, 'R2_版本审视报告.md');
@@ -354,8 +378,8 @@ async function performR2Review(config, options = {}) {
354
378
  console.log(chalk.cyan(' "请帮我执行 R2 审视,项目路径是 [当前目录]"'));
355
379
  console.log('');
356
380
  console.log('AI 将会:');
357
- console.log(' 1. 读取 B3、C0、C1 文档');
358
- console.log(' 2. 检查版本是否偏离规划');
381
+ console.log(' 1. 读取 B3、IT 文档');
382
+ console.log(' 2. 检查 IT 用户故事是否忠实执行了 B3 规划');
359
383
  console.log(' 3. 填写 R2 报告');
360
384
  console.log(' 4. 展示结论让你决策');
361
385
  console.log('');
@@ -368,7 +392,7 @@ function getR1Prompt() {
368
392
 
369
393
  我将提供以下输入:
370
394
  - A 类文档(产品现状与基线)
371
- - B1 / B2(需求规划草案与拆解)
395
+ - B_规划文档(需求规划与拆解)
372
396
 
373
397
  你的任务不是提出新方案,而是判断:
374
398
  【这份规划是否有资格被冻结为 B3】
@@ -408,36 +432,40 @@ function getR2Prompt() {
408
432
 
409
433
  我将提供以下输入:
410
434
  - B3(已冻结的规划文档)
411
- - C0 / C1(版本范围与版本需求清单)
435
+ - IT 文档(业务需求与技术规格)
412
436
 
413
437
  你的任务不是评判方向,而是判断:
414
- 【该版本是否忠实执行了既定规划】
438
+ 【IT 文档是否忠实执行了既定规划(B3)】
415
439
 
416
- 请严格按以下 5 个维度进行审视,并逐条给出判断依据:
440
+ 请严格按以下 6 个维度进行审视,并逐条给出判断依据:
441
+
442
+ 1. 业务场景闭环(以终为始)
443
+ - IT-BIZ 场景是否服务于 B3 核心目标
444
+ - 是否引入了未定义的业务目标
417
445
 
418
- 1. 版本目标一致性
419
- - C0 的版本目标是否能在 B3 中找到明确对应
420
- - 是否存在规划外目标
446
+ 2. 规划范围一致性
447
+ - B2 需求项是否都有对应 IT
448
+ - IT 是否包含 B2 没有的新增需求(Gap 检查)
421
449
 
422
- 2. 版本范围偏移检查
423
- - 版本包含/不包含是否符合 B3 边界
424
- - 是否存在隐性扩展或"顺手加戏"
450
+ 3. 验收标准完整性
451
+ - 每个 IT 是否有明确的业务验收标准
452
+ - 是否包含体验验收标准
425
453
 
426
- 3. 规划覆盖完整性
427
- - B3 中的核心规划点是否在 C1 中得到体现
428
- - 是否存在规划被版本拆没的情况
454
+ 4. 细节与边界
455
+ - 是否有异常处理和状态流转
456
+ - 技术规格(DEV)是否关联了 A2UI 原型
429
457
 
430
- 4. 需求粒度成熟度
431
- - 每条需求是否达到版本级、可理解、可评估
432
- - 是否仍停留在规划语言
458
+ 5. 开发就绪状态
459
+ - 研发是否能直接基于文档开发
460
+ - 是否存在模棱两可的描述
433
461
 
434
- 5. 进入执行准备度
435
- - 是否已不再需要产品侧做判断决策
436
- - 是否只剩实现与执行问题
462
+ 6. 用户视角审查
463
+ - 用户能否立即理解该功能
464
+ - 是否真正解决了用户痛点
437
465
 
438
466
  最后,请给出唯一结论(三选一):
439
467
  - 【通过】允许版本冻结
440
- - 【有条件通过】需修订的具体需求项
468
+ - 【有条件通过】需修订的具体 IT 文档
441
469
  - 【不通过】禁止版本冻结
442
470
 
443
471
  禁止讨论规划是否正确,禁止提出新增需求。`;
@@ -55,42 +55,38 @@ module.exports = async function () {
55
55
  if (await fs.pathExists(iterationDir)) {
56
56
  const files = await fs.readdir(iterationDir);
57
57
 
58
- const hasR1Start = files.some(f => f.includes('R1_规划启动'));
59
- const hasB1 = files.some(f => f.includes('B1'));
60
- const hasB2 = files.some(f => f.includes('B2'));
61
- const hasR1Review = files.some(f => f.includes('R1_规划审视'));
58
+ const hasB = files.some(f => f.includes('B_规划文档'));
62
59
  const hasB3 = files.some(f => f.includes('B3'));
63
- const hasC0 = files.some(f => f.includes('C0'));
64
- const hasC1 = files.some(f => f.includes('C1'));
60
+
61
+ // 检查 IT 目录
62
+ const itDir = path.join(iterationDir, 'IT');
63
+ let hasIT = false;
64
+ let itCount = 0;
65
+ if (await fs.pathExists(itDir)) {
66
+ const folders = await fs.readdir(itDir);
67
+ itCount = folders.filter(f => f.startsWith('IT-')).length;
68
+ hasIT = itCount > 0;
69
+ }
70
+
65
71
  const hasR2 = files.some(f => f.includes('R2'));
66
72
  const hasC3 = files.some(f => f.includes('C3'));
67
73
 
68
- console.log(` R1 启动检查: ${hasR1Start ? chalk.green('✓') : chalk.gray('○')}`);
69
- console.log(` B1 规划草案: ${hasB1 ? chalk.green('✓') : chalk.gray('○')}`);
70
- console.log(` B2 规划拆解: ${hasB2 ? chalk.green('✓') : chalk.gray('○')}`);
71
- console.log(` R1 规划审视: ${hasR1Review ? chalk.green('✓') : chalk.gray('○')}`);
74
+ console.log(` B 规划文档: ${hasB ? chalk.green('✓') : chalk.gray('○')}`);
72
75
  console.log(` B3 规划冻结: ${hasB3 ? chalk.green('✓') : chalk.gray('○')}`);
73
- console.log(` C0 版本范围: ${hasC0 ? chalk.green('✓') : chalk.gray('○')}`);
74
- console.log(` C1 版本需求: ${hasC1 ? chalk.green('✓') : chalk.gray('○')}`);
75
- console.log(` R2 版本审视: ${hasR2 ? chalk.green('✓') : chalk.gray('○')}`);
76
- console.log(` C3 版本冻结: ${hasC3 ? chalk.green('✓') : chalk.gray('○')}`);
76
+ console.log(` IT 用户故事: ${hasIT ? chalk.green(`✓ (${itCount}个)`) : chalk.gray('○')}`);
77
+ console.log(` R2 版本审视: ${hasR2 ? chalk.green('✓') : chalk.gray('○')} ${!hasR2 && hasIT ? chalk.gray('(自动执行)') : ''}`);
78
+ console.log(` C3 版本冻结: ${hasC3 ? chalk.green('✓') : chalk.gray('○')}`);;
77
79
 
78
80
  // 判断当前阶段
79
81
  let currentStage = '';
80
82
  if (hasC3) {
81
83
  currentStage = chalk.green('✓ 已完成');
82
- } else if (hasC1) {
83
- currentStage = chalk.cyan('· 待 R2 审视');
84
- } else if (hasC0) {
85
- currentStage = chalk.cyan('· C1 创建中');
84
+ } else if (hasIT) {
85
+ currentStage = chalk.cyan('· 待版本冻结');
86
86
  } else if (hasB3) {
87
- currentStage = chalk.cyan('· 版本需求阶段');
88
- } else if (hasB2) {
89
- currentStage = chalk.cyan('· 待 R1 审视');
90
- } else if (hasB1) {
91
- currentStage = chalk.cyan('· B2 创建中');
92
- } else if (hasR1Start) {
93
- currentStage = chalk.cyan('· 规划阶段');
87
+ currentStage = chalk.cyan('· 需求开发中 (IT)');
88
+ } else if (hasB) {
89
+ currentStage = chalk.cyan('· 待规划冻结');
94
90
  } else {
95
91
  currentStage = chalk.yellow('· 已创建');
96
92
  }
@@ -115,23 +111,18 @@ module.exports = async function () {
115
111
 
116
112
  if (await fs.pathExists(iterationDir)) {
117
113
  const files = await fs.readdir(iterationDir);
114
+ const itDir = path.join(iterationDir, 'IT');
115
+ const hasIT = await fs.pathExists(itDir) &&
116
+ (await fs.readdir(itDir)).some(f => f.startsWith('IT-'));
118
117
 
119
- if (!files.some(f => f.includes('B1'))) {
120
- console.log(chalk.cyan(' prd plan create B1 # 创建规划草案'));
121
- } else if (!files.some(f => f.includes('B2'))) {
122
- console.log(chalk.cyan(' prd plan create B2 # 创建规划拆解'));
123
- } else if (!files.some(f => f.includes('R1_规划审视'))) {
124
- console.log(chalk.cyan(' prd review r1 # 执行 R1 审视'));
118
+ if (!files.some(f => f.includes('B_规划文档'))) {
119
+ console.log(chalk.cyan(' prd plan create B # 创建规划文档'));
125
120
  } else if (!files.some(f => f.includes('B3'))) {
126
121
  console.log(chalk.cyan(' prd plan freeze # 冻结规划'));
127
- } else if (!files.some(f => f.includes('C0'))) {
128
- console.log(chalk.cyan(' prd version create C0 # 创建版本范围'));
129
- } else if (!files.some(f => f.includes('C1'))) {
130
- console.log(chalk.cyan(' prd version create C1 # 创建版本需求'));
131
- } else if (!files.some(f => f.includes('R2'))) {
132
- console.log(chalk.cyan(' prd review r2 # 执行 R2 审视'));
122
+ } else if (!hasIT) {
123
+ console.log(chalk.cyan(' prd it create "需求名称" # 创建 IT 用户故事'));
133
124
  } else if (!files.some(f => f.includes('C3'))) {
134
- console.log(chalk.cyan(' prd version freeze # 冻结版本'));
125
+ console.log(chalk.cyan(' prd version freeze # 冻结版本(自动R2审视)'));
135
126
  } else {
136
127
  console.log(chalk.green(' 当前迭代已完成!'));
137
128
  console.log(chalk.cyan(' prd iteration new # 开始新迭代'));
@@ -209,6 +209,26 @@ module.exports = async function (options = {}) {
209
209
 
210
210
  const totalChanges = newFiles.length + updatedFiles.length;
211
211
 
212
+ // 清理废弃文件
213
+ const deprecatedFiles = [
214
+ '.agent/workflows/prd-c1-requirement-list.md',
215
+ '.agent/workflows/prd-b1-planning-draft.md',
216
+ '.agent/workflows/prd-b2-planning-breakdown.md',
217
+ '.agent/workflows/prd-r1-review.md'
218
+ ];
219
+
220
+ for (const file of deprecatedFiles) {
221
+ const filePath = path.join(projectPath, file);
222
+ if (await fs.pathExists(filePath)) {
223
+ if (!dryRun) {
224
+ await fs.remove(filePath);
225
+ console.log(chalk.red(`🗑️ 已删除废弃文件: ${file}`));
226
+ } else {
227
+ console.log(chalk.red(`🗑️ [预览] 将删除废弃文件: ${file}`));
228
+ }
229
+ }
230
+ }
231
+
212
232
  if (totalChanges === 0) {
213
233
  console.log(chalk.green('✓ 所有文件已是最新版本!'));
214
234
  } else if (!dryRun) {