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,365 @@
|
|
|
1
|
+
const fs = require('fs-extra');
|
|
2
|
+
const path = require('path');
|
|
3
|
+
const chalk = require('chalk');
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* 生成或更新 P1 迭代索引
|
|
7
|
+
* 自动从文档元信息汇总生成索引
|
|
8
|
+
*/
|
|
9
|
+
module.exports = async function () {
|
|
10
|
+
const configPath = path.join(process.cwd(), '.prd-config.json');
|
|
11
|
+
|
|
12
|
+
if (!await fs.pathExists(configPath)) {
|
|
13
|
+
console.log(chalk.red('✗ 当前目录不是一个 PRD 项目'));
|
|
14
|
+
return;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const config = await fs.readJSON(configPath);
|
|
18
|
+
|
|
19
|
+
console.log(chalk.blue('正在生成迭代索引...'));
|
|
20
|
+
|
|
21
|
+
// 扫描所有迭代目录
|
|
22
|
+
const iterationBaseDir = path.join(process.cwd(), '02_迭代记录');
|
|
23
|
+
const iterations = [];
|
|
24
|
+
|
|
25
|
+
if (await fs.pathExists(iterationBaseDir)) {
|
|
26
|
+
const dirs = await fs.readdir(iterationBaseDir);
|
|
27
|
+
|
|
28
|
+
for (const dir of dirs) {
|
|
29
|
+
const iterationDir = path.join(iterationBaseDir, dir);
|
|
30
|
+
const stat = await fs.stat(iterationDir);
|
|
31
|
+
|
|
32
|
+
if (stat.isDirectory() && dir.match(/第\d+轮迭代/)) {
|
|
33
|
+
const iterationData = await extractIterationInfo(iterationDir, dir);
|
|
34
|
+
iterations.push(iterationData);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// 生成 P1 索引文档
|
|
40
|
+
const p1Content = generateP1Index(iterations, config);
|
|
41
|
+
const p1Path = path.join(process.cwd(), '00_项目总览/P1_迭代索引.md');
|
|
42
|
+
|
|
43
|
+
await fs.writeFile(p1Path, p1Content);
|
|
44
|
+
|
|
45
|
+
console.log(chalk.green('✓ P1 迭代索引已生成'));
|
|
46
|
+
console.log(chalk.cyan(`文件位置: ${p1Path}\n`));
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* 提取单个迭代的信息
|
|
51
|
+
*/
|
|
52
|
+
async function extractIterationInfo(iterationDir, dirName) {
|
|
53
|
+
const iterationNumber = dirName.match(/第(\d+)轮迭代/)[1];
|
|
54
|
+
|
|
55
|
+
const iteration = {
|
|
56
|
+
number: parseInt(iterationNumber),
|
|
57
|
+
name: dirName,
|
|
58
|
+
documents: {
|
|
59
|
+
R1: null,
|
|
60
|
+
B1: null,
|
|
61
|
+
B2: null,
|
|
62
|
+
B3: null,
|
|
63
|
+
R2: null,
|
|
64
|
+
C0: null,
|
|
65
|
+
C1: null,
|
|
66
|
+
C2: null,
|
|
67
|
+
C3: null
|
|
68
|
+
},
|
|
69
|
+
status: 'unknown',
|
|
70
|
+
createdAt: null,
|
|
71
|
+
frozenAt: null
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
// 扫描文档
|
|
75
|
+
const files = await fs.readdir(iterationDir);
|
|
76
|
+
|
|
77
|
+
for (const file of files) {
|
|
78
|
+
const filePath = path.join(iterationDir, file);
|
|
79
|
+
|
|
80
|
+
if (file.includes('R1_')) iteration.documents.R1 = file;
|
|
81
|
+
if (file.includes('B1_')) iteration.documents.B1 = file;
|
|
82
|
+
if (file.includes('B2_')) iteration.documents.B2 = file;
|
|
83
|
+
if (file.includes('B3_')) {
|
|
84
|
+
iteration.documents.B3 = file;
|
|
85
|
+
// 读取冻结时间
|
|
86
|
+
const content = await fs.readFile(filePath, 'utf-8');
|
|
87
|
+
const match = content.match(/冻结时间[:\s]*([^\n]+)/);
|
|
88
|
+
if (match) iteration.frozenAt = match[1].trim();
|
|
89
|
+
}
|
|
90
|
+
if (file.includes('R2_')) iteration.documents.R2 = file;
|
|
91
|
+
if (file.includes('C0_')) iteration.documents.C0 = file;
|
|
92
|
+
if (file.includes('C1_')) iteration.documents.C1 = file;
|
|
93
|
+
if (file.includes('C2_')) iteration.documents.C2 = file;
|
|
94
|
+
if (file.includes('C3_')) {
|
|
95
|
+
iteration.documents.C3 = file;
|
|
96
|
+
if (!iteration.frozenAt) {
|
|
97
|
+
const content = await fs.readFile(filePath, 'utf-8');
|
|
98
|
+
const match = content.match(/冻结时间[:\s]*([^\n]+)/);
|
|
99
|
+
if (match) iteration.frozenAt = match[1].trim();
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// 判断状态
|
|
105
|
+
iteration.status = determineIterationStatus(iteration.documents);
|
|
106
|
+
|
|
107
|
+
return iteration;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* 判断迭代状态
|
|
112
|
+
*/
|
|
113
|
+
function determineIterationStatus(docs) {
|
|
114
|
+
if (docs.C3) return '✅ 已完成';
|
|
115
|
+
if (docs.C1 || docs.C0) return '🔄 版本阶段';
|
|
116
|
+
if (docs.B3) return '📋 规划已冻结';
|
|
117
|
+
if (docs.B2 || docs.B1) return '💡 规划中';
|
|
118
|
+
return '🆕 刚启动';
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* 生成 P1 索引内容
|
|
123
|
+
*/
|
|
124
|
+
function generateP1Index(iterations, config) {
|
|
125
|
+
const now = new Date().toLocaleString('zh-CN');
|
|
126
|
+
|
|
127
|
+
let content = `# P1_迭代索引
|
|
128
|
+
|
|
129
|
+
**生成时间**: ${now}
|
|
130
|
+
**项目名称**: ${config.projectName}
|
|
131
|
+
**当前迭代**: 第 ${config.currentIteration} 轮
|
|
132
|
+
|
|
133
|
+
---
|
|
134
|
+
|
|
135
|
+
## 📋 索引说明
|
|
136
|
+
|
|
137
|
+
本文档自动生成,提供所有迭代的快速导航。
|
|
138
|
+
|
|
139
|
+
**作用**:
|
|
140
|
+
- 快速了解项目迭代历史
|
|
141
|
+
- 查看每轮迭代的文档完整性
|
|
142
|
+
- 追踪冻结点和版本关系
|
|
143
|
+
|
|
144
|
+
---
|
|
145
|
+
|
|
146
|
+
## 🔄 迭代总览
|
|
147
|
+
|
|
148
|
+
**总迭代数**: ${iterations.length} 轮
|
|
149
|
+
**已完成**: ${iterations.filter(i => i.status === '✅ 已完成').length} 轮
|
|
150
|
+
**进行中**: ${iterations.filter(i => i.status !== '✅ 已完成').length} 轮
|
|
151
|
+
|
|
152
|
+
---
|
|
153
|
+
|
|
154
|
+
## 📊 迭代清单
|
|
155
|
+
|
|
156
|
+
`;
|
|
157
|
+
|
|
158
|
+
// 为每个迭代生成条目
|
|
159
|
+
for (const iter of iterations.sort((a, b) => a.number - b.number)) {
|
|
160
|
+
content += generateIterationEntry(iter);
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
content += `\n---
|
|
164
|
+
|
|
165
|
+
## 🔍 文档完整性检查
|
|
166
|
+
|
|
167
|
+
`;
|
|
168
|
+
|
|
169
|
+
// 文档完整性统计
|
|
170
|
+
content += generateCompletenessCheck(iterations);
|
|
171
|
+
|
|
172
|
+
content += `\n---
|
|
173
|
+
|
|
174
|
+
## ⚠️ 违规检查
|
|
175
|
+
|
|
176
|
+
`;
|
|
177
|
+
|
|
178
|
+
// 违规链路检查
|
|
179
|
+
content += generateViolationCheck(iterations);
|
|
180
|
+
|
|
181
|
+
content += `\n---
|
|
182
|
+
|
|
183
|
+
## 📝 下一步
|
|
184
|
+
|
|
185
|
+
`;
|
|
186
|
+
|
|
187
|
+
if (config.currentIteration === 0) {
|
|
188
|
+
content += `- 创建基线文档(A0/A1/A2)\n`;
|
|
189
|
+
content += `- 执行 R0 基线审视\n`;
|
|
190
|
+
content += `- 开始第一轮迭代:\`prd iteration new\`\n`;
|
|
191
|
+
} else {
|
|
192
|
+
const current = iterations.find(i => i.number === config.currentIteration);
|
|
193
|
+
if (current) {
|
|
194
|
+
content += generateNextSteps(current);
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
content += `\n---
|
|
199
|
+
|
|
200
|
+
**此文档由 AI 自动生成,请勿手动编辑索引部分。**
|
|
201
|
+
**如需更新,运行:\`prd status\` 或 \`prd index update\`**
|
|
202
|
+
`;
|
|
203
|
+
|
|
204
|
+
return content;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
/**
|
|
208
|
+
* 生成单个迭代条目
|
|
209
|
+
*/
|
|
210
|
+
function generateIterationEntry(iter) {
|
|
211
|
+
let entry = `### 第 ${iter.number} 轮迭代 ${iter.status}\n\n`;
|
|
212
|
+
|
|
213
|
+
if (iter.frozenAt) {
|
|
214
|
+
entry += `**冻结时间**: ${iter.frozenAt}\n\n`;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
entry += `**文档清单**:\n\n`;
|
|
218
|
+
entry += `| 阶段 | 文档 | 状态 |\n`;
|
|
219
|
+
entry += `|-----|-----|-----|\n`;
|
|
220
|
+
|
|
221
|
+
const stages = [
|
|
222
|
+
{ key: 'R1', name: 'R1 规划审视' },
|
|
223
|
+
{ key: 'B1', name: 'B1 规划草案' },
|
|
224
|
+
{ key: 'B2', name: 'B2 规划拆解' },
|
|
225
|
+
{ key: 'B3', name: 'B3 规划冻结' },
|
|
226
|
+
{ key: 'R2', name: 'R2 版本审视' },
|
|
227
|
+
{ key: 'C0', name: 'C0 版本范围' },
|
|
228
|
+
{ key: 'C1', name: 'C1 版本需求' },
|
|
229
|
+
{ key: 'C2', name: 'C2 版本变更' },
|
|
230
|
+
{ key: 'C3', name: 'C3 版本冻结' }
|
|
231
|
+
];
|
|
232
|
+
|
|
233
|
+
for (const stage of stages) {
|
|
234
|
+
const exists = iter.documents[stage.key];
|
|
235
|
+
entry += `| ${stage.name} | ${exists || '-'} | ${exists ? '✅' : '○'} |\n`;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
entry += `\n**关系映射**:\n`;
|
|
239
|
+
if (iter.documents.B3) {
|
|
240
|
+
entry += `- B3(规划)`;
|
|
241
|
+
if (iter.documents.C0 || iter.documents.C1) {
|
|
242
|
+
entry += ` → C0/C1(版本)`;
|
|
243
|
+
}
|
|
244
|
+
if (iter.documents.C3) {
|
|
245
|
+
entry += ` → C3(冻结)`;
|
|
246
|
+
}
|
|
247
|
+
entry += `\n`;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
entry += `\n**文档位置**: \`02_迭代记录/${iter.name}/\`\n\n`;
|
|
251
|
+
entry += `---\n\n`;
|
|
252
|
+
|
|
253
|
+
return entry;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
/**
|
|
257
|
+
* 文档完整性检查
|
|
258
|
+
*/
|
|
259
|
+
function generateCompletenessCheck(iterations) {
|
|
260
|
+
let check = ``;
|
|
261
|
+
|
|
262
|
+
for (const iter of iterations) {
|
|
263
|
+
const issues = [];
|
|
264
|
+
|
|
265
|
+
// 检查 B3 → C 的链路
|
|
266
|
+
if (iter.documents.B3 && !(iter.documents.C0 || iter.documents.C1)) {
|
|
267
|
+
issues.push('⚠️ 有 B3 但缺少 C0/C1');
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
// 检查 C1 → C3 的链路
|
|
271
|
+
if (iter.documents.C1 && !iter.documents.C3) {
|
|
272
|
+
issues.push('⚠️ 有 C1 但未冻结为 C3');
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
// 检查 R1
|
|
276
|
+
if ((iter.documents.B1 || iter.documents.B2) && !iter.documents.R1) {
|
|
277
|
+
issues.push('💡 提示:建议执行 R1 审视');
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
// 检查 R2
|
|
281
|
+
if (iter.documents.C1 && !iter.documents.R2) {
|
|
282
|
+
issues.push('💡 提示:建议执行 R2 审视');
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
if (issues.length > 0) {
|
|
286
|
+
check += `**第 ${iter.number} 轮迭代**:\n`;
|
|
287
|
+
for (const issue of issues) {
|
|
288
|
+
check += `- ${issue}\n`;
|
|
289
|
+
}
|
|
290
|
+
check += `\n`;
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
if (check === '') {
|
|
295
|
+
check = `✅ 所有迭代文档完整,无缺失。\n`;
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
return check;
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
/**
|
|
302
|
+
* 违规检查
|
|
303
|
+
*/
|
|
304
|
+
function generateViolationCheck(iterations) {
|
|
305
|
+
let violations = ``;
|
|
306
|
+
|
|
307
|
+
for (const iter of iterations) {
|
|
308
|
+
const issues = [];
|
|
309
|
+
|
|
310
|
+
// 检查"未冻结却进入下游"
|
|
311
|
+
if (!iter.documents.B3 && (iter.documents.C0 || iter.documents.C1)) {
|
|
312
|
+
issues.push('🔴 违规:没有 B3 但创建了 C0/C1(违反规范)');
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
if (!iter.documents.C3 && iter.number < Math.max(...iterations.map(i => i.number))) {
|
|
316
|
+
// 不是最新迭代但没有 C3
|
|
317
|
+
issues.push('⚠️ 注意:迭代未完成就启动了新轮次');
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
if (issues.length > 0) {
|
|
321
|
+
violations += `**第 ${iter.number} 轮迭代**:\n`;
|
|
322
|
+
for (const issue of issues) {
|
|
323
|
+
violations += `- ${issue}\n`;
|
|
324
|
+
}
|
|
325
|
+
violations += `\n`;
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
if (violations === '') {
|
|
330
|
+
violations = `✅ 未发现违规链路。\n`;
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
return violations;
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
/**
|
|
337
|
+
* 生成下一步建议
|
|
338
|
+
*/
|
|
339
|
+
function generateNextSteps(current) {
|
|
340
|
+
let steps = `**当前迭代(第 ${current.number} 轮)下一步**:\n\n`;
|
|
341
|
+
|
|
342
|
+
if (current.status === '✅ 已完成') {
|
|
343
|
+
steps += `- 当前迭代已完成\n`;
|
|
344
|
+
steps += `- 如需新迭代:\`prd iteration new\`\n`;
|
|
345
|
+
} else if (current.status === '📋 规划已冻结') {
|
|
346
|
+
steps += `- 创建版本范围:\`prd version create C0\`\n`;
|
|
347
|
+
steps += `- 创建版本需求:\`prd version create C1\`\n`;
|
|
348
|
+
} else if (current.status === '💡 规划中') {
|
|
349
|
+
if (!current.documents.R1) {
|
|
350
|
+
steps += `- 执行 R1 审视:\`prd review r1\`\n`;
|
|
351
|
+
}
|
|
352
|
+
if (current.documents.B2 && !current.documents.B3) {
|
|
353
|
+
steps += `- 冻结规划:\`prd plan freeze\`\n`;
|
|
354
|
+
}
|
|
355
|
+
} else if (current.status === '🔄 版本阶段') {
|
|
356
|
+
if (!current.documents.R2) {
|
|
357
|
+
steps += `- 执行 R2 审视:\`prd review r2\`\n`;
|
|
358
|
+
}
|
|
359
|
+
if (current.documents.R2 && !current.documents.C3) {
|
|
360
|
+
steps += `- 冻结版本:\`prd version freeze\`\n`;
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
return steps;
|
|
365
|
+
}
|
package/commands/init.js
ADDED
|
@@ -0,0 +1,357 @@
|
|
|
1
|
+
const fs = require('fs-extra');
|
|
2
|
+
const path = require('path');
|
|
3
|
+
const chalk = require('chalk');
|
|
4
|
+
|
|
5
|
+
module.exports = async function (projectName) {
|
|
6
|
+
const projectPath = path.join(process.cwd(), projectName);
|
|
7
|
+
|
|
8
|
+
try {
|
|
9
|
+
// 检查目录是否已存在
|
|
10
|
+
if (await fs.pathExists(projectPath)) {
|
|
11
|
+
console.log(chalk.red(`✗ 目录 ${projectName} 已存在`));
|
|
12
|
+
return;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
console.log(chalk.blue(`正在创建项目: ${projectName}...`));
|
|
16
|
+
|
|
17
|
+
// 创建项目目录结构
|
|
18
|
+
const directories = [
|
|
19
|
+
'00_项目总览',
|
|
20
|
+
'01_产品基线',
|
|
21
|
+
'02_迭代记录',
|
|
22
|
+
'98_对话归档',
|
|
23
|
+
'99_归档区/历史参考与废弃文档',
|
|
24
|
+
'.agent/workflows'
|
|
25
|
+
];
|
|
26
|
+
|
|
27
|
+
for (const dir of directories) {
|
|
28
|
+
await fs.ensureDir(path.join(projectPath, dir));
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// 创建项目配置文件
|
|
32
|
+
const config = {
|
|
33
|
+
projectName,
|
|
34
|
+
createdAt: new Date().toISOString(),
|
|
35
|
+
currentIteration: 0,
|
|
36
|
+
workflow: 'A → R → B → C',
|
|
37
|
+
stages: {
|
|
38
|
+
baseline: { completed: false, documents: [] },
|
|
39
|
+
planning: { completed: false, documents: [] },
|
|
40
|
+
version: { completed: false, documents: [] }
|
|
41
|
+
}
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
await fs.writeJSON(
|
|
45
|
+
path.join(projectPath, '.prd-config.json'),
|
|
46
|
+
config,
|
|
47
|
+
{ spaces: 2 }
|
|
48
|
+
);
|
|
49
|
+
|
|
50
|
+
// 创建 package.json(让其他用户可以通过 npm install 安装 CLI)
|
|
51
|
+
const packageJson = {
|
|
52
|
+
name: projectName.toLowerCase().replace(/[^a-z0-9]/g, '-'),
|
|
53
|
+
version: '1.0.0',
|
|
54
|
+
description: `${projectName} - PRD 需求管理项目`,
|
|
55
|
+
private: true,
|
|
56
|
+
scripts: {
|
|
57
|
+
prd: 'prd',
|
|
58
|
+
status: 'prd status',
|
|
59
|
+
help: 'prd --help'
|
|
60
|
+
},
|
|
61
|
+
dependencies: {
|
|
62
|
+
'prd-workflow-cli': '^1.1.12'
|
|
63
|
+
}
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
await fs.writeJSON(
|
|
67
|
+
path.join(projectPath, 'package.json'),
|
|
68
|
+
packageJson,
|
|
69
|
+
{ spaces: 2 }
|
|
70
|
+
);
|
|
71
|
+
|
|
72
|
+
// 创建 P0 项目基本信息模板
|
|
73
|
+
const p0Template = `# P0_项目基本信息
|
|
74
|
+
|
|
75
|
+
**创建时间**: ${new Date().toLocaleString('zh-CN')}
|
|
76
|
+
**项目名称**: ${projectName}
|
|
77
|
+
**文档状态**: 草案
|
|
78
|
+
|
|
79
|
+
---
|
|
80
|
+
|
|
81
|
+
## 文档说明
|
|
82
|
+
|
|
83
|
+
**目的**:
|
|
84
|
+
- 明确项目是否应该存在
|
|
85
|
+
- 确认项目目标是否成立
|
|
86
|
+
- 识别关键干系人
|
|
87
|
+
|
|
88
|
+
**填写要求**:
|
|
89
|
+
- 只填写事实,不填愿景
|
|
90
|
+
- 目标要可检验
|
|
91
|
+
- 干系人要具体到人
|
|
92
|
+
|
|
93
|
+
---
|
|
94
|
+
|
|
95
|
+
## 1. 项目基本信息
|
|
96
|
+
|
|
97
|
+
### 1.1 项目定位
|
|
98
|
+
|
|
99
|
+
**项目全称**: ${projectName}
|
|
100
|
+
|
|
101
|
+
**项目简述**:
|
|
102
|
+
<!-- 一句话说明这个项目是什么 -->
|
|
103
|
+
|
|
104
|
+
**所属产品线**:
|
|
105
|
+
<!-- 例如:核心业务系统、辅助工具、创新试点 -->
|
|
106
|
+
|
|
107
|
+
**项目级别**:
|
|
108
|
+
- [ ] 战略级(公司级重点)
|
|
109
|
+
- [ ] 业务级(部门级重点)
|
|
110
|
+
- [ ] 支撑级(基础能力)
|
|
111
|
+
|
|
112
|
+
---
|
|
113
|
+
|
|
114
|
+
## 2. 项目目标
|
|
115
|
+
|
|
116
|
+
### 2.1 核心目标
|
|
117
|
+
|
|
118
|
+
**要解决的主要问题**:
|
|
119
|
+
<!-- 不超过 3 个核心问题 -->
|
|
120
|
+
1.
|
|
121
|
+
2.
|
|
122
|
+
3.
|
|
123
|
+
|
|
124
|
+
**成功标准**:
|
|
125
|
+
<!-- 如何判断项目成功?用可衡量的指标 -->
|
|
126
|
+
- 指标 1: ______
|
|
127
|
+
- 指标 2: ______
|
|
128
|
+
- 指标 3: ______
|
|
129
|
+
|
|
130
|
+
### 2.2 目标合理性确认
|
|
131
|
+
|
|
132
|
+
**为什么现在做这个项目?**
|
|
133
|
+
<!-- 时机/背景/触发因素 -->
|
|
134
|
+
|
|
135
|
+
**不做会怎样?**
|
|
136
|
+
<!-- 说明紧迫性 -->
|
|
137
|
+
|
|
138
|
+
---
|
|
139
|
+
|
|
140
|
+
## 3. 干系人
|
|
141
|
+
|
|
142
|
+
### 3.1 核心干系人
|
|
143
|
+
|
|
144
|
+
**PM(产品负责人)**:
|
|
145
|
+
- 姓名: ____________
|
|
146
|
+
- 职责: 项目最终决策
|
|
147
|
+
- 联系方式: ____________
|
|
148
|
+
|
|
149
|
+
**技术负责人**:
|
|
150
|
+
- 姓名: ____________
|
|
151
|
+
- 职责: 技术可行性把关
|
|
152
|
+
- 联系方式: ____________
|
|
153
|
+
|
|
154
|
+
**业务方**:
|
|
155
|
+
- 部门: ____________
|
|
156
|
+
- 联系人: ____________
|
|
157
|
+
- 职责: 业务需求确认
|
|
158
|
+
|
|
159
|
+
### 3.2 相关方
|
|
160
|
+
|
|
161
|
+
**可能受影响的团队/系统**:
|
|
162
|
+
<!-- 列出会受此项目影响的其他团队或系统 -->
|
|
163
|
+
|
|
164
|
+
---
|
|
165
|
+
|
|
166
|
+
## 4. 项目约束
|
|
167
|
+
|
|
168
|
+
### 4.1 时间约束
|
|
169
|
+
|
|
170
|
+
**期望启动时间**: ____________
|
|
171
|
+
**期望交付时间**: ____________
|
|
172
|
+
|
|
173
|
+
### 4.2 资源约束
|
|
174
|
+
|
|
175
|
+
**已知的资源限制**:
|
|
176
|
+
<!-- 人力/预算/技术限制 -->
|
|
177
|
+
|
|
178
|
+
### 4.3 依赖条件
|
|
179
|
+
|
|
180
|
+
**项目依赖**:
|
|
181
|
+
<!-- 需要其他项目/系统先完成什么? -->
|
|
182
|
+
|
|
183
|
+
---
|
|
184
|
+
|
|
185
|
+
## 5. 项目状态
|
|
186
|
+
|
|
187
|
+
**当前状态**: 初始化
|
|
188
|
+
**当前迭代**: 0 轮
|
|
189
|
+
**下一步**: 创建 A 类基线文档
|
|
190
|
+
|
|
191
|
+
---
|
|
192
|
+
|
|
193
|
+
## 6. PM 确认
|
|
194
|
+
|
|
195
|
+
- [ ] 项目目标已明确且合理
|
|
196
|
+
- [ ] 干系人已确认
|
|
197
|
+
- [ ] 约束条件已记录
|
|
198
|
+
- [ ] 可以开始基线建立
|
|
199
|
+
|
|
200
|
+
**PM 签字**: _____________
|
|
201
|
+
**日期**: _____________
|
|
202
|
+
|
|
203
|
+
---
|
|
204
|
+
|
|
205
|
+
## 备注
|
|
206
|
+
|
|
207
|
+
<!-- 其他需要说明的重要信息 -->
|
|
208
|
+
`;
|
|
209
|
+
|
|
210
|
+
await fs.writeFile(
|
|
211
|
+
path.join(projectPath, '00_项目总览/P0_项目基本信息.md'),
|
|
212
|
+
p0Template
|
|
213
|
+
);
|
|
214
|
+
|
|
215
|
+
// 复制工作流模板
|
|
216
|
+
const workflowsDir = path.join(__dirname, '../.agent/workflows');
|
|
217
|
+
if (await fs.pathExists(workflowsDir)) {
|
|
218
|
+
await fs.copy(
|
|
219
|
+
workflowsDir,
|
|
220
|
+
path.join(projectPath, '.agent/workflows')
|
|
221
|
+
);
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
// 复制对话归档模板
|
|
225
|
+
const dialogTemplateSource = path.join(__dirname, '../templates/dialog-template.md');
|
|
226
|
+
if (await fs.pathExists(dialogTemplateSource)) {
|
|
227
|
+
await fs.copy(
|
|
228
|
+
dialogTemplateSource,
|
|
229
|
+
path.join(projectPath, '98_对话归档/AI_对话归档模板.md')
|
|
230
|
+
);
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
// 复制 AI 规则文件
|
|
234
|
+
// .cursorrules (Cursor IDE)
|
|
235
|
+
const cursorrules = path.join(__dirname, '../.cursorrules');
|
|
236
|
+
if (await fs.pathExists(cursorrules)) {
|
|
237
|
+
await fs.copy(
|
|
238
|
+
cursorrules,
|
|
239
|
+
path.join(projectPath, '.cursorrules')
|
|
240
|
+
);
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
// .antigravity/rules.md (Antigravity)
|
|
244
|
+
const antigravityDir = path.join(__dirname, '../.antigravity');
|
|
245
|
+
if (await fs.pathExists(antigravityDir)) {
|
|
246
|
+
await fs.copy(
|
|
247
|
+
antigravityDir,
|
|
248
|
+
path.join(projectPath, '.antigravity')
|
|
249
|
+
);
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
// AI-GUIDE.md (通用 AI 指引)
|
|
253
|
+
const aiGuide = path.join(__dirname, '../AI-GUIDE.md');
|
|
254
|
+
if (await fs.pathExists(aiGuide)) {
|
|
255
|
+
await fs.copy(
|
|
256
|
+
aiGuide,
|
|
257
|
+
path.join(projectPath, 'AI-GUIDE.md')
|
|
258
|
+
);
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
// 创建 README
|
|
262
|
+
const readme = `# ${projectName}
|
|
263
|
+
|
|
264
|
+
本项目采用规范化的产品需求管理流程
|
|
265
|
+
|
|
266
|
+
## 📁 目录结构
|
|
267
|
+
|
|
268
|
+
\`\`\`
|
|
269
|
+
${projectName}/
|
|
270
|
+
├── 00_项目总览/ # 项目基本信息
|
|
271
|
+
├── 01_产品基线/ # A 类文档:现状基线
|
|
272
|
+
├── 02_迭代记录/ # 各轮迭代的 B、C 类文档
|
|
273
|
+
│ ├── 第01轮迭代/
|
|
274
|
+
│ ├── 第02轮迭代/
|
|
275
|
+
│ └── ...
|
|
276
|
+
└── 99_归档区/ # 历史文档归档
|
|
277
|
+
\`\`\`
|
|
278
|
+
|
|
279
|
+
## 🔄 工作流程
|
|
280
|
+
|
|
281
|
+
1. **A 类 - 建立基线** (01_产品基线/)
|
|
282
|
+
- A0: 产品基础与范围说明
|
|
283
|
+
- A1: 已上线功能与流程清单
|
|
284
|
+
- A2: 存量反馈与数据汇总
|
|
285
|
+
- R0: 基线审视报告
|
|
286
|
+
|
|
287
|
+
2. **B 类 - 需求规划** (02_迭代记录/第N轮迭代/)
|
|
288
|
+
- R1: 规划前审视(启动条件检查)
|
|
289
|
+
- B1: 需求规划草案
|
|
290
|
+
- B2: 规划拆解与范围界定
|
|
291
|
+
- R1: 规划审视(冻结前审查)
|
|
292
|
+
- B3: 规划冻结归档
|
|
293
|
+
|
|
294
|
+
3. **C 类 - 版本需求** (02_迭代记录/第N轮迭代/)
|
|
295
|
+
- R2: 版本审视
|
|
296
|
+
- C0: 版本范围声明
|
|
297
|
+
- C1: 版本需求清单
|
|
298
|
+
- C3: 版本冻结归档
|
|
299
|
+
|
|
300
|
+
## 🛠️ 使用 CLI 工具
|
|
301
|
+
|
|
302
|
+
\`\`\`bash
|
|
303
|
+
# 查看项目状态
|
|
304
|
+
prd status
|
|
305
|
+
|
|
306
|
+
# 创建基线文档
|
|
307
|
+
prd baseline create A0
|
|
308
|
+
|
|
309
|
+
# 开始新迭代
|
|
310
|
+
prd iteration new
|
|
311
|
+
|
|
312
|
+
# 创建规划文档
|
|
313
|
+
prd plan create B1
|
|
314
|
+
|
|
315
|
+
# 执行 R1 审视
|
|
316
|
+
prd review r1
|
|
317
|
+
|
|
318
|
+
# 冻结规划
|
|
319
|
+
prd plan freeze
|
|
320
|
+
\`\`\`
|
|
321
|
+
|
|
322
|
+
## 📝 关键原则
|
|
323
|
+
|
|
324
|
+
- **R1 是启动闸门**: 必须满足三个条件才能开始规划
|
|
325
|
+
- **B3 是决策冻结**: 规划一旦冻结不可随意更改
|
|
326
|
+
- **C 类不讨论方向**: 只执行已冻结的规划
|
|
327
|
+
- **审视是强制的**: R1/R2 必须通过才能进入下一阶段
|
|
328
|
+
|
|
329
|
+
---
|
|
330
|
+
创建时间: ${new Date().toLocaleString('zh-CN')}
|
|
331
|
+
`;
|
|
332
|
+
|
|
333
|
+
await fs.writeFile(
|
|
334
|
+
path.join(projectPath, 'README.md'),
|
|
335
|
+
readme
|
|
336
|
+
);
|
|
337
|
+
|
|
338
|
+
console.log(chalk.green('✓ 项目创建成功!'));
|
|
339
|
+
console.log('');
|
|
340
|
+
console.log(chalk.bold('📋 下一步操作(请按顺序执行):'));
|
|
341
|
+
console.log('');
|
|
342
|
+
console.log(chalk.cyan('第 1 步: 进入项目目录'));
|
|
343
|
+
console.log(` cd ${projectName}`);
|
|
344
|
+
console.log('');
|
|
345
|
+
console.log(chalk.cyan('第 2 步: 完善 P0_项目基本信息.md'));
|
|
346
|
+
console.log(chalk.gray(' 文件位置: 00_项目总览/P0_项目基本信息.md'));
|
|
347
|
+
console.log(chalk.gray(' 填写内容: 项目目标、干系人、约束条件等'));
|
|
348
|
+
console.log(chalk.yellow(' ⚠️ 必须完成 P0 填写后才能开始创建 A 类基线文档'));
|
|
349
|
+
console.log('');
|
|
350
|
+
console.log(chalk.cyan('第 3 步: 创建 A0 基线文档'));
|
|
351
|
+
console.log(' prd baseline create A0 # P0 填写完成后执行');
|
|
352
|
+
console.log('');
|
|
353
|
+
|
|
354
|
+
} catch (error) {
|
|
355
|
+
console.log(chalk.red('✗ 创建项目失败:'), error.message);
|
|
356
|
+
}
|
|
357
|
+
};
|