prd-workflow-cli 1.1.25
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/.agent/workflows/prd-b1-planning-draft.md +501 -0
- package/.agent/workflows/prd-b2-planning-breakdown.md +731 -0
- package/.agent/workflows/prd-c1-requirement-list.md +907 -0
- package/.agent/workflows/prd-c2-change-tracking.md +320 -0
- package/.agent/workflows/prd-dialog-archive.md +215 -0
- package/.agent/workflows/prd-p0-project-info.md +379 -0
- package/.agent/workflows/prd-r0-baseline-review.md +925 -0
- package/.agent/workflows/prd-r1-review.md +458 -0
- package/.agent/workflows/prd-r2-review.md +483 -0
- package/.antigravity/rules.md +238 -0
- package/.cursorrules +284 -0
- package/GUIDE.md +341 -0
- package/README.md +416 -0
- package/bin/prd-cli.js +134 -0
- package/commands/baseline.js +470 -0
- package/commands/change.js +151 -0
- package/commands/confirm.js +364 -0
- package/commands/dialog.js +227 -0
- package/commands/index.js +365 -0
- package/commands/init.js +357 -0
- package/commands/iteration.js +192 -0
- package/commands/planning.js +710 -0
- package/commands/review.js +444 -0
- package/commands/status.js +142 -0
- package/commands/upgrade.js +228 -0
- package/commands/version.js +794 -0
- package/package.json +74 -0
- package/scripts/postinstall.js +241 -0
- package/templates/README-FOR-NEW-USER.md +105 -0
- package/templates/dialog-template.md +109 -0
|
@@ -0,0 +1,444 @@
|
|
|
1
|
+
const fs = require('fs-extra');
|
|
2
|
+
const path = require('path');
|
|
3
|
+
const chalk = require('chalk');
|
|
4
|
+
const inquirer = require('inquirer');
|
|
5
|
+
const confirm = require('./confirm');
|
|
6
|
+
const dialog = require('./dialog');
|
|
7
|
+
|
|
8
|
+
module.exports = async function (type, options = {}) {
|
|
9
|
+
const configPath = path.join(process.cwd(), '.prd-config.json');
|
|
10
|
+
|
|
11
|
+
if (!await fs.pathExists(configPath)) {
|
|
12
|
+
console.log(chalk.red('✗ 当前目录不是一个 PRD 项目'));
|
|
13
|
+
console.log('请先运行: prd init <项目名>');
|
|
14
|
+
return;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const config = await fs.readJSON(configPath);
|
|
18
|
+
|
|
19
|
+
if (type === 'r1') {
|
|
20
|
+
await performR1Review(config, options);
|
|
21
|
+
} else if (type === 'r2') {
|
|
22
|
+
await performR2Review(config, options);
|
|
23
|
+
} else {
|
|
24
|
+
console.log(chalk.red('✗ 未知的审视类型'));
|
|
25
|
+
console.log('可用类型: r1, r2');
|
|
26
|
+
}
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
async function performR1Review(config, options = {}) {
|
|
30
|
+
console.log(chalk.bold.blue('\n=== R1 规划审视 ===\n'));
|
|
31
|
+
|
|
32
|
+
const iterationDir = path.join(
|
|
33
|
+
process.cwd(),
|
|
34
|
+
'02_迭代记录',
|
|
35
|
+
`第${String(config.currentIteration).padStart(2, '0')}轮迭代`
|
|
36
|
+
);
|
|
37
|
+
|
|
38
|
+
// 检查迭代目录是否存在
|
|
39
|
+
if (!await fs.pathExists(iterationDir)) {
|
|
40
|
+
console.log(chalk.red('✗ 第一轮迭代尚未创建'));
|
|
41
|
+
console.log('请先执行:prd iteration new');
|
|
42
|
+
if (process.env.PRD_TEST_MODE === 'true') {
|
|
43
|
+
throw new Error('第一轮迭代尚未创建');
|
|
44
|
+
}
|
|
45
|
+
process.exit(1);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// 检查必需文档
|
|
49
|
+
const b1Path = path.join(iterationDir, 'B1_需求规划草案.md');
|
|
50
|
+
const b2Path = path.join(iterationDir, 'B2_规划拆解与范围界定.md');
|
|
51
|
+
|
|
52
|
+
if (!await fs.pathExists(b1Path) || !await fs.pathExists(b2Path)) {
|
|
53
|
+
console.log(chalk.red('✗ 缺少必需的 B1 或 B2 文档'));
|
|
54
|
+
console.log('请先创建:');
|
|
55
|
+
console.log(' prd plan create B1');
|
|
56
|
+
console.log(' prd plan create B2');
|
|
57
|
+
if (process.env.PRD_TEST_MODE === 'true') {
|
|
58
|
+
throw new Error('缺少必需的 B1 或 B2 文档');
|
|
59
|
+
}
|
|
60
|
+
process.exit(1);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// ⭐ 支持预确认模式和非交互模式(用于测试)
|
|
64
|
+
if (options.pmConfirmed) {
|
|
65
|
+
console.log(chalk.green('✓ PM 已在对话中确认 B1B2 已填写完成,理解角色分工'));
|
|
66
|
+
} else if (process.env.PRD_TEST_MODE === 'true') {
|
|
67
|
+
// 测试模式:跳过交互式确认
|
|
68
|
+
console.log(chalk.yellow('⚠️ 测试模式:跳过交互式确认'));
|
|
69
|
+
} else {
|
|
70
|
+
// 交互式确认
|
|
71
|
+
const canProceed = await confirm.confirmR1Review();
|
|
72
|
+
if (!canProceed) {
|
|
73
|
+
console.log(chalk.yellow('已取消 R1 审视'));
|
|
74
|
+
process.exit(0);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// 显示审视指令
|
|
79
|
+
console.log(chalk.yellow('【AI 审视指令】\n'));
|
|
80
|
+
console.log(getR1Prompt());
|
|
81
|
+
console.log('\n' + chalk.gray('='.repeat(80)) + '\n');
|
|
82
|
+
|
|
83
|
+
// 生成 R1 报告模板
|
|
84
|
+
const r1Template = `# R1_规划审视报告
|
|
85
|
+
|
|
86
|
+
**审视时间**: ${new Date().toLocaleString('zh-CN')}
|
|
87
|
+
|
|
88
|
+
**审视对象**:
|
|
89
|
+
- B1_需求规划草案.md
|
|
90
|
+
- B2_规划拆解与范围界定.md
|
|
91
|
+
|
|
92
|
+
---
|
|
93
|
+
|
|
94
|
+
## 一、目标清晰性
|
|
95
|
+
|
|
96
|
+
**审视标准**:
|
|
97
|
+
- 是否能用一句话说明本次规划要解决的核心问题
|
|
98
|
+
- 是否存在多个不相关目标混杂
|
|
99
|
+
|
|
100
|
+
**审视结果**:
|
|
101
|
+
<!-- AI 填写 -->
|
|
102
|
+
|
|
103
|
+
---
|
|
104
|
+
|
|
105
|
+
## 二、场景真实性
|
|
106
|
+
|
|
107
|
+
**审视标准**:
|
|
108
|
+
- 使用场景是否基于 A 类现状
|
|
109
|
+
- 是否依赖当前不存在的能力
|
|
110
|
+
|
|
111
|
+
**审视结果**:
|
|
112
|
+
<!-- AI 填写 -->
|
|
113
|
+
|
|
114
|
+
---
|
|
115
|
+
|
|
116
|
+
## 三、现状一致性
|
|
117
|
+
|
|
118
|
+
**审视标准**:
|
|
119
|
+
- 是否明确区分"已有能力改进"与"新增能力"
|
|
120
|
+
- 是否存在从零重建的倾向
|
|
121
|
+
|
|
122
|
+
**审视结果**:
|
|
123
|
+
<!-- AI 填写 -->
|
|
124
|
+
|
|
125
|
+
---
|
|
126
|
+
|
|
127
|
+
## 四、范围收敛性
|
|
128
|
+
|
|
129
|
+
**审视标准**:
|
|
130
|
+
- 是否明确说明"不做什么"
|
|
131
|
+
- 是否存在明显范围膨胀风险
|
|
132
|
+
|
|
133
|
+
**审视结果**:
|
|
134
|
+
<!-- AI 填写 -->
|
|
135
|
+
|
|
136
|
+
---
|
|
137
|
+
|
|
138
|
+
## 五、版本化准备度
|
|
139
|
+
|
|
140
|
+
**审视标准**:
|
|
141
|
+
- 是否已不再讨论"值不值得做"
|
|
142
|
+
- 是否具备进入版本拆分的条件
|
|
143
|
+
|
|
144
|
+
**审视结果**:
|
|
145
|
+
<!-- AI 填写 -->
|
|
146
|
+
|
|
147
|
+
---
|
|
148
|
+
|
|
149
|
+
## 最终结论
|
|
150
|
+
|
|
151
|
+
**请从以下三项中选择一项**:
|
|
152
|
+
|
|
153
|
+
- [ ] 【通过】可冻结为 B3
|
|
154
|
+
- [ ] 【有条件通过】需补齐以下内容:
|
|
155
|
+
- <!-- 列出需要补充的具体项 -->
|
|
156
|
+
- [ ] 【不通过】不可进入 B3,原因:
|
|
157
|
+
- <!-- 列出不通过的具体原因 -->
|
|
158
|
+
|
|
159
|
+
---
|
|
160
|
+
|
|
161
|
+
## 审视人员签字
|
|
162
|
+
|
|
163
|
+
**审视人**:
|
|
164
|
+
**日期**: ${new Date().toLocaleDateString('zh-CN')}
|
|
165
|
+
|
|
166
|
+
---
|
|
167
|
+
|
|
168
|
+
⚠️ **重要提醒**:
|
|
169
|
+
- 本审视不应提出新的需求或设计建议
|
|
170
|
+
- 只审视现有规划是否满足冻结条件
|
|
171
|
+
- 必须给出明确的通过/不通过结论
|
|
172
|
+
`;
|
|
173
|
+
|
|
174
|
+
const r1Path = path.join(iterationDir, 'R1_规划审视报告.md');
|
|
175
|
+
await fs.writeFile(r1Path, r1Template);
|
|
176
|
+
|
|
177
|
+
// 记录审视启动
|
|
178
|
+
await dialog.logDialog('review', 'start_r1', { type: 'R1审视', status: '已生成模板' });
|
|
179
|
+
|
|
180
|
+
console.log(chalk.green('✓ R1 审视报告模板已生成'));
|
|
181
|
+
console.log(chalk.cyan(`\n文件位置: ${r1Path}\n`));
|
|
182
|
+
console.log(chalk.bold.yellow('━━━ 下一步操作 ━━━\n'));
|
|
183
|
+
console.log(chalk.bold('请回到 AI 对话中,发送以下消息:'));
|
|
184
|
+
console.log(chalk.cyan(' "请帮我执行 R1 审视,项目路径是 [当前目录]"'));
|
|
185
|
+
console.log('');
|
|
186
|
+
console.log('AI 将会:');
|
|
187
|
+
console.log(' 1. 读取 B1、B2 文档');
|
|
188
|
+
console.log(' 2. 按 5 维度审视');
|
|
189
|
+
console.log(' 3. 填写 R1 报告');
|
|
190
|
+
console.log(' 4. 展示结论让你决策');
|
|
191
|
+
console.log('');
|
|
192
|
+
console.log(chalk.gray('审视通过后,回到终端运行: prd plan freeze'));
|
|
193
|
+
console.log('');
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
async function performR2Review(config, options = {}) {
|
|
197
|
+
console.log(chalk.bold.blue('\n=== R2 版本审视 ===\n'));
|
|
198
|
+
|
|
199
|
+
const iterationDir = path.join(
|
|
200
|
+
process.cwd(),
|
|
201
|
+
'02_迭代记录',
|
|
202
|
+
`第${String(config.currentIteration).padStart(2, '0')}轮迭代`
|
|
203
|
+
);
|
|
204
|
+
|
|
205
|
+
// 检查迭代目录是否存在
|
|
206
|
+
if (!await fs.pathExists(iterationDir)) {
|
|
207
|
+
console.log(chalk.red('✗ 第一轮迭代尚未创建'));
|
|
208
|
+
console.log('请先执行:prd iteration new');
|
|
209
|
+
if (process.env.PRD_TEST_MODE === 'true') {
|
|
210
|
+
throw new Error('第一轮迭代尚未创建');
|
|
211
|
+
}
|
|
212
|
+
process.exit(1);
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
// 检查必需文档
|
|
216
|
+
const b3Path = path.join(iterationDir, 'B3_规划冻结归档.md');
|
|
217
|
+
const c0Path = path.join(iterationDir, 'C0_版本范围声明.md');
|
|
218
|
+
const c1Path = path.join(iterationDir, 'C1_版本需求清单.md');
|
|
219
|
+
|
|
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');
|
|
226
|
+
if (process.env.PRD_TEST_MODE === 'true') {
|
|
227
|
+
throw new Error('缺少必需的 B3、C0 或 C1 文档');
|
|
228
|
+
}
|
|
229
|
+
process.exit(1);
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
// ⭐ 支持预确认模式和非交互模式(用于测试)
|
|
233
|
+
if (options.pmConfirmed) {
|
|
234
|
+
console.log(chalk.green('✓ PM 已在对话中确认 C0C1 已填写完成,理解角色分工'));
|
|
235
|
+
} else if (process.env.PRD_TEST_MODE === 'true') {
|
|
236
|
+
// 测试模式:跳过交互式确认
|
|
237
|
+
console.log(chalk.yellow('⚠️ 测试模式:跳过交互式确认'));
|
|
238
|
+
} else {
|
|
239
|
+
// 交互式确认
|
|
240
|
+
const canProceed = await confirm.confirmR2Review();
|
|
241
|
+
if (!canProceed) {
|
|
242
|
+
console.log(chalk.yellow('已取消 R2 审视'));
|
|
243
|
+
process.exit(0);
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
// 显示审视指令
|
|
248
|
+
console.log(chalk.yellow('【AI 审视指令】\n'));
|
|
249
|
+
console.log(getR2Prompt());
|
|
250
|
+
console.log('\n' + chalk.gray('='.repeat(80)) + '\n');
|
|
251
|
+
|
|
252
|
+
// 生成 R2 报告模板
|
|
253
|
+
const r2Template = `# R2_版本审视报告
|
|
254
|
+
|
|
255
|
+
**审视时间**: ${new Date().toLocaleString('zh-CN')}
|
|
256
|
+
|
|
257
|
+
**审视对象**:
|
|
258
|
+
- B3_规划冻结归档.md
|
|
259
|
+
- C0_版本范围声明.md
|
|
260
|
+
- C1_版本需求清单.md
|
|
261
|
+
|
|
262
|
+
---
|
|
263
|
+
|
|
264
|
+
## 一、版本目标一致性
|
|
265
|
+
|
|
266
|
+
**审视标准**:
|
|
267
|
+
- C0 的版本目标是否能在 B3 中找到明确对应
|
|
268
|
+
- 是否存在规划外目标
|
|
269
|
+
|
|
270
|
+
**审视结果**:
|
|
271
|
+
<!-- AI 填写 -->
|
|
272
|
+
|
|
273
|
+
---
|
|
274
|
+
|
|
275
|
+
## 二、版本范围偏移检查
|
|
276
|
+
|
|
277
|
+
**审视标准**:
|
|
278
|
+
- 版本包含/不包含是否符合 B3 边界
|
|
279
|
+
- 是否存在隐性扩展或"顺手加戏"
|
|
280
|
+
|
|
281
|
+
**审视结果**:
|
|
282
|
+
<!-- AI 填写 -->
|
|
283
|
+
|
|
284
|
+
---
|
|
285
|
+
|
|
286
|
+
## 三、规划覆盖完整性
|
|
287
|
+
|
|
288
|
+
**审视标准**:
|
|
289
|
+
- B3 中的核心规划点是否在 C1 中得到体现
|
|
290
|
+
- 是否存在规划被版本拆没的情况
|
|
291
|
+
|
|
292
|
+
**审视结果**:
|
|
293
|
+
<!-- AI 填写 -->
|
|
294
|
+
|
|
295
|
+
---
|
|
296
|
+
|
|
297
|
+
## 四、需求粒度成熟度
|
|
298
|
+
|
|
299
|
+
**审视标准**:
|
|
300
|
+
- 每条需求是否达到版本级、可理解、可评估
|
|
301
|
+
- 是否仍停留在规划语言
|
|
302
|
+
|
|
303
|
+
**审视结果**:
|
|
304
|
+
<!-- AI 填写 -->
|
|
305
|
+
|
|
306
|
+
---
|
|
307
|
+
|
|
308
|
+
## 五、进入执行准备度
|
|
309
|
+
|
|
310
|
+
**审视标准**:
|
|
311
|
+
- 是否已不再需要产品侧做判断决策
|
|
312
|
+
- 是否只剩实现与执行问题
|
|
313
|
+
|
|
314
|
+
**审视结果**:
|
|
315
|
+
<!-- AI 填写 -->
|
|
316
|
+
|
|
317
|
+
---
|
|
318
|
+
|
|
319
|
+
## 最终结论
|
|
320
|
+
|
|
321
|
+
**请从以下三项中选择一项**:
|
|
322
|
+
|
|
323
|
+
- [ ] 【通过】允许版本冻结
|
|
324
|
+
- [ ] 【有条件通过】需修订以下需求项:
|
|
325
|
+
- <!-- 列出需要修订的具体需求 -->
|
|
326
|
+
- [ ] 【不通过】禁止版本冻结,原因:
|
|
327
|
+
- <!-- 列出不通过的具体原因 -->
|
|
328
|
+
|
|
329
|
+
---
|
|
330
|
+
|
|
331
|
+
## 审视人员签字
|
|
332
|
+
|
|
333
|
+
**审视人**:
|
|
334
|
+
**日期**: ${new Date().toLocaleDateString('zh-CN')}
|
|
335
|
+
|
|
336
|
+
---
|
|
337
|
+
|
|
338
|
+
⚠️ **重要提醒**:
|
|
339
|
+
- 禁止讨论规划是否正确
|
|
340
|
+
- 禁止提出新增需求
|
|
341
|
+
- 只判断版本是否忠实执行了既定规划
|
|
342
|
+
`;
|
|
343
|
+
|
|
344
|
+
const r2Path = path.join(iterationDir, 'R2_版本审视报告.md');
|
|
345
|
+
await fs.writeFile(r2Path, r2Template);
|
|
346
|
+
|
|
347
|
+
// 记录审视启动
|
|
348
|
+
await dialog.logDialog('review', 'start_r2', { type: 'R2审视', status: '已生成模板' });
|
|
349
|
+
|
|
350
|
+
console.log(chalk.green('✓ R2 审视报告模板已生成'));
|
|
351
|
+
console.log(chalk.cyan(`\n文件位置: ${r2Path}\n`));
|
|
352
|
+
console.log(chalk.bold.yellow('━━━ 下一步操作 ━━━\n'));
|
|
353
|
+
console.log(chalk.bold('请回到 AI 对话中,发送以下消息:'));
|
|
354
|
+
console.log(chalk.cyan(' "请帮我执行 R2 审视,项目路径是 [当前目录]"'));
|
|
355
|
+
console.log('');
|
|
356
|
+
console.log('AI 将会:');
|
|
357
|
+
console.log(' 1. 读取 B3、C0、C1 文档');
|
|
358
|
+
console.log(' 2. 检查版本是否偏离规划');
|
|
359
|
+
console.log(' 3. 填写 R2 报告');
|
|
360
|
+
console.log(' 4. 展示结论让你决策');
|
|
361
|
+
console.log('');
|
|
362
|
+
console.log(chalk.gray('审视通过后,回到终端运行: prd version freeze'));
|
|
363
|
+
console.log('');
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
function getR1Prompt() {
|
|
367
|
+
return `你现在扮演【产品规划审视官】。
|
|
368
|
+
|
|
369
|
+
我将提供以下输入:
|
|
370
|
+
- A 类文档(产品现状与基线)
|
|
371
|
+
- B1 / B2(需求规划草案与拆解)
|
|
372
|
+
|
|
373
|
+
你的任务不是提出新方案,而是判断:
|
|
374
|
+
【这份规划是否有资格被冻结为 B3】
|
|
375
|
+
|
|
376
|
+
请严格按以下 5 个维度进行审视,并逐条给出判断依据:
|
|
377
|
+
|
|
378
|
+
1. 目标清晰性
|
|
379
|
+
- 是否能用一句话说明本次规划要解决的核心问题
|
|
380
|
+
- 是否存在多个不相关目标混杂
|
|
381
|
+
|
|
382
|
+
2. 场景真实性
|
|
383
|
+
- 使用场景是否基于 A 类现状
|
|
384
|
+
- 是否依赖当前不存在的能力
|
|
385
|
+
|
|
386
|
+
3. 现状一致性
|
|
387
|
+
- 是否明确区分"已有能力改进"与"新增能力"
|
|
388
|
+
- 是否存在从零重建的倾向
|
|
389
|
+
|
|
390
|
+
4. 范围收敛性
|
|
391
|
+
- 是否明确说明"不做什么"
|
|
392
|
+
- 是否存在明显范围膨胀风险
|
|
393
|
+
|
|
394
|
+
5. 版本化准备度
|
|
395
|
+
- 是否已不再讨论"值不值得做"
|
|
396
|
+
- 是否具备进入版本拆分的条件
|
|
397
|
+
|
|
398
|
+
最后,请给出唯一结论(三选一):
|
|
399
|
+
- 【通过】可冻结为 B3
|
|
400
|
+
- 【有条件通过】需补齐的明确项
|
|
401
|
+
- 【不通过】不可进入 B3
|
|
402
|
+
|
|
403
|
+
禁止输出任何新的需求或设计建议。`;
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
function getR2Prompt() {
|
|
407
|
+
return `你现在扮演【版本一致性审视官】。
|
|
408
|
+
|
|
409
|
+
我将提供以下输入:
|
|
410
|
+
- B3(已冻结的规划文档)
|
|
411
|
+
- C0 / C1(版本范围与版本需求清单)
|
|
412
|
+
|
|
413
|
+
你的任务不是评判方向,而是判断:
|
|
414
|
+
【该版本是否忠实执行了既定规划】
|
|
415
|
+
|
|
416
|
+
请严格按以下 5 个维度进行审视,并逐条给出判断依据:
|
|
417
|
+
|
|
418
|
+
1. 版本目标一致性
|
|
419
|
+
- C0 的版本目标是否能在 B3 中找到明确对应
|
|
420
|
+
- 是否存在规划外目标
|
|
421
|
+
|
|
422
|
+
2. 版本范围偏移检查
|
|
423
|
+
- 版本包含/不包含是否符合 B3 边界
|
|
424
|
+
- 是否存在隐性扩展或"顺手加戏"
|
|
425
|
+
|
|
426
|
+
3. 规划覆盖完整性
|
|
427
|
+
- B3 中的核心规划点是否在 C1 中得到体现
|
|
428
|
+
- 是否存在规划被版本拆没的情况
|
|
429
|
+
|
|
430
|
+
4. 需求粒度成熟度
|
|
431
|
+
- 每条需求是否达到版本级、可理解、可评估
|
|
432
|
+
- 是否仍停留在规划语言
|
|
433
|
+
|
|
434
|
+
5. 进入执行准备度
|
|
435
|
+
- 是否已不再需要产品侧做判断决策
|
|
436
|
+
- 是否只剩实现与执行问题
|
|
437
|
+
|
|
438
|
+
最后,请给出唯一结论(三选一):
|
|
439
|
+
- 【通过】允许版本冻结
|
|
440
|
+
- 【有条件通过】需修订的具体需求项
|
|
441
|
+
- 【不通过】禁止版本冻结
|
|
442
|
+
|
|
443
|
+
禁止讨论规划是否正确,禁止提出新增需求。`;
|
|
444
|
+
}
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
const fs = require('fs-extra');
|
|
2
|
+
const path = require('path');
|
|
3
|
+
const chalk = require('chalk');
|
|
4
|
+
|
|
5
|
+
module.exports = async function () {
|
|
6
|
+
const configPath = path.join(process.cwd(), '.prd-config.json');
|
|
7
|
+
|
|
8
|
+
if (!await fs.pathExists(configPath)) {
|
|
9
|
+
console.log(chalk.red('✗ 当前目录不是一个 PRD 项目'));
|
|
10
|
+
console.log('请先运行: prd init <项目名>');
|
|
11
|
+
return;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
const config = await fs.readJSON(configPath);
|
|
15
|
+
|
|
16
|
+
console.log(chalk.bold.cyan('\n=== 项目状态 ===\n'));
|
|
17
|
+
|
|
18
|
+
console.log(chalk.bold('项目信息:'));
|
|
19
|
+
console.log(` 名称: ${config.projectName}`);
|
|
20
|
+
console.log(` 创建时间: ${new Date(config.createdAt).toLocaleString('zh-CN')}`);
|
|
21
|
+
console.log(` 当前迭代: 第 ${config.currentIteration} 轮`);
|
|
22
|
+
console.log('');
|
|
23
|
+
|
|
24
|
+
// 检查基线完成情况
|
|
25
|
+
console.log(chalk.bold('基线状态:'));
|
|
26
|
+
const baselineDir = path.join(process.cwd(), '01_产品基线');
|
|
27
|
+
if (await fs.pathExists(baselineDir)) {
|
|
28
|
+
const files = await fs.readdir(baselineDir);
|
|
29
|
+
const hasA0 = files.some(f => f.includes('A0'));
|
|
30
|
+
const hasA1 = files.some(f => f.includes('A1'));
|
|
31
|
+
const hasA2 = files.some(f => f.includes('A2'));
|
|
32
|
+
const hasR0 = files.some(f => f.includes('R0'));
|
|
33
|
+
|
|
34
|
+
console.log(` A0 产品基础: ${hasA0 ? chalk.green('✓') : chalk.gray('○')}`);
|
|
35
|
+
console.log(` A1 功能清单: ${hasA1 ? chalk.green('✓') : chalk.gray('○')}`);
|
|
36
|
+
console.log(` A2 反馈汇总: ${hasA2 ? chalk.green('✓') : chalk.gray('○')}`);
|
|
37
|
+
console.log(` R0 基线审视: ${hasR0 ? chalk.green('✓') : chalk.gray('○')}`);
|
|
38
|
+
|
|
39
|
+
const baselineComplete = hasA0 && hasA1 && hasA2 && hasR0;
|
|
40
|
+
console.log(` 状态: ${baselineComplete ? chalk.green('已完成') : chalk.yellow('进行中')}`);
|
|
41
|
+
} else {
|
|
42
|
+
console.log(chalk.gray(' 尚未开始'));
|
|
43
|
+
}
|
|
44
|
+
console.log('');
|
|
45
|
+
|
|
46
|
+
// 检查当前迭代状态
|
|
47
|
+
if (config.currentIteration > 0) {
|
|
48
|
+
console.log(chalk.bold('当前迭代:'));
|
|
49
|
+
const iterationDir = path.join(
|
|
50
|
+
process.cwd(),
|
|
51
|
+
'02_迭代记录',
|
|
52
|
+
`第${String(config.currentIteration).padStart(2, '0')}轮迭代`
|
|
53
|
+
);
|
|
54
|
+
|
|
55
|
+
if (await fs.pathExists(iterationDir)) {
|
|
56
|
+
const files = await fs.readdir(iterationDir);
|
|
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_规划审视'));
|
|
62
|
+
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'));
|
|
65
|
+
const hasR2 = files.some(f => f.includes('R2'));
|
|
66
|
+
const hasC3 = files.some(f => f.includes('C3'));
|
|
67
|
+
|
|
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('○')}`);
|
|
72
|
+
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('○')}`);
|
|
77
|
+
|
|
78
|
+
// 判断当前阶段
|
|
79
|
+
let currentStage = '';
|
|
80
|
+
if (hasC3) {
|
|
81
|
+
currentStage = chalk.green('✓ 已完成');
|
|
82
|
+
} else if (hasC1) {
|
|
83
|
+
currentStage = chalk.cyan('· 待 R2 审视');
|
|
84
|
+
} else if (hasC0) {
|
|
85
|
+
currentStage = chalk.cyan('· C1 创建中');
|
|
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('· 规划阶段');
|
|
94
|
+
} else {
|
|
95
|
+
currentStage = chalk.yellow('· 已创建');
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
console.log(` 状态: ${currentStage}`);
|
|
99
|
+
}
|
|
100
|
+
} else {
|
|
101
|
+
console.log(chalk.gray('尚未开始迭代'));
|
|
102
|
+
}
|
|
103
|
+
console.log('');
|
|
104
|
+
|
|
105
|
+
// 下一步建议
|
|
106
|
+
console.log(chalk.bold('下一步建议:'));
|
|
107
|
+
if (config.currentIteration === 0) {
|
|
108
|
+
console.log(chalk.cyan(' prd baseline create A0 # 创建基线文档'));
|
|
109
|
+
} else {
|
|
110
|
+
const iterationDir = path.join(
|
|
111
|
+
process.cwd(),
|
|
112
|
+
'02_迭代记录',
|
|
113
|
+
`第${String(config.currentIteration).padStart(2, '0')}轮迭代`
|
|
114
|
+
);
|
|
115
|
+
|
|
116
|
+
if (await fs.pathExists(iterationDir)) {
|
|
117
|
+
const files = await fs.readdir(iterationDir);
|
|
118
|
+
|
|
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 审视'));
|
|
125
|
+
} else if (!files.some(f => f.includes('B3'))) {
|
|
126
|
+
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 审视'));
|
|
133
|
+
} else if (!files.some(f => f.includes('C3'))) {
|
|
134
|
+
console.log(chalk.cyan(' prd version freeze # 冻结版本'));
|
|
135
|
+
} else {
|
|
136
|
+
console.log(chalk.green(' 当前迭代已完成!'));
|
|
137
|
+
console.log(chalk.cyan(' prd iteration new # 开始新迭代'));
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
console.log('');
|
|
142
|
+
};
|