prd-workflow-cli 1.4.0 → 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.
package/commands/init.js CHANGED
@@ -28,7 +28,7 @@ module.exports = async function (projectName) {
28
28
  console.log(chalk.yellow(` 你正在尝试在 PRD 项目中创建子项目 "${projectName}"。`));
29
29
  console.log('');
30
30
  console.log(chalk.cyan(' 建议操作:'));
31
- console.log(chalk.gray(' 1. 如果要在当前项目工作,直接使用 prd baseline create A0 等命令'));
31
+ console.log(chalk.gray(' 1. 如果要在当前项目工作,直接使用 prd baseline create 产品定义 等命令'));
32
32
  console.log(chalk.gray(' 2. 如果确实要创建独立新项目,请先 cd 到其他目录'));
33
33
  console.log(chalk.gray(' 3. 如果要更新规则文件,请运行: prd upgrade'));
34
34
  console.log('');
@@ -38,10 +38,10 @@ module.exports = async function (projectName) {
38
38
 
39
39
  console.log(chalk.blue(`正在${isCurrentDir ? '在当前目录' : '创建项目: ' + projectName}初始化...`));
40
40
 
41
- // 创建项目目录结构
41
+ // 创建项目目录结构(使用新的中文命名)
42
42
  const directories = [
43
43
  '00_项目总览',
44
- '01_产品基线',
44
+ '01_基线', // 原 01_产品基线
45
45
  '02_迭代记录',
46
46
  '98_对话归档',
47
47
  '99_归档区/历史参考与废弃文档',
@@ -56,8 +56,9 @@ module.exports = async function (projectName) {
56
56
  const config = {
57
57
  projectName: displayName,
58
58
  createdAt: new Date().toISOString(),
59
+ cliVersion: '2.0.0',
59
60
  currentIteration: 0,
60
- workflow: 'ARBC',
61
+ workflow: '基线规划IT版本',
61
62
  stages: {
62
63
  baseline: { completed: false, documents: [] },
63
64
  planning: { completed: false, documents: [] },
@@ -83,7 +84,7 @@ module.exports = async function (projectName) {
83
84
  help: 'prd --help'
84
85
  },
85
86
  dependencies: {
86
- 'prd-workflow-cli': '^1.1.29'
87
+ 'prd-workflow-cli': '^2.0.0'
87
88
  }
88
89
  };
89
90
 
@@ -93,147 +94,89 @@ module.exports = async function (projectName) {
93
94
  { spaces: 2 }
94
95
  );
95
96
 
96
- // 创建 P0 项目基本信息模板
97
- const p0Template = `# P0_项目基本信息
97
+ // 创建项目信息模板(原 P0
98
+ const projectInfoTemplate = `# 项目信息
98
99
 
99
100
  **创建时间**: ${new Date().toLocaleString('zh-CN')}
100
101
  **项目名称**: ${displayName}
101
- **文档状态**: 草案
102
102
 
103
103
  ---
104
104
 
105
- ## 文档说明
106
-
107
- **目的**:
108
- - 明确项目是否应该存在
109
- - 确认项目目标是否成立
110
- - 识别关键干系人
111
-
112
- **填写要求**:
113
- - 只填写事实,不填愿景
114
- - 目标要可检验
115
- - 干系人要具体到人
105
+ > ⚠️ **这是业务的"宪法"** - 只记录代码无法表达的决策
106
+ >
107
+ > 功能清单由 AI 扫描代码自动生成(见代码快照),无需在此重复
116
108
 
117
109
  ---
118
110
 
119
- ## 1. 项目基本信息
111
+ ## 1. 产品定位
120
112
 
121
- ### 1.1 项目定位
113
+ **一句话说明这个产品是什么**:
114
+ <!-- 例如:面向企业用户的项目管理工具 -->
122
115
 
123
- **项目全称**: ${displayName}
124
116
 
125
- **项目简述**:
126
- <!-- 一句话说明这个项目是什么 -->
117
+ **核心价值主张**:
118
+ <!-- 用户为什么选择你而不是竞品? -->
127
119
 
128
- **所属产品线**:
129
- <!-- 例如:核心业务系统、辅助工具、创新试点 -->
130
-
131
- **项目级别**:
132
- - [ ] 战略级(公司级重点)
133
- - [ ] 业务级(部门级重点)
134
- - [ ] 支撑级(基础能力)
135
120
 
136
121
  ---
137
122
 
138
- ## 2. 项目目标
123
+ ## 2. 边界声明
139
124
 
140
- ### 2.1 核心目标
125
+ ### 2.1 明确不做的事情
141
126
 
142
- **要解决的主要问题**:
143
- <!-- 不超过 3 个核心问题 -->
144
- 1.
145
- 2.
146
- 3.
127
+ <!-- 这是最重要的部分!列出被拒绝的需求/方向 -->
147
128
 
148
- **成功标准**:
149
- <!-- 如何判断项目成功?用可衡量的指标 -->
150
- - 指标 1: ______
151
- - 指标 2: ______
152
- - 指标 3: ______
129
+ | 不做的事情 | 原因 |
130
+ |-----------|------|
131
+ | 例:第三方登录 | 数据安全考虑 |
132
+ | 例:移动端 App | 资源限制,优先 Web |
133
+ | | |
153
134
 
154
- ### 2.2 目标合理性确认
135
+ ### 2.2 核心约束(红线)
155
136
 
156
- **为什么现在做这个项目?**
157
- <!-- 时机/背景/触发因素 -->
137
+ <!-- 不可妥协的限制 -->
158
138
 
159
- **不做会怎样?**
160
- <!-- 说明紧迫性 -->
139
+ - [ ] 安全约束: ___________
140
+ - [ ] 合规约束: ___________
141
+ - [ ] 性能约束: ___________
142
+ - [ ] 其他: ___________
161
143
 
162
144
  ---
163
145
 
164
- ## 3. 干系人
165
-
166
- ### 3.1 核心干系人
167
-
168
- **PM(产品负责人)**:
169
- - 姓名: ____________
170
- - 职责: 项目最终决策
171
- - 联系方式: ____________
172
-
173
- **技术负责人**:
174
- - 姓名: ____________
175
- - 职责: 技术可行性把关
176
- - 联系方式: ____________
177
-
178
- **业务方**:
179
- - 部门: ____________
180
- - 联系人: ____________
181
- - 职责: 业务需求确认
182
-
183
- ### 3.2 相关方
146
+ ## 3. 责任人
184
147
 
185
- **可能受影响的团队/系统**:
186
- <!-- 列出会受此项目影响的其他团队或系统 -->
148
+ | 角色 | 姓名 | 职责 |
149
+ |-----|------|------|
150
+ | **产品负责人 (PM)** | _____ | 最终决策 |
151
+ | **技术负责人** | _____ | 技术可行性 |
152
+ | **业务方** | _____ | 业务验收 |
187
153
 
188
154
  ---
189
155
 
190
- ## 4. 项目约束
156
+ ## 4. 成功标准
191
157
 
192
- ### 4.1 时间约束
158
+ <!-- 如何判断项目成功?必须可衡量 -->
193
159
 
194
- **期望启动时间**: ____________
195
- **期望交付时间**: ____________
196
-
197
- ### 4.2 资源约束
198
-
199
- **已知的资源限制**:
200
- <!-- 人力/预算/技术限制 -->
201
-
202
- ### 4.3 依赖条件
203
-
204
- **项目依赖**:
205
- <!-- 需要其他项目/系统先完成什么? -->
160
+ | 指标 | 当前值 | 目标值 | 截止日期 |
161
+ |-----|-------|-------|---------|
162
+ | 例:注册转化率 | 30% | 60% | 2024-Q2 |
163
+ | | | | |
206
164
 
207
165
  ---
208
166
 
209
- ## 5. 项目状态
210
-
211
- **当前状态**: 初始化
212
- **当前迭代**: 0 轮
213
- **下一步**: 创建 A 类基线文档
214
-
215
- ---
167
+ ## PM 确认
216
168
 
217
- ## 6. PM 确认
218
-
219
- - [ ] 项目目标已明确且合理
220
- - [ ] 干系人已确认
221
- - [ ] 约束条件已记录
222
- - [ ] 可以开始基线建立
169
+ - [ ] 边界声明已明确
170
+ - [ ] 责任人已确认
171
+ - [ ] 成功标准可衡量
223
172
 
224
173
  **PM 签字**: _____________
225
174
  **日期**: _____________
226
-
227
- ---
228
-
229
- ## 备注
230
-
231
- <!-- 其他需要说明的重要信息 -->
232
175
  `;
233
176
 
234
177
  await fs.writeFile(
235
- path.join(projectPath, '00_项目总览/P0_项目基本信息.md'),
236
- p0Template
178
+ path.join(projectPath, '00_项目总览/项目信息.md'),
179
+ projectInfoTemplate
237
180
  );
238
181
 
239
182
  // 复制工作流模板
@@ -294,73 +237,74 @@ module.exports = async function (projectName) {
294
237
  // 创建 .a2ui 目录(用于临时预览数据)
295
238
  await fs.ensureDir(path.join(projectPath, '.a2ui'));
296
239
 
297
- // 创建 README
240
+ // 创建 README(使用新的中文命名)
298
241
  const readme = `# ${displayName}
299
242
 
300
- 本项目采用规范化的产品需求管理流程
243
+ 本项目采用规范化的产品需求管理流程 (PRD-CLI v2.0.0)
301
244
 
302
245
  ## 📁 目录结构
303
246
 
304
247
  \`\`\`
305
248
  ${displayName}/
306
- ├── 00_项目总览/ # 项目基本信息
307
- ├── 01_产品基线/ # A 类文档:现状基线
308
- ├── 02_迭代记录/ # 各轮迭代的 B、C 类文档
309
- │ ├── 第01轮迭代/
310
- │ ├── 第02轮迭代/
311
- │ └── ...
249
+ ├── 00_项目总览/ # 项目信息
250
+ │ └── 项目信息.md
251
+ ├── 01_基线/ # 产品基线
252
+ │ ├── 产品定义.md # PM 填写
253
+ │ ├── 代码快照.md # AI 扫描生成
254
+ │ └── 用户反馈.md # AI 整理
255
+ ├── 02_迭代记录/ # 各轮迭代
256
+ │ └── 第01轮迭代/
257
+ │ ├── 需求规划.md # PM + AI 对话
258
+ │ ├── 规划冻结.md # 自动生成
259
+ │ ├── IT/ # 用户故事
260
+ │ │ └── IT-001-功能名/
261
+ │ │ ├── 业务需求.md
262
+ │ │ └── 技术规格.md
263
+ │ └── 版本发布.md # 自动生成
312
264
  └── 99_归档区/ # 历史文档归档
313
265
  \`\`\`
314
266
 
315
267
  ## 🔄 工作流程
316
268
 
317
- 1. **A 类 - 建立基线** (01_产品基线/)
318
- - A0: 产品基础与范围说明
319
- - A1: 已上线功能与流程清单
320
- - A2: 存量反馈与数据汇总
321
- - R0: 基线审视报告
322
-
323
- 2. **B 类 - 需求规划** (02_迭代记录/第N轮迭代/)
324
- - R1: 规划前审视(启动条件检查)
325
- - B1: 需求规划草案
326
- - B2: 规划拆解与范围界定
327
- - R1: 规划审视(冻结前审查)
328
- - B3: 规划冻结归档
329
-
330
- 3. **C 类 - 版本需求** (02_迭代记录/第N轮迭代/)
331
- - R2: 版本审视
332
- - C0: 版本范围声明
333
- - C1: 版本需求清单
334
- - C3: 版本冻结归档
269
+ \`\`\`
270
+ 基线阶段 规划阶段 → IT阶段 → 版本阶段
271
+ ↓ ↓ ↓ ↓
272
+ AI生成 PM+AI对话 PM+AI对话 自动生成
273
+ \`\`\`
335
274
 
336
- ## 🛠️ 使用 CLI 工具
275
+ ## 🛠️ 常用命令
337
276
 
338
277
  \`\`\`bash
339
278
  # 查看项目状态
340
279
  prd status
341
280
 
342
281
  # 创建基线文档
343
- prd baseline create A0
282
+ prd baseline create 产品定义
283
+ prd baseline create 代码快照
284
+ prd baseline create 用户反馈
344
285
 
345
286
  # 开始新迭代
346
287
  prd iteration new
347
288
 
348
289
  # 创建规划文档
349
- prd plan create B1
290
+ prd plan create
350
291
 
351
- # 执行 R1 审视
352
- prd review r1
353
-
354
- # 冻结规划
292
+ # 冻结规划(自动审视)
355
293
  prd plan freeze
294
+
295
+ # 创建 IT 用户故事
296
+ prd it create "功能名称"
297
+
298
+ # 冻结版本(自动审视)
299
+ prd version freeze
356
300
  \`\`\`
357
301
 
358
- ## 📝 关键原则
302
+ ## 📝 核心原则
359
303
 
360
- - **R1 是启动闸门**: 必须满足三个条件才能开始规划
361
- - **B3 是决策冻结**: 规划一旦冻结不可随意更改
362
- - **C 类不讨论方向**: 只执行已冻结的规划
363
- - **审视是强制的**: R1/R2 必须通过才能进入下一阶段
304
+ - **PM 决策,AI 执行**:AI 不替 PM 做决策
305
+ - **对话驱动**:文档通过对话逐步完成,不一次填充
306
+ - **审视内化**:审视作为动作内化到 freeze 命令中
307
+ - **防止幻觉**:AI 不编造技术细节
364
308
 
365
309
  ---
366
310
  创建时间: ${new Date().toLocaleString('zh-CN')}
@@ -376,38 +320,29 @@ prd plan freeze
376
320
 
377
321
  // 显示 AI 集成信息
378
322
  console.log(chalk.bold('🤖 AI 集成已配置:'));
379
- console.log(chalk.gray(' ✓ .agent/workflows/ - PRD 工作流指引(包含所有阶段的详细步骤)'));
323
+ console.log(chalk.gray(' ✓ .agent/workflows/ - PRD 工作流指引'));
380
324
  console.log(chalk.gray(' ✓ .cursorrules - Cursor AI 规则'));
381
325
  console.log(chalk.gray(' ✓ .antigravity/ - Antigravity AI 规则'));
382
326
  console.log(chalk.gray(' ✓ a2ui-viewer/ - A2UI 界面预览器'));
383
- console.log(chalk.gray(' ✓ .a2ui/ - A2UI 临时数据目录'));
384
327
  console.log('');
385
328
  console.log(chalk.yellow(' 💡 现在你可以直接与 AI 助手对话,AI 已经知道如何协助你完成 PRD 流程!'));
386
- console.log(chalk.gray(' 例如:告诉 AI "我要创建一个新项目的需求文档"'));
387
- console.log(chalk.gray(' 启动界面预览:运行 prd ui'));
388
329
  console.log('');
389
330
 
390
- console.log(chalk.bold('📋 下一步操作(请按顺序执行):'));
331
+ console.log(chalk.bold('📋 下一步操作:'));
391
332
  console.log('');
392
333
  if (!isCurrentDir) {
393
- console.log(chalk.cyan('1 步: 进入项目目录'));
394
- console.log(` cd ${displayName}`);
334
+ console.log(chalk.cyan('1. 进入项目目录'));
335
+ console.log(` cd ${displayName}`);
395
336
  console.log('');
396
- console.log(chalk.cyan('2 步: 完善 P0_项目基本信息.md'));
337
+ console.log(chalk.cyan('2. 完善项目信息'));
397
338
  } else {
398
- console.log(chalk.cyan('1 步: 完善 P0_项目基本信息.md'));
339
+ console.log(chalk.cyan('1. 完善项目信息'));
399
340
  }
400
- console.log(chalk.gray(' 文件位置: 00_项目总览/P0_项目基本信息.md'));
401
- console.log(chalk.gray(' 填写内容: 项目目标、干系人、约束条件等'));
402
- console.log(chalk.yellow(' ⚠️ 必须完成 P0 填写后才能开始创建 A 类基线文档'));
403
- console.log('');
404
- console.log(chalk.cyan(`第 ${isCurrentDir ? '2' : '3'} 步: 创建 A0 基线文档`));
405
- console.log(' prd baseline create A0 # P0 填写完成后执行');
341
+ console.log(chalk.gray(' 文件位置: 00_项目总览/项目信息.md'));
342
+ console.log(chalk.yellow(' ⚠️ 必须完成项目信息后才能开始创建基线文档'));
406
343
  console.log('');
407
-
408
- console.log(chalk.bold('🔄 后续更新:'));
409
- console.log(chalk.gray(' 当 CLI 包有新版本时,运行以下命令同步更新项目规则:'));
410
- console.log(chalk.cyan(' npm update -g prd-workflow-cli && prd upgrade'));
344
+ console.log(chalk.cyan(`${isCurrentDir ? '2' : '3'}. 创建产品定义`));
345
+ console.log(' prd baseline create 产品定义');
411
346
  console.log('');
412
347
 
413
348
  } catch (error) {
package/commands/it.js ADDED
@@ -0,0 +1,286 @@
1
+ const fs = require('fs-extra');
2
+ const path = require('path');
3
+ const chalk = require('chalk');
4
+
5
+ /**
6
+ * IT (INVEST) 管理命令
7
+ * 替代原来的 C1 大文档,每个 IT 是一个独立的用户故事
8
+ */
9
+
10
+ module.exports = async function (action, name, options = {}) {
11
+ const configPath = path.join(process.cwd(), '.prd-config.json');
12
+
13
+ if (!await fs.pathExists(configPath)) {
14
+ console.log(chalk.red('✗ 当前目录不是一个 PRD 项目'));
15
+ console.log('请先运行: prd init <项目名>');
16
+ return;
17
+ }
18
+
19
+ const config = await fs.readJSON(configPath);
20
+
21
+ if (action === 'create') {
22
+ await createIT(config, name, options);
23
+ } else if (action === 'list') {
24
+ await listITs(config, options);
25
+ } else if (action === 'show') {
26
+ await showIT(config, name, options);
27
+ } else {
28
+ console.log(chalk.red('✗ 未知的操作'));
29
+ console.log('可用操作: create, list, show');
30
+ }
31
+ };
32
+
33
+ async function createIT(config, name, options = {}) {
34
+ if (config.currentIteration === 0) {
35
+ console.log(chalk.red('✗ 请先创建迭代'));
36
+ console.log('运行: prd iteration new');
37
+ return;
38
+ }
39
+
40
+ if (!name) {
41
+ console.log(chalk.red('✗ 请提供 IT 名称'));
42
+ console.log('示例: prd it create 用户反馈');
43
+ return;
44
+ }
45
+
46
+ const iterationDir = path.join(
47
+ process.cwd(),
48
+ '02_迭代记录',
49
+ `第${String(config.currentIteration).padStart(2, '0')}轮迭代`
50
+ );
51
+
52
+ // 检查规划冻结是否存在(支持新旧文件名)
53
+ const freezePath = path.join(iterationDir, '规划冻结.md');
54
+ const oldB3Path = path.join(iterationDir, 'B3_规划冻结归档.md');
55
+ if (!await fs.pathExists(freezePath) && !await fs.pathExists(oldB3Path)) {
56
+ console.log(chalk.red('✗ 请先完成规划冻结'));
57
+ console.log('运行: prd plan freeze');
58
+ return;
59
+ }
60
+
61
+ const itDir = path.join(iterationDir, 'IT');
62
+ await fs.ensureDir(itDir);
63
+
64
+ // 获取下一个 IT 编号
65
+ const existingITs = await fs.readdir(itDir);
66
+ const itNumbers = existingITs
67
+ .filter(dir => dir.startsWith('IT-'))
68
+ .map(dir => parseInt(dir.split('-')[1]))
69
+ .filter(n => !isNaN(n));
70
+
71
+ const nextNumber = itNumbers.length > 0 ? Math.max(...itNumbers) + 1 : 1;
72
+ const itId = `IT-${String(nextNumber).padStart(3, '0')}`;
73
+ const itFolderName = `${itId}-${name}`;
74
+ const itPath = path.join(itDir, itFolderName);
75
+
76
+ if (await fs.pathExists(itPath)) {
77
+ console.log(chalk.yellow(`⚠ IT 已存在: ${itFolderName}`));
78
+ return;
79
+ }
80
+
81
+ await fs.ensureDir(itPath);
82
+
83
+ // 读取模板(优先使用新的中文模板)
84
+ let bizTemplatePath = path.join(__dirname, '../templates/业务需求.md');
85
+ let devTemplatePath = path.join(__dirname, '../templates/技术规格.md');
86
+
87
+ // 兼容旧模板
88
+ if (!await fs.pathExists(bizTemplatePath)) {
89
+ bizTemplatePath = path.join(__dirname, '../templates/it-biz.md');
90
+ }
91
+ if (!await fs.pathExists(devTemplatePath)) {
92
+ devTemplatePath = path.join(__dirname, '../templates/it-dev.md');
93
+ }
94
+
95
+ const bizTemplate = await fs.readFile(bizTemplatePath, 'utf-8');
96
+ const devTemplate = await fs.readFile(devTemplatePath, 'utf-8');
97
+
98
+ // 替换模板变量
99
+ const createTime = new Date().toLocaleString('zh-CN');
100
+ const replacements = {
101
+ '{{IT_ID}}': itId,
102
+ '{{IT_NAME}}': name,
103
+ '{{CREATE_TIME}}': createTime
104
+ };
105
+
106
+ let bizContent = bizTemplate;
107
+ let devContent = devTemplate;
108
+
109
+ Object.entries(replacements).forEach(([key, value]) => {
110
+ bizContent = bizContent.replace(new RegExp(key, 'g'), value);
111
+ devContent = devContent.replace(new RegExp(key, 'g'), value);
112
+ });
113
+
114
+ // 生成文件(使用中文文件名)
115
+ const bizFilePath = path.join(itPath, '业务需求.md');
116
+ const devFilePath = path.join(itPath, '技术规格.md');
117
+
118
+ await fs.writeFile(bizFilePath, bizContent);
119
+ await fs.writeFile(devFilePath, devContent);
120
+
121
+ console.log(chalk.green(`✓ 创建 IT: ${itFolderName}\n`));
122
+ console.log(chalk.cyan(`📁 位置: ${itPath}`));
123
+ console.log(chalk.gray(` 业务需求.md`));
124
+ console.log(chalk.gray(` 技术规格.md`));
125
+ console.log('');
126
+ console.log(chalk.bold('下一步:'));
127
+ console.log('1. 填写业务需求.md(与 AI 对话)');
128
+ console.log('2. 填写技术规格.md(技术负责人补充)');
129
+ console.log('3. 查看所有 IT: prd it list');
130
+ console.log('');
131
+ }
132
+
133
+ async function listITs(config, options = {}) {
134
+ if (config.currentIteration === 0) {
135
+ console.log(chalk.red('✗ 尚未创建迭代'));
136
+ return;
137
+ }
138
+
139
+ const iterationDir = path.join(
140
+ process.cwd(),
141
+ '02_迭代记录',
142
+ `第${String(config.currentIteration).padStart(2, '0')}轮迭代`
143
+ );
144
+
145
+ const itDir = path.join(iterationDir, 'IT');
146
+
147
+ if (!await fs.pathExists(itDir)) {
148
+ console.log(chalk.yellow('尚未创建任何 IT'));
149
+ console.log('运行: prd it create <名称>');
150
+ return;
151
+ }
152
+
153
+ const its = await fs.readdir(itDir);
154
+ const itFolders = its.filter(name => name.startsWith('IT-'));
155
+
156
+ if (itFolders.length === 0) {
157
+ console.log(chalk.yellow('尚未创建任何 IT'));
158
+ return;
159
+ }
160
+
161
+ console.log(chalk.bold.cyan(`\n=== 当前迭代 IT 列表 ( 共 ${itFolders.length} 个 ) ===\n`));
162
+
163
+ // 模板文件用于对比
164
+ const bizTemplatePath = path.join(__dirname, '../templates/it-biz.md');
165
+ const devTemplatePath = path.join(__dirname, '../templates/it-dev.md');
166
+ const bizTemplate = await fs.readFile(bizTemplatePath, 'utf-8');
167
+ const devTemplate = await fs.readFile(devTemplatePath, 'utf-8');
168
+ // 提取模板特征(用于简单判断是否修改)
169
+ const bizFeature = "### 1. 用户故事";
170
+ const devFeature = "### 1.1 用户故事";
171
+
172
+ for (const itFolder of itFolders) {
173
+ const itPath = path.join(itDir, itFolder);
174
+ const itId = itFolder.split('-').slice(0, 2).join('-');
175
+ const bizPath = path.join(itPath, `${itId}-BIZ.md`);
176
+ const devPath = path.join(itPath, `${itId}-DEV.md`);
177
+
178
+ const hasBiz = await fs.pathExists(bizPath);
179
+ const hasDev = await fs.pathExists(devPath);
180
+
181
+ let bizStatus = chalk.gray('缺失');
182
+ let devStatus = chalk.gray('缺失');
183
+
184
+ if (hasBiz) {
185
+ const content = await fs.readFile(bizPath, 'utf-8');
186
+ // 简单判断:如果内容长度比模板由明显变化,或者关键部分被修改
187
+ // 这里用简单逻辑:只要文件存在且不仅仅是模板替换后的初始状态
188
+ // 更好的方式是检查是否有 "[用户角色]" 这样的占位符
189
+ const isDefault = content.includes('[用户角色]');
190
+ bizStatus = isDefault ? chalk.yellow('待填写') : chalk.green('已填写');
191
+ }
192
+
193
+ if (hasDev) {
194
+ const content = await fs.readFile(devPath, 'utf-8');
195
+ const isDefault = content.includes('<!-- 从 BIZ 复制 -->');
196
+ devStatus = isDefault ? chalk.yellow('待填写') : chalk.green('已填写');
197
+ }
198
+
199
+ console.log(chalk.bold(`${itFolder}`));
200
+ console.log(` BIZ: ${bizStatus}`);
201
+ console.log(` DEV: ${devStatus}`);
202
+ console.log(chalk.gray('-'.repeat(40)));
203
+ }
204
+ console.log('');
205
+ }
206
+
207
+ async function showIT(config, idOrName, options = {}) {
208
+ if (!idOrName) {
209
+ console.log(chalk.red('✗ 请提供 IT 编号或名称'));
210
+ console.log('示例: prd it show 001');
211
+ return;
212
+ }
213
+
214
+ if (config.currentIteration === 0) {
215
+ console.log(chalk.red('✗ 尚未创建迭代'));
216
+ return;
217
+ }
218
+
219
+ const iterationDir = path.join(
220
+ process.cwd(),
221
+ '02_迭代记录',
222
+ `第${String(config.currentIteration).padStart(2, '0')}轮迭代`
223
+ );
224
+
225
+ const itDir = path.join(iterationDir, 'IT');
226
+
227
+ if (!await fs.pathExists(itDir)) {
228
+ console.log(chalk.yellow('尚未创建任何 IT'));
229
+ return;
230
+ }
231
+
232
+ const its = await fs.readdir(itDir);
233
+ const targetIT = its.find(name =>
234
+ name.includes(idOrName) || name.startsWith(`IT-${idOrName}`)
235
+ );
236
+
237
+ if (!targetIT) {
238
+ console.log(chalk.red(`✗ 未找到 IT: ${idOrName}`));
239
+ return;
240
+ }
241
+
242
+ const itPath = path.join(itDir, targetIT);
243
+ console.log(chalk.bold.cyan(`\n=== ${targetIT} ===\n`));
244
+
245
+ // BIZ 文件信息
246
+ const itId = targetIT.split('-').slice(0, 2).join('-');
247
+ const bizFileName = `${itId}-BIZ.md`;
248
+ const bizPath = path.join(itPath, bizFileName);
249
+
250
+ if (await fs.pathExists(bizPath)) {
251
+ const content = await fs.readFile(bizPath, 'utf-8');
252
+ const isDefault = content.includes('[用户角色]');
253
+ const status = isDefault ? chalk.yellow('待填写') : chalk.green('已填写');
254
+ console.log(`📄 ${chalk.bold('BIZ 业务需求')} (${bizFileName})`);
255
+ console.log(` 状态: ${status}`);
256
+ console.log(` 路径: ${bizPath}`);
257
+ } else {
258
+ console.log(`📄 ${chalk.bold('BIZ 业务需求')} (${bizFileName})`);
259
+ console.log(` 状态: ${chalk.red('缺失')}`);
260
+ }
261
+ console.log('');
262
+
263
+ // DEV 文件信息
264
+ const devFileName = `${itId}-DEV.md`;
265
+ const devPath = path.join(itPath, devFileName);
266
+
267
+ if (await fs.pathExists(devPath)) {
268
+ const content = await fs.readFile(devPath, 'utf-8');
269
+ const isDefault = content.includes('<!-- 从 BIZ 复制 -->');
270
+ const status = isDefault ? chalk.yellow('待填写') : chalk.green('已填写');
271
+ console.log(`🛠️ ${chalk.bold('DEV 功能规格')} (${devFileName})`);
272
+ console.log(` 状态: ${status}`);
273
+ console.log(` 路径: ${devPath}`);
274
+ } else {
275
+ console.log(`🛠️ ${chalk.bold('DEV 功能规格')} (${devFileName})`);
276
+ console.log(` 状态: ${chalk.red('缺失')}`);
277
+ }
278
+ console.log('');
279
+
280
+ // 操作提示
281
+ console.log(chalk.gray('-'.repeat(40)));
282
+ console.log(chalk.bold('提示:'));
283
+ console.log(`- 编辑业务需求: code "${bizPath}"`);
284
+ console.log(`- 编辑开发规格: code "${devPath}"`);
285
+ console.log('');
286
+ }