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,470 @@
|
|
|
1
|
+
const fs = require('fs-extra');
|
|
2
|
+
const path = require('path');
|
|
3
|
+
const chalk = require('chalk');
|
|
4
|
+
const confirm = require('./confirm');
|
|
5
|
+
|
|
6
|
+
module.exports = async function (action, type, options = {}) {
|
|
7
|
+
const configPath = path.join(process.cwd(), '.prd-config.json');
|
|
8
|
+
|
|
9
|
+
if (!await fs.pathExists(configPath)) {
|
|
10
|
+
console.log(chalk.red('✗ 当前目录不是一个 PRD 项目'));
|
|
11
|
+
return;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
const config = await fs.readJSON(configPath);
|
|
15
|
+
const baselineDir = path.join(process.cwd(), '01_产品基线');
|
|
16
|
+
|
|
17
|
+
if (action === 'create') {
|
|
18
|
+
await createBaselineDoc(type, baselineDir, config, configPath, options);
|
|
19
|
+
} else {
|
|
20
|
+
console.log(chalk.red('✗ 未知操作'));
|
|
21
|
+
}
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
async function createBaselineDoc(type, baselineDir, config, configPath, options = {}) {
|
|
25
|
+
const templates = {
|
|
26
|
+
'A0': getA0Template(),
|
|
27
|
+
'A1': getA1Template(),
|
|
28
|
+
'A2': getA2Template(),
|
|
29
|
+
'R0': getR0Template()
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
if (!templates[type]) {
|
|
33
|
+
console.log(chalk.red(`✗ 未知的文档类型: ${type}`));
|
|
34
|
+
console.log('可用类型: A0, A1, A2, R0');
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const fileName = getFileName(type);
|
|
39
|
+
const filePath = path.join(baselineDir, fileName);
|
|
40
|
+
|
|
41
|
+
if (await fs.pathExists(filePath)) {
|
|
42
|
+
console.log(chalk.yellow(`⚠ 文件已存在: ${fileName}`));
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// ⭐ R0 创建需要特殊处理:前置条件检查 + PM 确认
|
|
47
|
+
if (type === 'R0') {
|
|
48
|
+
console.log(chalk.bold.blue('\n=== R0 基线审视创建 ===\n'));
|
|
49
|
+
|
|
50
|
+
// 1. 前置条件检查
|
|
51
|
+
const projectDir = path.join(process.cwd(), '00_项目总览');
|
|
52
|
+
const p0Path = path.join(projectDir, 'P0_项目基本信息.md');
|
|
53
|
+
const a0Path = path.join(baselineDir, 'A0_产品基础与范围说明.md');
|
|
54
|
+
const a1Path = path.join(baselineDir, 'A1_已上线功能与流程清单.md');
|
|
55
|
+
const a2Path = path.join(baselineDir, 'A2_存量反馈与数据汇总.md');
|
|
56
|
+
|
|
57
|
+
console.log(chalk.yellow('📋 前置条件检查:\n'));
|
|
58
|
+
|
|
59
|
+
const p0Exists = await fs.pathExists(p0Path);
|
|
60
|
+
const a0Exists = await fs.pathExists(a0Path);
|
|
61
|
+
const a1Exists = await fs.pathExists(a1Path);
|
|
62
|
+
const a2Exists = await fs.pathExists(a2Path);
|
|
63
|
+
|
|
64
|
+
console.log(` ${p0Exists ? '✅' : '❌'} P0_项目基本信息.md`);
|
|
65
|
+
console.log(` ${a0Exists ? '✅' : '❌'} A0_产品基础与范围说明.md`);
|
|
66
|
+
console.log(` ${a1Exists ? '✅' : '❌'} A1_已上线功能与流程清单.md`);
|
|
67
|
+
console.log(` ${a2Exists ? '✅' : '⚠️ (可选)'} A2_存量反馈与数据汇总.md`);
|
|
68
|
+
console.log('');
|
|
69
|
+
|
|
70
|
+
// P0、A0、A1 是必需的
|
|
71
|
+
if (!p0Exists || !a0Exists || !a1Exists) {
|
|
72
|
+
console.log(chalk.red('❌ 前置条件检查未通过!\n'));
|
|
73
|
+
console.log(chalk.yellow('R0 基线审视必须基于完整的 A 类基线文档。\n'));
|
|
74
|
+
console.log(chalk.bold('请先完成缺失的文档:'));
|
|
75
|
+
if (!p0Exists) console.log(' - 完善 P0(00_项目总览/P0_项目基本信息.md)');
|
|
76
|
+
if (!a0Exists) console.log(' - 创建 A0:prd baseline create A0');
|
|
77
|
+
if (!a1Exists) console.log(' - 创建 A1:prd baseline create A1');
|
|
78
|
+
console.log('');
|
|
79
|
+
console.log(chalk.gray('提示:A2 是可选的,但建议创建'));
|
|
80
|
+
|
|
81
|
+
// 在测试模式下抛出错误
|
|
82
|
+
if (process.env.PRD_TEST_MODE === 'true') {
|
|
83
|
+
throw new Error('R0 前置条件检查未通过');
|
|
84
|
+
}
|
|
85
|
+
process.exit(1);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
console.log(chalk.green('✅ 前置条件检查通过!\n'));
|
|
89
|
+
|
|
90
|
+
// 2. PM 确认
|
|
91
|
+
if (options.pmConfirmed) {
|
|
92
|
+
console.log(chalk.green('✓ PM 已在对话中确认创建 R0 基线审视'));
|
|
93
|
+
} else if (process.env.PRD_TEST_MODE === 'true') {
|
|
94
|
+
// 测试模式:跳过交互式确认
|
|
95
|
+
console.log(chalk.yellow('⚠️ 测试模式:跳过交互式确认'));
|
|
96
|
+
} else {
|
|
97
|
+
// 交互式确认
|
|
98
|
+
console.log(chalk.yellow('⚠️ R0 基线审视将:'));
|
|
99
|
+
console.log(' 1. 系统性审视产品基线(基于 A0/A1/A2)');
|
|
100
|
+
console.log(' 2. 梳理用户路径和问题');
|
|
101
|
+
console.log(' 3. 识别关键成功因素');
|
|
102
|
+
console.log(' 4. 给出基线稳定性判定\n');
|
|
103
|
+
|
|
104
|
+
const confirmed = await confirm.confirmR0Creation();
|
|
105
|
+
if (!confirmed) {
|
|
106
|
+
console.log(chalk.yellow('\n已取消创建 R0'));
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
console.log(chalk.green('\n✓ PM 确认创建 R0\n'));
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
await fs.writeFile(filePath, templates[type]);
|
|
114
|
+
|
|
115
|
+
// 更新配置
|
|
116
|
+
config.stages.baseline.documents.push(type);
|
|
117
|
+
await fs.writeJSON(configPath, config, { spaces: 2 });
|
|
118
|
+
|
|
119
|
+
console.log(chalk.green(`✓ 已创建: ${fileName}`));
|
|
120
|
+
console.log(chalk.cyan(`\n文件位置: ${filePath}\n`));
|
|
121
|
+
|
|
122
|
+
// 给出下一步提示
|
|
123
|
+
if (type === 'A0') {
|
|
124
|
+
console.log(chalk.bold('下一步建议:'));
|
|
125
|
+
console.log('1. 填写 A0 产品基础与范围说明');
|
|
126
|
+
console.log('2. 创建 A1: prd baseline create A1');
|
|
127
|
+
} else if (type === 'A1') {
|
|
128
|
+
console.log(chalk.bold('下一步建议:'));
|
|
129
|
+
console.log('1. 填写 A1 已上线功能与流程清单');
|
|
130
|
+
console.log('2. 创建 A2: prd baseline create A2');
|
|
131
|
+
} else if (type === 'A2') {
|
|
132
|
+
console.log(chalk.bold('下一步建议:'));
|
|
133
|
+
console.log('1. 填写 A2 存量反馈与数据汇总');
|
|
134
|
+
console.log('2. 创建 R0 基线审视: prd baseline create R0');
|
|
135
|
+
} else if (type === 'R0') {
|
|
136
|
+
console.log(chalk.bold('下一步建议:'));
|
|
137
|
+
console.log('1. 完成 R0 基线审视(与 AI 协作填写)');
|
|
138
|
+
console.log('2. 开始第一轮迭代: prd iteration new');
|
|
139
|
+
console.log('');
|
|
140
|
+
console.log(chalk.yellow('⚠️ 重要提醒:'));
|
|
141
|
+
console.log(' R0 完成后,请勿自动创建后续文档!');
|
|
142
|
+
console.log(' 必须由 PM 明确指示才能进入下一阶段。');
|
|
143
|
+
}
|
|
144
|
+
console.log('');
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
function getFileName(type) {
|
|
148
|
+
const nameMap = {
|
|
149
|
+
'A0': 'A0_产品基础与范围说明.md',
|
|
150
|
+
'A1': 'A1_已上线功能与流程清单.md',
|
|
151
|
+
'A2': 'A2_存量反馈与数据汇总.md',
|
|
152
|
+
'R0': 'R0_基线审视报告.md'
|
|
153
|
+
};
|
|
154
|
+
return nameMap[type];
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
function getA0Template() {
|
|
158
|
+
return `# A0_产品基础与范围说明
|
|
159
|
+
|
|
160
|
+
**文档创建时间**: ${new Date().toLocaleString('zh-CN')}
|
|
161
|
+
|
|
162
|
+
---
|
|
163
|
+
|
|
164
|
+
## 一、产品背景与定位
|
|
165
|
+
|
|
166
|
+
### 产品是什么
|
|
167
|
+
<!-- 用一句话描述此产品 -->
|
|
168
|
+
|
|
169
|
+
### 产品定位
|
|
170
|
+
<!-- 在整个业务体系中的角色 -->
|
|
171
|
+
|
|
172
|
+
---
|
|
173
|
+
|
|
174
|
+
## 二、目标用户定义
|
|
175
|
+
|
|
176
|
+
### 主要用户群体
|
|
177
|
+
<!-- 列出主要用户类型 -->
|
|
178
|
+
|
|
179
|
+
### 用户画像
|
|
180
|
+
<!-- 描述典型用户特征 -->
|
|
181
|
+
|
|
182
|
+
---
|
|
183
|
+
|
|
184
|
+
## 三、核心使用场景
|
|
185
|
+
|
|
186
|
+
### 场景一:
|
|
187
|
+
<!-- 场景描述 -->
|
|
188
|
+
|
|
189
|
+
### 场景二:
|
|
190
|
+
<!-- 场景描述 -->
|
|
191
|
+
|
|
192
|
+
---
|
|
193
|
+
|
|
194
|
+
## 四、当前版本范围与边界
|
|
195
|
+
|
|
196
|
+
### 当前包含的能力
|
|
197
|
+
<!-- 列出已有的核心功能 -->
|
|
198
|
+
|
|
199
|
+
### 当前的技术架构
|
|
200
|
+
<!-- 简要说明技术实现方式 -->
|
|
201
|
+
|
|
202
|
+
---
|
|
203
|
+
|
|
204
|
+
## 五、明确不覆盖的内容
|
|
205
|
+
|
|
206
|
+
### 当前不支持的场景
|
|
207
|
+
<!-- 明确说明哪些场景不支持 -->
|
|
208
|
+
|
|
209
|
+
### 已知限制
|
|
210
|
+
<!-- 列出当前的限制条件 -->
|
|
211
|
+
|
|
212
|
+
---
|
|
213
|
+
|
|
214
|
+
## 填写说明
|
|
215
|
+
|
|
216
|
+
⚠️ **重要约束**:
|
|
217
|
+
- 不写规划、不写愿景
|
|
218
|
+
- 只描述"现在这个产品是什么样"
|
|
219
|
+
- 边界要写清楚(哪些能力没有、哪些不支持)
|
|
220
|
+
|
|
221
|
+
**目的**:
|
|
222
|
+
- 给 AI 和人一个统一的"现状语境"
|
|
223
|
+
- 防止后续规划"假设一个不存在的产品"
|
|
224
|
+
- 作为所有 B 规划的前置事实引用源
|
|
225
|
+
`;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
function getA1Template() {
|
|
229
|
+
return `# A1_已上线功能与流程清单
|
|
230
|
+
|
|
231
|
+
**文档创建时间**: ${new Date().toLocaleString('zh-CN')}
|
|
232
|
+
|
|
233
|
+
---
|
|
234
|
+
|
|
235
|
+
## 一、功能列表(按模块)
|
|
236
|
+
|
|
237
|
+
### 模块一: [模块名称]
|
|
238
|
+
- 功能 1.1:
|
|
239
|
+
- 功能 1.2:
|
|
240
|
+
|
|
241
|
+
### 模块二: [模块名称]
|
|
242
|
+
- 功能 2.1:
|
|
243
|
+
- 功能 2.2:
|
|
244
|
+
|
|
245
|
+
---
|
|
246
|
+
|
|
247
|
+
## 二、核心用户路径
|
|
248
|
+
|
|
249
|
+
### 路径一: [路径名称]
|
|
250
|
+
1. 步骤 1
|
|
251
|
+
2. 步骤 2
|
|
252
|
+
3. 步骤 3
|
|
253
|
+
|
|
254
|
+
### 路径二: [路径名称]
|
|
255
|
+
1. 步骤 1
|
|
256
|
+
2. 步骤 2
|
|
257
|
+
|
|
258
|
+
---
|
|
259
|
+
|
|
260
|
+
## 三、关键业务流程节点
|
|
261
|
+
|
|
262
|
+
### 流程一:
|
|
263
|
+
<!-- 描述业务流程的关键节点 -->
|
|
264
|
+
|
|
265
|
+
---
|
|
266
|
+
|
|
267
|
+
## 四、功能之间的依赖关系
|
|
268
|
+
|
|
269
|
+
### 依赖关系图
|
|
270
|
+
<!-- 描述功能间的依赖 -->
|
|
271
|
+
|
|
272
|
+
---
|
|
273
|
+
|
|
274
|
+
## 填写说明
|
|
275
|
+
|
|
276
|
+
⚠️ **重要约束**:
|
|
277
|
+
- 功能是"客观存在的",不是"设计过的"
|
|
278
|
+
- 用户路径用真实使用顺序,不用理想流程
|
|
279
|
+
- 不评价好坏,只陈述事实
|
|
280
|
+
|
|
281
|
+
**目的**:
|
|
282
|
+
- 让审视与规划基于真实系统
|
|
283
|
+
- 防止 AI 反复"重建已有能力"
|
|
284
|
+
- 为 R0 / R1 提供审视对象
|
|
285
|
+
`;
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
function getA2Template() {
|
|
289
|
+
return `# A2_存量反馈与数据输入汇总
|
|
290
|
+
|
|
291
|
+
**文档创建时间**: ${new Date().toLocaleString('zh-CN')}
|
|
292
|
+
|
|
293
|
+
---
|
|
294
|
+
|
|
295
|
+
## 一、用户反馈摘要
|
|
296
|
+
|
|
297
|
+
### 反馈类型一:
|
|
298
|
+
<!-- 摘要用户反馈内容 -->
|
|
299
|
+
- 来源:
|
|
300
|
+
- 时间:
|
|
301
|
+
|
|
302
|
+
### 反馈类型二:
|
|
303
|
+
<!-- 摘要用户反馈内容 -->
|
|
304
|
+
|
|
305
|
+
---
|
|
306
|
+
|
|
307
|
+
## 二、数据异常或指标变化
|
|
308
|
+
|
|
309
|
+
### 异常一:
|
|
310
|
+
<!-- 描述数据异常情况 -->
|
|
311
|
+
|
|
312
|
+
---
|
|
313
|
+
|
|
314
|
+
## 三、内部问题/投诉
|
|
315
|
+
|
|
316
|
+
### 问题一:
|
|
317
|
+
<!-- 描述问题 -->
|
|
318
|
+
- 来源:
|
|
319
|
+
- 影响范围:
|
|
320
|
+
|
|
321
|
+
---
|
|
322
|
+
|
|
323
|
+
## 四、已知未解决事项
|
|
324
|
+
|
|
325
|
+
### 事项一:
|
|
326
|
+
<!-- 描述未解决的问题 -->
|
|
327
|
+
- 原因:
|
|
328
|
+
- 优先级:
|
|
329
|
+
|
|
330
|
+
---
|
|
331
|
+
|
|
332
|
+
## 五、待下版事项(C 阶段产生的新需求)
|
|
333
|
+
|
|
334
|
+
**用途说明**:
|
|
335
|
+
当 C1/C2 讨论过程中产生了新需求,但超出当前版本(B3)的规划范围时,
|
|
336
|
+
应记录在此章节,等待下一轮迭代时纳入 B1 规划。
|
|
337
|
+
|
|
338
|
+
### 待下版事项 #1: [需求标题]
|
|
339
|
+
|
|
340
|
+
**来源**:C1/C2 讨论过程(第 XX 轮迭代,YYYY-MM-DD)
|
|
341
|
+
**原因**:超出 B3 首版范围,延后处理
|
|
342
|
+
|
|
343
|
+
**优先级**:
|
|
344
|
+
- [ ] P0 - 紧急
|
|
345
|
+
- [ ] P1 - 重要
|
|
346
|
+
- [ ] P2 - 一般
|
|
347
|
+
|
|
348
|
+
**详细描述**:
|
|
349
|
+
<!-- 需求的详细说明 -->
|
|
350
|
+
|
|
351
|
+
**PM 补充说明**:
|
|
352
|
+
<!-- 保留 PM 原话 -->
|
|
353
|
+
|
|
354
|
+
**关联需求**:
|
|
355
|
+
<!-- 与现有需求的关联 -->
|
|
356
|
+
|
|
357
|
+
**记录时间**:
|
|
358
|
+
**记录人**:
|
|
359
|
+
|
|
360
|
+
---
|
|
361
|
+
|
|
362
|
+
## 填写说明
|
|
363
|
+
|
|
364
|
+
⚠️ **重要约束**:
|
|
365
|
+
- 不做结论、不做方案
|
|
366
|
+
- 可以是原始反馈的整理
|
|
367
|
+
- 标注来源即可
|
|
368
|
+
|
|
369
|
+
**目的**:
|
|
370
|
+
- 为 B 规划提供动因素材
|
|
371
|
+
- 防止规划"拍脑袋"
|
|
372
|
+
- 为 R 审视提供"现实校验"
|
|
373
|
+
- **暂存 C 阶段产生的超范围需求(待下版处理)**
|
|
374
|
+
|
|
375
|
+
---
|
|
376
|
+
|
|
377
|
+
## 📋 使用流程
|
|
378
|
+
|
|
379
|
+
### 何时写入本文档?
|
|
380
|
+
|
|
381
|
+
| 场景 | 写入章节 |
|
|
382
|
+
|------|----------|
|
|
383
|
+
| 收到用户反馈 | 一、用户反馈摘要 |
|
|
384
|
+
| 发现数据异常 | 二、数据异常或指标变化 |
|
|
385
|
+
| 内部发现问题 | 三、内部问题/投诉 |
|
|
386
|
+
| 已知但未解决的问题 | 四、已知未解决事项 |
|
|
387
|
+
| **C1/C2 讨论中产生的新需求** | **五、待下版事项** |
|
|
388
|
+
|
|
389
|
+
### 何时从本文档提取?
|
|
390
|
+
|
|
391
|
+
- **开始新一轮迭代时**:从第四、五章节提取问题/需求到 B1
|
|
392
|
+
- **B1 规划时**:引用第一~三章节作为需求来源依据
|
|
393
|
+
`;
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
function getR0Template() {
|
|
397
|
+
return `# R0_基线审视报告
|
|
398
|
+
|
|
399
|
+
**审视时间**: ${new Date().toLocaleString('zh-CN')}
|
|
400
|
+
|
|
401
|
+
---
|
|
402
|
+
|
|
403
|
+
## 一、审视范围说明
|
|
404
|
+
|
|
405
|
+
**审视对象**:
|
|
406
|
+
- A0_产品基础与范围说明.md
|
|
407
|
+
- A1_已上线功能与流程清单.md
|
|
408
|
+
- A2_存量反馈与数据汇总.md
|
|
409
|
+
|
|
410
|
+
**审视目标**:
|
|
411
|
+
建立产品基线,为后续迭代规划提供稳定的起点。
|
|
412
|
+
|
|
413
|
+
---
|
|
414
|
+
|
|
415
|
+
## 二、实际用户路径审视
|
|
416
|
+
|
|
417
|
+
### 主要用户路径梳理
|
|
418
|
+
<!-- 从头到尾走一遍系统,描述实际使用情况 -->
|
|
419
|
+
|
|
420
|
+
### 发现的路径问题
|
|
421
|
+
<!-- 列出用户路径中的断点、痛点 -->
|
|
422
|
+
|
|
423
|
+
---
|
|
424
|
+
|
|
425
|
+
## 三、主要问题与机会
|
|
426
|
+
|
|
427
|
+
### 问题清单
|
|
428
|
+
1.
|
|
429
|
+
2.
|
|
430
|
+
|
|
431
|
+
### 机会点
|
|
432
|
+
1.
|
|
433
|
+
2.
|
|
434
|
+
|
|
435
|
+
---
|
|
436
|
+
|
|
437
|
+
## 四、风险与隐患
|
|
438
|
+
|
|
439
|
+
### 技术风险
|
|
440
|
+
<!-- 列出技术层面的风险 -->
|
|
441
|
+
|
|
442
|
+
### 业务风险
|
|
443
|
+
<!-- 列出业务层面的风险 -->
|
|
444
|
+
|
|
445
|
+
---
|
|
446
|
+
|
|
447
|
+
## 五、总体判断结论
|
|
448
|
+
|
|
449
|
+
**基线稳定性评估**:
|
|
450
|
+
- [ ] 可以作为稳定基线
|
|
451
|
+
- [ ] 需要先解决关键问题
|
|
452
|
+
|
|
453
|
+
**下一步建议**:
|
|
454
|
+
<!-- 给出后续规划建议 -->
|
|
455
|
+
|
|
456
|
+
---
|
|
457
|
+
|
|
458
|
+
## 填写说明
|
|
459
|
+
|
|
460
|
+
⚠️ **重要约束**:
|
|
461
|
+
- 必须"从头到尾走一遍系统"
|
|
462
|
+
- 问题基于事实,不基于偏好
|
|
463
|
+
- 结论是"是否适合作为稳定基线"
|
|
464
|
+
|
|
465
|
+
**目的**:
|
|
466
|
+
- 给存量系统建立一个起点基线
|
|
467
|
+
- 为第一次 B 规划提供"共识事实"
|
|
468
|
+
- 防止一上来就大改而不知问题在哪
|
|
469
|
+
`;
|
|
470
|
+
}
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
const fs = require('fs-extra');
|
|
2
|
+
const path = require('path');
|
|
3
|
+
const chalk = require('chalk');
|
|
4
|
+
const inquirer = require('inquirer');
|
|
5
|
+
const dialog = require('./dialog');
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* 需求变更命令
|
|
9
|
+
* 自动判断当前项目状态,引导用户正确记录变更
|
|
10
|
+
*/
|
|
11
|
+
module.exports = async function () {
|
|
12
|
+
const configPath = path.join(process.cwd(), '.prd-config.json');
|
|
13
|
+
|
|
14
|
+
if (!await fs.pathExists(configPath)) {
|
|
15
|
+
console.log(chalk.red('✗ 当前目录不是一个 PRD 项目'));
|
|
16
|
+
console.log('请先运行: prd init <项目名>');
|
|
17
|
+
return;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const config = await fs.readJSON(configPath);
|
|
21
|
+
|
|
22
|
+
if (config.currentIteration === 0) {
|
|
23
|
+
console.log(chalk.red('✗ 还没有开始任何迭代'));
|
|
24
|
+
console.log('请先运行: prd iteration new');
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
console.log(chalk.bold.blue('\n━━━ 需求变更检查 ━━━\n'));
|
|
29
|
+
|
|
30
|
+
// 检查当前迭代的文档状态
|
|
31
|
+
const iterationDir = path.join(
|
|
32
|
+
process.cwd(),
|
|
33
|
+
'02_迭代记录',
|
|
34
|
+
`第${String(config.currentIteration).padStart(2, '0')}轮迭代`
|
|
35
|
+
);
|
|
36
|
+
|
|
37
|
+
const c3Path = path.join(iterationDir, 'C3_版本冻结归档.md');
|
|
38
|
+
const c1Path = path.join(iterationDir, 'C1_版本需求清单.md');
|
|
39
|
+
const c0Path = path.join(iterationDir, 'C0_版本范围声明.md');
|
|
40
|
+
const b3Path = path.join(iterationDir, 'B3_规划冻结归档.md');
|
|
41
|
+
const b2Path = path.join(iterationDir, 'B2_规划拆解与范围界定.md');
|
|
42
|
+
const b1Path = path.join(iterationDir, 'B1_需求规划草案.md');
|
|
43
|
+
|
|
44
|
+
const hasC3 = await fs.pathExists(c3Path);
|
|
45
|
+
const hasC1 = await fs.pathExists(c1Path);
|
|
46
|
+
const hasC0 = await fs.pathExists(c0Path);
|
|
47
|
+
const hasB3 = await fs.pathExists(b3Path);
|
|
48
|
+
const hasB2 = await fs.pathExists(b2Path);
|
|
49
|
+
const hasB1 = await fs.pathExists(b1Path);
|
|
50
|
+
|
|
51
|
+
console.log(chalk.gray('当前文档状态:'));
|
|
52
|
+
console.log(` B1: ${hasB1 ? '✅' : '❌'} B2: ${hasB2 ? '✅' : '❌'} B3: ${hasB3 ? '✅' : '❌'}`);
|
|
53
|
+
console.log(` C0: ${hasC0 ? '✅' : '❌'} C1: ${hasC1 ? '✅' : '❌'} C3: ${hasC3 ? '✅' : '❌'}`);
|
|
54
|
+
console.log('');
|
|
55
|
+
|
|
56
|
+
// 根据状态给出不同的引导
|
|
57
|
+
if (hasC3) {
|
|
58
|
+
// 版本已冻结,需要创建 C2 记录变更
|
|
59
|
+
console.log(chalk.yellow('⚠️ 版本已冻结 (C3 已存在)'));
|
|
60
|
+
console.log(chalk.bold('\n这种情况下的变更需要:'));
|
|
61
|
+
console.log('1. 创建 C2_版本变更说明.md 记录变更');
|
|
62
|
+
console.log('2. 评估变更对版本目标的影响');
|
|
63
|
+
console.log('3. 如果变更较大,可能需要重新执行 R2 审视\n');
|
|
64
|
+
|
|
65
|
+
const answer = await inquirer.prompt([
|
|
66
|
+
{
|
|
67
|
+
type: 'confirm',
|
|
68
|
+
name: 'createC2',
|
|
69
|
+
message: '是否创建 C2 记录此次变更?',
|
|
70
|
+
default: true
|
|
71
|
+
}
|
|
72
|
+
]);
|
|
73
|
+
|
|
74
|
+
if (answer.createC2) {
|
|
75
|
+
// 调用 version 模块创建 C2
|
|
76
|
+
const version = require('./version');
|
|
77
|
+
await version('create', 'C2', {});
|
|
78
|
+
|
|
79
|
+
// 记录到对话归档
|
|
80
|
+
await dialog.logDialog('change', 'create_c2', {
|
|
81
|
+
type: 'change_request',
|
|
82
|
+
stage: 'after_c3_freeze',
|
|
83
|
+
action: '创建 C2 记录变更'
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
} else if (hasC1 || hasC0) {
|
|
88
|
+
// 在 C 阶段但未冻结
|
|
89
|
+
console.log(chalk.yellow('⚠️ 当前在 C 阶段(版本需求阶段)'));
|
|
90
|
+
console.log(chalk.bold('\n根据规范,C 阶段禁止新增规划外的需求。\n'));
|
|
91
|
+
|
|
92
|
+
const answer = await inquirer.prompt([
|
|
93
|
+
{
|
|
94
|
+
type: 'list',
|
|
95
|
+
name: 'changeType',
|
|
96
|
+
message: '您的变更属于哪种类型?',
|
|
97
|
+
choices: [
|
|
98
|
+
{ name: '细化现有需求(补充验收标准/边界情况)- 允许', value: 'refine' },
|
|
99
|
+
{ name: '新增规划外需求 - 禁止,需解冻 B3', value: 'new_requirement' },
|
|
100
|
+
{ name: '调整需求优先级 - 需记录并评估', value: 'priority' },
|
|
101
|
+
{ name: '删除/裁剪需求 - 需记录并评估', value: 'remove' }
|
|
102
|
+
]
|
|
103
|
+
}
|
|
104
|
+
]);
|
|
105
|
+
|
|
106
|
+
if (answer.changeType === 'refine') {
|
|
107
|
+
console.log(chalk.green('\n✓ 细化现有需求是允许的'));
|
|
108
|
+
console.log('请直接修改 C1_版本需求清单.md');
|
|
109
|
+
console.log('修改后建议重新执行 R2 审视: prd review r2\n');
|
|
110
|
+
} else if (answer.changeType === 'new_requirement') {
|
|
111
|
+
console.log(chalk.red('\n✗ C 阶段禁止新增规划外需求!\n'));
|
|
112
|
+
console.log('您有两个选择:');
|
|
113
|
+
console.log('1. 将新需求放入下一轮迭代的 B1 规划');
|
|
114
|
+
console.log('2. 解冻当前 B3,重新走 R1 审视流程\n');
|
|
115
|
+
console.log(chalk.yellow('建议:如果不是紧急需求,推荐放入下一轮迭代。'));
|
|
116
|
+
} else {
|
|
117
|
+
console.log(chalk.yellow('\n⚠️ 此类变更需要记录'));
|
|
118
|
+
console.log('1. 先在 C1 中做相应修改');
|
|
119
|
+
console.log('2. 重新执行 R2 审视: prd review r2');
|
|
120
|
+
console.log('3. 如果已有 C3,需要创建 C2 记录变更\n');
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// 记录到对话归档
|
|
124
|
+
await dialog.logDialog('change', 'change_request', {
|
|
125
|
+
type: 'change_request',
|
|
126
|
+
stage: 'c_phase',
|
|
127
|
+
changeType: answer.changeType
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
} else if (hasB3) {
|
|
131
|
+
// B3 已冻结,正准备进入 C 阶段
|
|
132
|
+
console.log(chalk.yellow('⚠️ 规划已冻结 (B3 已存在),尚未开始版本需求'));
|
|
133
|
+
console.log(chalk.bold('\n如果需要变更规划,您需要:'));
|
|
134
|
+
console.log('1. 解冻 B3(删除 B3 文件)');
|
|
135
|
+
console.log('2. 修改 B1/B2');
|
|
136
|
+
console.log('3. 重新执行 R1 审视');
|
|
137
|
+
console.log('4. 重新冻结 B3\n');
|
|
138
|
+
console.log(chalk.yellow('建议:除非必要,不建议解冻已冻结的规划。'));
|
|
139
|
+
|
|
140
|
+
} else if (hasB1 || hasB2) {
|
|
141
|
+
// 还在规划阶段
|
|
142
|
+
console.log(chalk.green('✓ 当前在规划阶段(B1/B2),可以自由调整'));
|
|
143
|
+
console.log('\n直接修改 B1 或 B2 即可,无需特殊流程。');
|
|
144
|
+
console.log('修改后需要重新执行 R1 审视: prd review r1\n');
|
|
145
|
+
|
|
146
|
+
} else {
|
|
147
|
+
// 还没开始
|
|
148
|
+
console.log(chalk.green('✓ 当前迭代刚开始,可以自由规划'));
|
|
149
|
+
console.log('\n请先创建 B1: prd plan create B1\n');
|
|
150
|
+
}
|
|
151
|
+
};
|