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,364 @@
|
|
|
1
|
+
const inquirer = require('inquirer');
|
|
2
|
+
const chalk = require('chalk');
|
|
3
|
+
const dialog = require('./dialog');
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* PM 确认模块
|
|
7
|
+
* 强制要求 PM 做决策,防止 AI 越权
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
module.exports = {
|
|
11
|
+
/**
|
|
12
|
+
* 文档创建前确认
|
|
13
|
+
*/
|
|
14
|
+
async confirmDocumentCreation(docType, purpose) {
|
|
15
|
+
console.log(chalk.yellow('\n⚠️ PM 确认点\n'));
|
|
16
|
+
console.log(chalk.bold(`准备创建文档: ${docType}`));
|
|
17
|
+
console.log(`目的: ${purpose}\n`);
|
|
18
|
+
|
|
19
|
+
console.log(chalk.cyan('【权责说明】'));
|
|
20
|
+
console.log('- PM 职责: 确认是否需要创建此文档');
|
|
21
|
+
console.log('- AI 职责: 提供模板和填写指引\n');
|
|
22
|
+
|
|
23
|
+
const answer = await inquirer.prompt([
|
|
24
|
+
{
|
|
25
|
+
type: 'confirm',
|
|
26
|
+
name: 'proceed',
|
|
27
|
+
message: '确认创建此文档?',
|
|
28
|
+
default: false
|
|
29
|
+
}
|
|
30
|
+
]);
|
|
31
|
+
|
|
32
|
+
await dialog.logPMConfirmation('document', `create_${docType}`,
|
|
33
|
+
answer.proceed ? 'approved' : 'rejected',
|
|
34
|
+
answer.proceed ? '确认创建' : '取消创建'
|
|
35
|
+
);
|
|
36
|
+
|
|
37
|
+
return answer.proceed;
|
|
38
|
+
},
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* R0 基线审视创建确认
|
|
42
|
+
* ⚠️ 重要:必须由 PM 确认才能创建 R0
|
|
43
|
+
*/
|
|
44
|
+
async confirmR0Creation() {
|
|
45
|
+
console.log(chalk.bold.yellow('\n━━━ PM 决策点:R0 基线审视创建 ━━━\n'));
|
|
46
|
+
|
|
47
|
+
console.log(chalk.cyan('【权责说明】'));
|
|
48
|
+
console.log('- PM 职责: ');
|
|
49
|
+
console.log(' 1. 确认 A0/A1 已填写完成且内容准确');
|
|
50
|
+
console.log(' 2. 确认需要进行基线审视');
|
|
51
|
+
console.log(' 3. 对基线内容负责\n');
|
|
52
|
+
|
|
53
|
+
console.log('- AI 职责: ');
|
|
54
|
+
console.log(' 1. 辅助执行审视');
|
|
55
|
+
console.log(' 2. 梳理用户路径');
|
|
56
|
+
console.log(' 3. 识别问题和机会\n');
|
|
57
|
+
|
|
58
|
+
console.log(chalk.red('- AI 禁止: '));
|
|
59
|
+
console.log(' ❌ 在未经 PM 确认的情况下创建 R0');
|
|
60
|
+
console.log(' ❌ R0 完成后自动创建后续文档\n');
|
|
61
|
+
|
|
62
|
+
const checks = await inquirer.prompt([
|
|
63
|
+
{
|
|
64
|
+
type: 'confirm',
|
|
65
|
+
name: 'baselineReady',
|
|
66
|
+
message: '确认 A0、A1 基线文档已填写完成?',
|
|
67
|
+
default: false
|
|
68
|
+
},
|
|
69
|
+
{
|
|
70
|
+
type: 'confirm',
|
|
71
|
+
name: 'proceedR0',
|
|
72
|
+
message: 'PM 确认:现在开始 R0 基线审视?',
|
|
73
|
+
default: false
|
|
74
|
+
}
|
|
75
|
+
]);
|
|
76
|
+
|
|
77
|
+
if (!checks.baselineReady || !checks.proceedR0) {
|
|
78
|
+
await dialog.logPMConfirmation('r0_baseline', 'create_r0', 'rejected',
|
|
79
|
+
`基线就绪:${checks.baselineReady}, PM确认:${checks.proceedR0}`
|
|
80
|
+
);
|
|
81
|
+
return false;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
await dialog.logPMConfirmation('r0_baseline', 'create_r0', 'approved',
|
|
85
|
+
'PM确认基线文档已完成,开始R0审视'
|
|
86
|
+
);
|
|
87
|
+
|
|
88
|
+
return true;
|
|
89
|
+
},
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* R1 启动条件确认
|
|
93
|
+
*/
|
|
94
|
+
async confirmR1Start() {
|
|
95
|
+
console.log(chalk.bold.yellow('\n━━━ PM 决策点:R1 启动条件检查 ━━━\n'));
|
|
96
|
+
|
|
97
|
+
console.log(chalk.cyan('【权责说明】'));
|
|
98
|
+
console.log('- PM 职责: 判断是否满足三个启动条件(不可替代)');
|
|
99
|
+
console.log('- AI 职责: 帮助校验,但不得替 PM 做"值得做"的判断\n');
|
|
100
|
+
|
|
101
|
+
console.log(chalk.bold('请 PM 确认以下三个条件:\n'));
|
|
102
|
+
|
|
103
|
+
const answers = await inquirer.prompt([
|
|
104
|
+
{
|
|
105
|
+
type: 'list',
|
|
106
|
+
name: 'condition1',
|
|
107
|
+
message: '条件1: 问题是否真实存在(可在A类文档中找到依据)?',
|
|
108
|
+
choices: [
|
|
109
|
+
{ name: '✓ 满足 - 问题真实存在', value: true },
|
|
110
|
+
{ name: '✗ 不满足 - 问题不够明确', value: false }
|
|
111
|
+
]
|
|
112
|
+
},
|
|
113
|
+
{
|
|
114
|
+
type: 'list',
|
|
115
|
+
name: 'condition2',
|
|
116
|
+
message: '条件2: 是否值得单独一轮规划(不是小修小补)?',
|
|
117
|
+
choices: [
|
|
118
|
+
{ name: '✓ 满足 - 值得独立规划', value: true },
|
|
119
|
+
{ name: '✗ 不满足 - 不需要独立规划', value: false }
|
|
120
|
+
]
|
|
121
|
+
},
|
|
122
|
+
{
|
|
123
|
+
type: 'list',
|
|
124
|
+
name: 'condition3',
|
|
125
|
+
message: '条件3: 问题是否已理解清楚(边界明确)?',
|
|
126
|
+
choices: [
|
|
127
|
+
{ name: '✓ 满足 - 边界清晰', value: true },
|
|
128
|
+
{ name: '✗ 不满足 - 还需要进一步理解', value: false }
|
|
129
|
+
]
|
|
130
|
+
}
|
|
131
|
+
]);
|
|
132
|
+
|
|
133
|
+
const allPassed = answers.condition1 && answers.condition2 && answers.condition3;
|
|
134
|
+
|
|
135
|
+
if (!allPassed) {
|
|
136
|
+
console.log(chalk.red('\n✗ 启动条件未全部满足\n'));
|
|
137
|
+
console.log(chalk.yellow('根据规范,必须三个条件全部满足才能开始规划'));
|
|
138
|
+
|
|
139
|
+
await dialog.logPMConfirmation('r1_start', 'check_conditions', 'rejected',
|
|
140
|
+
`条件1:${answers.condition1}, 条件2:${answers.condition2}, 条件3:${answers.condition3}`
|
|
141
|
+
);
|
|
142
|
+
|
|
143
|
+
return false;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
console.log(chalk.green('\n✓ 三个条件全部满足\n'));
|
|
147
|
+
|
|
148
|
+
const finalConfirm = await inquirer.prompt([
|
|
149
|
+
{
|
|
150
|
+
type: 'confirm',
|
|
151
|
+
name: 'proceed',
|
|
152
|
+
message: 'PM 最终确认:是否开启本轮规划?',
|
|
153
|
+
default: true
|
|
154
|
+
}
|
|
155
|
+
]);
|
|
156
|
+
|
|
157
|
+
await dialog.logPMConfirmation('r1_start', 'final_decision',
|
|
158
|
+
finalConfirm.proceed ? 'approved' : 'rejected',
|
|
159
|
+
`PM决定${finalConfirm.proceed ? '开启' : '不开启'}规划`
|
|
160
|
+
);
|
|
161
|
+
|
|
162
|
+
return finalConfirm.proceed;
|
|
163
|
+
},
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* R1 规划审视前确认
|
|
167
|
+
*/
|
|
168
|
+
async confirmR1Review() {
|
|
169
|
+
console.log(chalk.bold.yellow('\n━━━ 关键提醒:R1 规划审视的角色分工 ━━━\n'));
|
|
170
|
+
|
|
171
|
+
console.log(chalk.cyan('【权责说明】'));
|
|
172
|
+
console.log('- PM 职责: ');
|
|
173
|
+
console.log(' 1. 已完成 B1、B2 的填写(这是PM的责任)');
|
|
174
|
+
console.log(' 2. 给出最终结论:通过/不通过');
|
|
175
|
+
console.log(' 3. 对"是否冻结规划"负责\n');
|
|
176
|
+
|
|
177
|
+
console.log('- AI 职责: ');
|
|
178
|
+
console.log(' 1. 按5个维度进行审视');
|
|
179
|
+
console.log(' 2. 指出问题和风险');
|
|
180
|
+
console.log(' 3. 给出结构化审视意见\n');
|
|
181
|
+
|
|
182
|
+
console.log(chalk.red('- AI 禁止: '));
|
|
183
|
+
console.log(' 1. ❌ 替 PM 填写 B1、B2 内容');
|
|
184
|
+
console.log(' 2. ❌ 自行判定"可以冻结"');
|
|
185
|
+
console.log(' 3. ❌ 越权做任何决策\n');
|
|
186
|
+
|
|
187
|
+
const checks = await inquirer.prompt([
|
|
188
|
+
{
|
|
189
|
+
type: 'confirm',
|
|
190
|
+
name: 'b1b2Ready',
|
|
191
|
+
message: '确认 B1、B2 已由 PM 填写完成(非 AI 代填)?',
|
|
192
|
+
default: false
|
|
193
|
+
},
|
|
194
|
+
{
|
|
195
|
+
type: 'confirm',
|
|
196
|
+
name: 'understand',
|
|
197
|
+
message: '确认理解:AI 只审视,最终决策由 PM 做?',
|
|
198
|
+
default: false
|
|
199
|
+
}
|
|
200
|
+
]);
|
|
201
|
+
|
|
202
|
+
if (!checks.b1b2Ready || !checks.understand) {
|
|
203
|
+
console.log(chalk.red('\n✗ 前置条件未满足,无法进行 R1 审视\n'));
|
|
204
|
+
|
|
205
|
+
await dialog.logPMConfirmation('r1_review', 'pre_check', 'rejected',
|
|
206
|
+
`B1B2就绪:${checks.b1b2Ready}, 理解分工:${checks.understand}`
|
|
207
|
+
);
|
|
208
|
+
|
|
209
|
+
return false;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
await dialog.logPMConfirmation('r1_review', 'pre_check', 'approved',
|
|
213
|
+
'PM确认B1B2已填写完成,理解角色分工'
|
|
214
|
+
);
|
|
215
|
+
|
|
216
|
+
return true;
|
|
217
|
+
},
|
|
218
|
+
|
|
219
|
+
/**
|
|
220
|
+
* R2 版本审视前确认
|
|
221
|
+
*/
|
|
222
|
+
async confirmR2Review() {
|
|
223
|
+
console.log(chalk.bold.yellow('\n━━━ 关键提醒:R2 版本审视的角色分工 ━━━\n'));
|
|
224
|
+
|
|
225
|
+
console.log(chalk.cyan('【权责说明】'));
|
|
226
|
+
console.log('- PM 职责: ');
|
|
227
|
+
console.log(' 1. 对"是否背叛规划"负责');
|
|
228
|
+
console.log(' 2. 给出最终放行/否决\n');
|
|
229
|
+
|
|
230
|
+
console.log('- AI 职责: ');
|
|
231
|
+
console.log(' 1. 做一致性比对');
|
|
232
|
+
console.log(' 2. 标出偏移点');
|
|
233
|
+
console.log(' 3. 给出审视报告\n');
|
|
234
|
+
|
|
235
|
+
console.log(chalk.red('- AI 禁止: '));
|
|
236
|
+
console.log(' 1. ❌ 替 PM 填写 C0、C1 内容');
|
|
237
|
+
console.log(' 2. ❌ 替 PM 放行版本');
|
|
238
|
+
console.log(' 3. ❌ 隐瞒与B3的不一致\n');
|
|
239
|
+
|
|
240
|
+
const checks = await inquirer.prompt([
|
|
241
|
+
{
|
|
242
|
+
type: 'confirm',
|
|
243
|
+
name: 'c0c1Ready',
|
|
244
|
+
message: '确认 C0、C1 已由 PM 填写完成?',
|
|
245
|
+
default: false
|
|
246
|
+
},
|
|
247
|
+
{
|
|
248
|
+
type: 'confirm',
|
|
249
|
+
name: 'understand',
|
|
250
|
+
message: '确认理解:AI 只做一致性检查,不替PM放行?',
|
|
251
|
+
default: false
|
|
252
|
+
}
|
|
253
|
+
]);
|
|
254
|
+
|
|
255
|
+
if (!checks.c0c1Ready || !checks.understand) {
|
|
256
|
+
console.log(chalk.red('\n✗ 前置条件未满足\n'));
|
|
257
|
+
|
|
258
|
+
await dialog.logPMConfirmation('r2_review', 'pre_check', 'rejected',
|
|
259
|
+
`C0C1就绪:${checks.c0c1Ready}, 理解分工:${checks.understand}`
|
|
260
|
+
);
|
|
261
|
+
|
|
262
|
+
return false;
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
await dialog.logPMConfirmation('r2_review', 'pre_check', 'approved',
|
|
266
|
+
'PM确认C0C1已填写完成,理解角色分工'
|
|
267
|
+
);
|
|
268
|
+
|
|
269
|
+
return true;
|
|
270
|
+
},
|
|
271
|
+
|
|
272
|
+
/**
|
|
273
|
+
* B3 冻结前确认
|
|
274
|
+
*/
|
|
275
|
+
async confirmB3Freeze() {
|
|
276
|
+
console.log(chalk.bold.yellow('\n━━━ PM 决策点:B3 规划冻结 ━━━\n'));
|
|
277
|
+
|
|
278
|
+
console.log(chalk.cyan('【权责说明】'));
|
|
279
|
+
console.log('- PM 职责(必须由人签名):');
|
|
280
|
+
console.log(' 1. 确认规划正式成立');
|
|
281
|
+
console.log(' 2. 承担规划决策责任');
|
|
282
|
+
console.log(' 3. 对"不做的部分"负责\n');
|
|
283
|
+
|
|
284
|
+
console.log('- AI 职责:');
|
|
285
|
+
console.log(' 1. 检查冻结条件是否满足');
|
|
286
|
+
console.log(' 2. 引用 R1 结论');
|
|
287
|
+
console.log(' 3. 标注未解决问题\n');
|
|
288
|
+
|
|
289
|
+
console.log(chalk.red('- AI 禁止:'));
|
|
290
|
+
console.log(' ❌ 单方面宣布冻结\n');
|
|
291
|
+
|
|
292
|
+
const answer = await inquirer.prompt([
|
|
293
|
+
{
|
|
294
|
+
type: 'input',
|
|
295
|
+
name: 'signature',
|
|
296
|
+
message: 'PM 签名确认(输入你的名字):',
|
|
297
|
+
validate: (input) => input.trim().length > 0 || '必须输入签名'
|
|
298
|
+
},
|
|
299
|
+
{
|
|
300
|
+
type: 'confirm',
|
|
301
|
+
name: 'confirmed',
|
|
302
|
+
message: '确认承担规划决策责任,执行冻结?',
|
|
303
|
+
default: false
|
|
304
|
+
}
|
|
305
|
+
]);
|
|
306
|
+
|
|
307
|
+
await dialog.logPMConfirmation('b3_freeze', 'freeze_decision',
|
|
308
|
+
answer.confirmed ? 'approved' : 'rejected',
|
|
309
|
+
`PM签名: ${answer.signature}`
|
|
310
|
+
);
|
|
311
|
+
|
|
312
|
+
if (answer.confirmed) {
|
|
313
|
+
return answer.signature;
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
return false;
|
|
317
|
+
},
|
|
318
|
+
|
|
319
|
+
/**
|
|
320
|
+
* C3 冻结前确认
|
|
321
|
+
*/
|
|
322
|
+
async confirmC3Freeze() {
|
|
323
|
+
console.log(chalk.bold.yellow('\n━━━ PM 决策点:C3 版本冻结 ━━━\n'));
|
|
324
|
+
|
|
325
|
+
console.log(chalk.cyan('【权责说明】'));
|
|
326
|
+
console.log('- PM 职责(最终责任人):');
|
|
327
|
+
console.log(' 1. 对版本需求最终负责');
|
|
328
|
+
console.log(' 2. 对外承诺的唯一依据\n');
|
|
329
|
+
|
|
330
|
+
console.log('- AI 职责:');
|
|
331
|
+
console.log(' 1. 校验冻结条件');
|
|
332
|
+
console.log(' 2. 生成冻结记录');
|
|
333
|
+
console.log(' 3. 阻止后续修改\n');
|
|
334
|
+
|
|
335
|
+
console.log(chalk.red('- AI 禁止:'));
|
|
336
|
+
console.log(' ❌ 在冻结后继续生成需求\n');
|
|
337
|
+
|
|
338
|
+
const answer = await inquirer.prompt([
|
|
339
|
+
{
|
|
340
|
+
type: 'input',
|
|
341
|
+
name: 'signature',
|
|
342
|
+
message: 'PM 签名确认(输入你的名字):',
|
|
343
|
+
validate: (input) => input.trim().length > 0 || '必须输入签名'
|
|
344
|
+
},
|
|
345
|
+
{
|
|
346
|
+
type: 'confirm',
|
|
347
|
+
name: 'confirmed',
|
|
348
|
+
message: '确认对版本需求最终负责,执行冻结?',
|
|
349
|
+
default: false
|
|
350
|
+
}
|
|
351
|
+
]);
|
|
352
|
+
|
|
353
|
+
await dialog.logPMConfirmation('c3_freeze', 'freeze_decision',
|
|
354
|
+
answer.confirmed ? 'approved' : 'rejected',
|
|
355
|
+
`PM签名: ${answer.signature}`
|
|
356
|
+
);
|
|
357
|
+
|
|
358
|
+
if (answer.confirmed) {
|
|
359
|
+
return answer.signature;
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
return false;
|
|
363
|
+
}
|
|
364
|
+
};
|
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
const fs = require('fs-extra');
|
|
2
|
+
const path = require('path');
|
|
3
|
+
const chalk = require('chalk');
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* 对话归档模块
|
|
7
|
+
* 用于追溯每轮对话过程
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
module.exports = {
|
|
11
|
+
/**
|
|
12
|
+
* 记录对话
|
|
13
|
+
* @param {string} stage - 阶段 (如 'baseline', 'planning', 'review_r1')
|
|
14
|
+
* @param {string} action - 动作 (如 'create_A0', 'review', 'confirm')
|
|
15
|
+
* @param {object} data - 对话数据
|
|
16
|
+
*/
|
|
17
|
+
async logDialog(stage, action, data) {
|
|
18
|
+
try {
|
|
19
|
+
const configPath = path.join(process.cwd(), '.prd-config.json');
|
|
20
|
+
if (!await fs.pathExists(configPath)) {
|
|
21
|
+
return; // 不在项目目录中,跳过
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const config = await fs.readJSON(configPath);
|
|
25
|
+
const dialogDir = path.join(process.cwd(), '98_对话归档');
|
|
26
|
+
await fs.ensureDir(dialogDir);
|
|
27
|
+
|
|
28
|
+
// 确定归档文件路径
|
|
29
|
+
let logFile;
|
|
30
|
+
if (config.currentIteration > 0) {
|
|
31
|
+
const iterationName = `第${String(config.currentIteration).padStart(2, '0')}轮迭代`;
|
|
32
|
+
const iterationDialogDir = path.join(dialogDir, iterationName);
|
|
33
|
+
await fs.ensureDir(iterationDialogDir);
|
|
34
|
+
logFile = path.join(iterationDialogDir, `${stage}_对话记录.jsonl`);
|
|
35
|
+
} else {
|
|
36
|
+
logFile = path.join(dialogDir, `${stage}_对话记录.jsonl`);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// 构建记录
|
|
40
|
+
const record = {
|
|
41
|
+
timestamp: new Date().toISOString(),
|
|
42
|
+
stage,
|
|
43
|
+
action,
|
|
44
|
+
data,
|
|
45
|
+
iteration: config.currentIteration
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
// 追加到 JSONL 文件
|
|
49
|
+
await fs.appendFile(
|
|
50
|
+
logFile,
|
|
51
|
+
JSON.stringify(record) + '\n'
|
|
52
|
+
);
|
|
53
|
+
|
|
54
|
+
// 同时创建人类可读的 markdown 版本
|
|
55
|
+
const mdFile = logFile.replace('.jsonl', '.md');
|
|
56
|
+
const mdContent = await this.generateMarkdownLog(logFile);
|
|
57
|
+
await fs.writeFile(mdFile, mdContent);
|
|
58
|
+
|
|
59
|
+
} catch (error) {
|
|
60
|
+
console.error(chalk.gray(`对话归档失败: ${error.message}`));
|
|
61
|
+
}
|
|
62
|
+
},
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* 生成 Markdown 格式的对话日志
|
|
66
|
+
*/
|
|
67
|
+
async generateMarkdownLog(jsonlFile) {
|
|
68
|
+
const content = await fs.readFile(jsonlFile, 'utf-8');
|
|
69
|
+
const lines = content.trim().split('\n');
|
|
70
|
+
|
|
71
|
+
let md = `# 对话记录\n\n`;
|
|
72
|
+
md += `**文件**: ${path.basename(jsonlFile)}\n`;
|
|
73
|
+
md += `**记录数**: ${lines.length}\n\n`;
|
|
74
|
+
md += `---\n\n`;
|
|
75
|
+
|
|
76
|
+
for (const line of lines) {
|
|
77
|
+
const record = JSON.parse(line);
|
|
78
|
+
md += `## ${new Date(record.timestamp).toLocaleString('zh-CN')}\n\n`;
|
|
79
|
+
md += `**阶段**: ${record.stage}\n`;
|
|
80
|
+
md += `**动作**: ${record.action}\n`;
|
|
81
|
+
md += `**迭代轮次**: ${record.iteration}\n\n`;
|
|
82
|
+
|
|
83
|
+
// 根据不同类型生成不同格式
|
|
84
|
+
if (record.data) {
|
|
85
|
+
if (record.data.type === 'conversation') {
|
|
86
|
+
// 对话轮次格式
|
|
87
|
+
md += `### 💬 对话内容\n\n`;
|
|
88
|
+
if (record.data.topic) {
|
|
89
|
+
md += `**讨论主题**: ${record.data.topic}\n\n`;
|
|
90
|
+
}
|
|
91
|
+
if (record.data.pmSaid) {
|
|
92
|
+
md += `**🧑 PM**: ${record.data.pmSaid}\n\n`;
|
|
93
|
+
}
|
|
94
|
+
if (record.data.aiResponse) {
|
|
95
|
+
md += `**🤖 AI**: ${record.data.aiResponse}\n\n`;
|
|
96
|
+
}
|
|
97
|
+
if (record.data.pmDecision) {
|
|
98
|
+
md += `**✅ PM 决策**: ${record.data.pmDecision}\n\n`;
|
|
99
|
+
}
|
|
100
|
+
if (record.data.context) {
|
|
101
|
+
md += `**📋 背景**: ${record.data.context}\n\n`;
|
|
102
|
+
}
|
|
103
|
+
} else if (record.data.type === 'decision') {
|
|
104
|
+
// 决策格式
|
|
105
|
+
md += `### ✅ PM 决策\n\n`;
|
|
106
|
+
md += `- **决策项**: ${record.data.action}\n`;
|
|
107
|
+
md += `- **结果**: ${record.data.decision}\n`;
|
|
108
|
+
md += `- **原因**: ${record.data.reason}\n\n`;
|
|
109
|
+
} else {
|
|
110
|
+
// 默认 JSON 格式
|
|
111
|
+
md += `**详细信息**:\n\`\`\`json\n${JSON.stringify(record.data, null, 2)}\n\`\`\`\n\n`;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
md += `---\n\n`;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
return md;
|
|
119
|
+
},
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* 记录 PM 确认
|
|
123
|
+
*/
|
|
124
|
+
async logPMConfirmation(stage, action, decision, reason) {
|
|
125
|
+
await this.logDialog(stage, 'pm_confirmation', {
|
|
126
|
+
action,
|
|
127
|
+
decision,
|
|
128
|
+
reason,
|
|
129
|
+
role: 'PM',
|
|
130
|
+
type: 'decision'
|
|
131
|
+
});
|
|
132
|
+
},
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* 记录 AI 建议
|
|
136
|
+
*/
|
|
137
|
+
async logAISuggestion(stage, action, suggestion) {
|
|
138
|
+
await this.logDialog(stage, 'ai_suggestion', {
|
|
139
|
+
action,
|
|
140
|
+
suggestion,
|
|
141
|
+
role: 'AI',
|
|
142
|
+
type: 'suggestion'
|
|
143
|
+
});
|
|
144
|
+
},
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* 记录文档创建
|
|
148
|
+
*/
|
|
149
|
+
async logDocumentCreation(stage, docType, filePath) {
|
|
150
|
+
await this.logDialog(stage, 'document_created', {
|
|
151
|
+
docType,
|
|
152
|
+
filePath,
|
|
153
|
+
type: 'document'
|
|
154
|
+
});
|
|
155
|
+
},
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* 记录一轮对话(包含完整对话内容)
|
|
159
|
+
* @param {string} stage - 阶段
|
|
160
|
+
* @param {string} topic - 讨论主题
|
|
161
|
+
* @param {string} pmSaid - PM 说的内容
|
|
162
|
+
* @param {string} aiResponse - AI 的回复
|
|
163
|
+
* @param {string} pmDecision - PM 的决策(可选)
|
|
164
|
+
* @param {string} context - 背景信息(可选)
|
|
165
|
+
*/
|
|
166
|
+
async logConversationRound(stage, topic, pmSaid, aiResponse, pmDecision = null, context = null) {
|
|
167
|
+
await this.logDialog(stage, 'conversation_round', {
|
|
168
|
+
type: 'conversation',
|
|
169
|
+
topic,
|
|
170
|
+
pmSaid,
|
|
171
|
+
aiResponse,
|
|
172
|
+
pmDecision,
|
|
173
|
+
context
|
|
174
|
+
});
|
|
175
|
+
},
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* 记录需求讨论
|
|
179
|
+
* @param {string} requirementId - 需求项编号
|
|
180
|
+
* @param {string} pmInput - PM 的输入
|
|
181
|
+
* @param {string} aiSummary - AI 的总结
|
|
182
|
+
* @param {boolean} confirmed - PM 是否确认
|
|
183
|
+
*/
|
|
184
|
+
async logRequirementDiscussion(stage, requirementId, pmInput, aiSummary, confirmed) {
|
|
185
|
+
await this.logDialog(stage, 'requirement_discussion', {
|
|
186
|
+
type: 'conversation',
|
|
187
|
+
topic: `需求项 ${requirementId} 讨论`,
|
|
188
|
+
pmSaid: pmInput,
|
|
189
|
+
aiResponse: aiSummary,
|
|
190
|
+
pmDecision: confirmed ? '确认' : '需修改'
|
|
191
|
+
});
|
|
192
|
+
},
|
|
193
|
+
|
|
194
|
+
/**
|
|
195
|
+
* 记录优先级决策
|
|
196
|
+
* @param {string} stage - 阶段
|
|
197
|
+
* @param {object} priorities - 优先级决策 { P0: [...], P1: [...], P2: [...] }
|
|
198
|
+
* @param {string} pmReason - PM 的决策理由
|
|
199
|
+
*/
|
|
200
|
+
async logPriorityDecision(stage, priorities, pmReason) {
|
|
201
|
+
await this.logDialog(stage, 'priority_decision', {
|
|
202
|
+
type: 'conversation',
|
|
203
|
+
topic: '优先级排序决策',
|
|
204
|
+
pmSaid: pmReason,
|
|
205
|
+
aiResponse: `已记录优先级:P0=${priorities.P0?.length || 0}项, P1=${priorities.P1?.length || 0}项, P2=${priorities.P2?.length || 0}项`,
|
|
206
|
+
pmDecision: JSON.stringify(priorities)
|
|
207
|
+
});
|
|
208
|
+
},
|
|
209
|
+
|
|
210
|
+
/**
|
|
211
|
+
* 记录范围决策
|
|
212
|
+
* @param {string} stage - 阶段
|
|
213
|
+
* @param {array} included - 首版包含
|
|
214
|
+
* @param {array} excluded - 延后的
|
|
215
|
+
* @param {string} pmReason - PM 的决策理由
|
|
216
|
+
*/
|
|
217
|
+
async logScopeDecision(stage, included, excluded, pmReason) {
|
|
218
|
+
await this.logDialog(stage, 'scope_decision', {
|
|
219
|
+
type: 'conversation',
|
|
220
|
+
topic: '范围界定决策',
|
|
221
|
+
pmSaid: pmReason,
|
|
222
|
+
aiResponse: `首版包含 ${included.length} 项,延后 ${excluded.length} 项`,
|
|
223
|
+
pmDecision: `包含: ${included.join(', ')} | 延后: ${excluded.join(', ')}`
|
|
224
|
+
});
|
|
225
|
+
}
|
|
226
|
+
};
|
|
227
|
+
|