prd-workflow-cli 1.3.4 → 1.4.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.
@@ -0,0 +1,221 @@
1
+ {
2
+ "$schema": "http://json-schema.org/draft-07/schema#",
3
+ "title": "PRD Rules Index",
4
+ "description": "PRD-CLI 规则索引的 JSON Schema",
5
+ "type": "object",
6
+ "required": [
7
+ "version",
8
+ "rules"
9
+ ],
10
+ "properties": {
11
+ "$schema": {
12
+ "type": "string"
13
+ },
14
+ "version": {
15
+ "type": "string",
16
+ "pattern": "^\\d+\\.\\d+\\.\\d+$"
17
+ },
18
+ "lastUpdated": {
19
+ "type": "string",
20
+ "format": "date"
21
+ },
22
+ "categories": {
23
+ "type": "object",
24
+ "additionalProperties": {
25
+ "type": "object",
26
+ "properties": {
27
+ "name": {
28
+ "type": "string"
29
+ },
30
+ "nameEn": {
31
+ "type": "string"
32
+ }
33
+ },
34
+ "required": [
35
+ "name"
36
+ ]
37
+ }
38
+ },
39
+ "rules": {
40
+ "type": "array",
41
+ "items": {
42
+ "$ref": "#/definitions/rule"
43
+ }
44
+ },
45
+ "validators": {
46
+ "type": "object",
47
+ "additionalProperties": {
48
+ "$ref": "#/definitions/validator"
49
+ }
50
+ },
51
+ "scopeMapping": {
52
+ "type": "object",
53
+ "additionalProperties": {
54
+ "type": "string"
55
+ }
56
+ }
57
+ },
58
+ "definitions": {
59
+ "rule": {
60
+ "type": "object",
61
+ "required": [
62
+ "id",
63
+ "category",
64
+ "severity",
65
+ "scope",
66
+ "description",
67
+ "validatorType"
68
+ ],
69
+ "properties": {
70
+ "id": {
71
+ "type": "string",
72
+ "pattern": "^[A-Z]\\d{3}$",
73
+ "description": "规则 ID,格式为 X000"
74
+ },
75
+ "category": {
76
+ "type": "string",
77
+ "enum": [
78
+ "G",
79
+ "D",
80
+ "F",
81
+ "S",
82
+ "V",
83
+ "I",
84
+ "U",
85
+ "C",
86
+ "W"
87
+ ],
88
+ "description": "规则分类"
89
+ },
90
+ "severity": {
91
+ "type": "string",
92
+ "enum": [
93
+ "CRITICAL",
94
+ "HIGH",
95
+ "MEDIUM",
96
+ "LOW"
97
+ ],
98
+ "description": "严重程度"
99
+ },
100
+ "scope": {
101
+ "type": "array",
102
+ "items": {
103
+ "type": "string",
104
+ "enum": [
105
+ "global",
106
+ "p0",
107
+ "b1",
108
+ "b2",
109
+ "plan",
110
+ "c0",
111
+ "c1",
112
+ "version",
113
+ "r1",
114
+ "r2",
115
+ "iteration",
116
+ "a2ui"
117
+ ]
118
+ },
119
+ "description": "适用范围"
120
+ },
121
+ "description": {
122
+ "type": "string",
123
+ "description": "规则描述"
124
+ },
125
+ "validatorType": {
126
+ "type": "string",
127
+ "enum": [
128
+ "program",
129
+ "ai-self-check",
130
+ "hybrid"
131
+ ],
132
+ "description": "校验类型"
133
+ },
134
+ "validator": {
135
+ "type": [
136
+ "string",
137
+ "null"
138
+ ],
139
+ "description": "校验器名称"
140
+ },
141
+ "checkLogic": {
142
+ "type": "string",
143
+ "description": "校验逻辑说明"
144
+ },
145
+ "examples": {
146
+ "type": "object",
147
+ "properties": {
148
+ "correct": {
149
+ "type": "string"
150
+ },
151
+ "wrong": {
152
+ "type": "string"
153
+ }
154
+ }
155
+ },
156
+ "triggers": {
157
+ "type": "array",
158
+ "items": {
159
+ "type": "string"
160
+ },
161
+ "description": "触发关键词"
162
+ },
163
+ "savePoints": {
164
+ "type": "array",
165
+ "items": {
166
+ "type": "string"
167
+ },
168
+ "description": "保存触发点"
169
+ },
170
+ "questionTemplates": {
171
+ "type": "array",
172
+ "items": {
173
+ "type": "string"
174
+ },
175
+ "description": "质疑问题模板"
176
+ },
177
+ "checkItems": {
178
+ "type": "array",
179
+ "items": {
180
+ "type": "string"
181
+ },
182
+ "description": "检查项"
183
+ },
184
+ "dimensions": {
185
+ "type": "array",
186
+ "items": {
187
+ "type": "string"
188
+ },
189
+ "description": "检查维度"
190
+ },
191
+ "dimensionDetails": {
192
+ "type": "object",
193
+ "description": "维度详细说明"
194
+ }
195
+ }
196
+ },
197
+ "validator": {
198
+ "type": "object",
199
+ "required": [
200
+ "description",
201
+ "status"
202
+ ],
203
+ "properties": {
204
+ "description": {
205
+ "type": "string"
206
+ },
207
+ "implementedIn": {
208
+ "type": "string"
209
+ },
210
+ "status": {
211
+ "type": "string",
212
+ "enum": [
213
+ "planned",
214
+ "implemented",
215
+ "deprecated"
216
+ ]
217
+ }
218
+ }
219
+ }
220
+ }
221
+ }
@@ -0,0 +1,167 @@
1
+ /**
2
+ * 规则注入工具
3
+ *
4
+ * 用于在 workflow 文件头部注入规则子集表
5
+ *
6
+ * 用法:
7
+ * node scripts/inject-rules.js # 注入所有 workflow
8
+ * node scripts/inject-rules.js prd-c1 # 注入指定 workflow
9
+ */
10
+
11
+ const fs = require('fs');
12
+ const path = require('path');
13
+
14
+ // 加载规则索引
15
+ function loadRules() {
16
+ const rulesPath = path.join(__dirname, '../rules/index.json');
17
+ return JSON.parse(fs.readFileSync(rulesPath, 'utf-8'));
18
+ }
19
+
20
+ // Scope 到 Workflow 的映射
21
+ const scopeToWorkflow = {
22
+ 'p0': 'prd-p0-project-info',
23
+ 'b1': 'prd-b1-planning-draft',
24
+ 'b2': 'prd-b2-planning-breakdown',
25
+ 'c1': 'prd-c1-requirement-list',
26
+ 'r1': 'prd-r1-review',
27
+ 'r2': 'prd-r2-review'
28
+ };
29
+
30
+ // 获取指定 scope 的规则
31
+ function getRulesForScope(rules, scope) {
32
+ return rules.filter(rule =>
33
+ rule.scope.includes(scope) || rule.scope.includes('global')
34
+ );
35
+ }
36
+
37
+ // 生成规则表格 Markdown
38
+ function generateRulesTable(rules, scopeName) {
39
+ const lines = [
40
+ '',
41
+ '---',
42
+ '',
43
+ '## 🚨 本阶段必须遵守的规则',
44
+ '',
45
+ '> ⚠️ AI 在执行任务前必须逐条确认以下规则。输出时需包含自检清单。',
46
+ '',
47
+ '| ID | 规则 | 严重程度 | 自检 |',
48
+ '|----|------|----------|------|'
49
+ ];
50
+
51
+ rules.forEach(rule => {
52
+ const severity = rule.severity === 'CRITICAL' ? '🔴 严重' :
53
+ rule.severity === 'HIGH' ? '🟠 高' :
54
+ rule.severity === 'MEDIUM' ? '🟡 中' : '🟢 低';
55
+ lines.push(`| ${rule.id} | ${rule.description} | ${severity} | ☐ |`);
56
+ });
57
+
58
+ lines.push('');
59
+ lines.push('**自检清单模板**(AI 输出时必须包含):');
60
+ lines.push('```');
61
+ lines.push(`## ✅ 规则自检 (${scopeName})`);
62
+ rules.forEach(rule => {
63
+ lines.push(`- [ ] ${rule.id}: ${rule.description.substring(0, 30)}...`);
64
+ });
65
+ lines.push('```');
66
+ lines.push('');
67
+ lines.push('---');
68
+ lines.push('');
69
+
70
+ return lines.join('\n');
71
+ }
72
+
73
+ // 在 workflow 文件中注入规则表格
74
+ function injectRulesToWorkflow(workflowPath, rules, scopeName) {
75
+ const content = fs.readFileSync(workflowPath, 'utf-8');
76
+
77
+ // 检查是否已有规则表格
78
+ if (content.includes('## 🚨 本阶段必须遵守的规则')) {
79
+ console.log(` 跳过(已有规则表格): ${path.basename(workflowPath)}`);
80
+ return false;
81
+ }
82
+
83
+ // 找到第一个 # 标题的位置(跳过 frontmatter)
84
+ const lines = content.split('\n');
85
+ let insertIndex = 0;
86
+ let inFrontmatter = false;
87
+
88
+ for (let i = 0; i < lines.length; i++) {
89
+ if (lines[i].startsWith('---')) {
90
+ if (!inFrontmatter) {
91
+ inFrontmatter = true;
92
+ } else {
93
+ inFrontmatter = false;
94
+ }
95
+ continue;
96
+ }
97
+
98
+ // 找到第一个 # 开头的标题
99
+ if (!inFrontmatter && lines[i].startsWith('# ')) {
100
+ // 在标题之后插入
101
+ insertIndex = i + 1;
102
+ break;
103
+ }
104
+ }
105
+
106
+ if (insertIndex === 0) {
107
+ console.log(` 跳过(未找到标题): ${path.basename(workflowPath)}`);
108
+ return false;
109
+ }
110
+
111
+ // 生成规则表格
112
+ const rulesTable = generateRulesTable(rules, scopeName);
113
+
114
+ // 插入规则表格
115
+ lines.splice(insertIndex, 0, rulesTable);
116
+
117
+ fs.writeFileSync(workflowPath, lines.join('\n'));
118
+ console.log(` ✅ 已注入: ${path.basename(workflowPath)} (${rules.length} 条规则)`);
119
+ return true;
120
+ }
121
+
122
+ // 主函数
123
+ function main() {
124
+ const rulesIndex = loadRules();
125
+ const workflowDir = path.join(__dirname, '../.agent/workflows');
126
+
127
+ console.log('🔧 规则注入工具');
128
+ console.log('');
129
+
130
+ let injected = 0;
131
+ let skipped = 0;
132
+
133
+ // 遍历所有 scope
134
+ for (const [scope, workflowName] of Object.entries(scopeToWorkflow)) {
135
+ const workflowPath = path.join(workflowDir, `${workflowName}.md`);
136
+
137
+ if (!fs.existsSync(workflowPath)) {
138
+ console.log(` 跳过(文件不存在): ${workflowName}.md`);
139
+ skipped++;
140
+ continue;
141
+ }
142
+
143
+ const rules = getRulesForScope(rulesIndex.rules, scope);
144
+
145
+ if (injectRulesToWorkflow(workflowPath, rules, scope.toUpperCase())) {
146
+ injected++;
147
+ } else {
148
+ skipped++;
149
+ }
150
+ }
151
+
152
+ console.log('');
153
+ console.log(`完成:注入 ${injected} 个,跳过 ${skipped} 个`);
154
+ }
155
+
156
+ // 导出函数供其他模块使用
157
+ module.exports = {
158
+ loadRules,
159
+ getRulesForScope,
160
+ generateRulesTable,
161
+ injectRulesToWorkflow
162
+ };
163
+
164
+ // 如果直接运行脚本
165
+ if (require.main === module) {
166
+ main();
167
+ }
@@ -68,7 +68,7 @@
68
68
 
69
69
  <script>
70
70
  // UI 数据(内嵌)
71
- const UI_DATA = {{JSON_DATA}};
71
+ const UI_DATA = {{ JSON_DATA }};
72
72
 
73
73
  // A2UI 渲染器
74
74
  const { ConfigProvider, Card, Button, Input, Select, Table, Tabs, Tag, Badge, Space, Row, Col, Typography, Divider, Alert, Upload, Form } = antd;
@@ -93,7 +93,7 @@
93
93
  case 'Button': return React.createElement(Button, { key: props.key, type: props.variant === 'secondary' ? 'default' : props.variant === 'danger' ? 'primary' : 'primary', danger: props.variant === 'danger', style: { marginRight: 8 } }, props.text);
94
94
  case 'Text': return React.createElement(Text, { key: props.key, style: { display: 'block', marginBottom: 8 } }, props.content);
95
95
  case 'Tabs': return React.createElement(Tabs, { key: props.key, items: (props.items || []).map((item, i) => ({ key: String(i), label: item })), style: { marginBottom: 16 } });
96
- case 'Table':
96
+ case 'Table':
97
97
  const columns = (props.columns || []).map(col => {
98
98
  const column = { key: col.key || col, dataIndex: col.key || col, title: col.title || col };
99
99
  if (col.type === 'link') column.render = (text) => React.createElement('a', null, text);
@@ -112,7 +112,7 @@
112
112
  case 'Box': return React.createElement(Card, { key: props.key, size: 'small', style: { minWidth: 120, textAlign: 'center', borderLeft: props.color ? `3px solid ${props.color}` : undefined } }, React.createElement(Text, { strong: true }, props.title), props.desc && React.createElement('div', null, React.createElement(Text, { type: 'secondary', style: { fontSize: 12 } }, props.desc)));
113
113
  case 'Arrow': return React.createElement('div', { key: props.key, style: { color: 'white', fontSize: 24, textAlign: 'center' } }, (props.direction === 'up' ? '↑' : props.direction === 'left' ? '←' : props.direction === 'right' ? '→' : '↓'), props.label && React.createElement('span', { style: { fontSize: 12, marginLeft: 8 } }, props.label));
114
114
  case 'Layer': return React.createElement('div', { key: props.key, style: { display: 'flex', flexWrap: 'wrap', gap: 12, justifyContent: 'center', width: '100%' } }, props.title && React.createElement('div', { style: { width: '100%', textAlign: 'center', color: 'rgba(255,255,255,0.8)', fontSize: 12, marginBottom: 8 } }, props.title), children && children.map((child, i) => renderNode({ ...child, key: i })));
115
- case 'DiagramGroup': return React.createElement('div', { key: props.key, style: { background: 'rgba(255,255,255,0.1)', border: '1px dashed rgba(255,255,255,0.3)', borderRadius: 8, padding: 16, width: '100%' } }, props.title && React.createElement('div', { style: { color: 'rgba(255,255,255,0.9)', fontSize: 14, marginBottom: 12 } }, props.title), React.createElement('div', { style: { display: 'flex', flexWrap: 'wrap', gap: 12, justifyContent: 'center' } }, children && children.map((child, i) => renderNode({ ...child, key: i })));
115
+ case 'DiagramGroup': return React.createElement('div', { key: props.key, style: { background: 'rgba(255,255,255,0.1)', border: '1px dashed rgba(255,255,255,0.3)', borderRadius: 8, padding: 16, width: '100%' } }, props.title && React.createElement('div', { style: { color: 'rgba(255,255,255,0.9)', fontSize: 14, marginBottom: 12 } }, props.title), React.createElement('div', { style: { display: 'flex', flexWrap: 'wrap', gap: 12, justifyContent: 'center' } }, children && children.map((child, i) => renderNode({ ...child, key: i }))));
116
116
  default: return React.createElement(Alert, { key: props.key, type: 'warning', message: `未知组件: ${type}` });
117
117
  }
118
118
  };