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,228 @@
|
|
|
1
|
+
const fs = require('fs-extra');
|
|
2
|
+
const path = require('path');
|
|
3
|
+
const chalk = require('chalk');
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* 获取 CLI 包的安装路径
|
|
7
|
+
* 优先从 node_modules 中查找(项目内安装),否则使用当前模块路径(全局安装)
|
|
8
|
+
*/
|
|
9
|
+
function getPackagePath() {
|
|
10
|
+
// 项目内安装:检查当前工作目录的 node_modules
|
|
11
|
+
const localPath = path.join(process.cwd(), 'node_modules', 'prd-workflow-cli');
|
|
12
|
+
if (fs.existsSync(localPath)) {
|
|
13
|
+
return localPath;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
// 全局安装:使用模块自身的路径
|
|
17
|
+
return path.join(__dirname, '..');
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* 比较文件是否有变化
|
|
22
|
+
*/
|
|
23
|
+
async function filesAreDifferent(sourcePath, targetPath) {
|
|
24
|
+
if (!await fs.pathExists(targetPath)) {
|
|
25
|
+
return true; // 目标不存在,需要复制
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
try {
|
|
29
|
+
const sourceContent = await fs.readFile(sourcePath, 'utf8');
|
|
30
|
+
const targetContent = await fs.readFile(targetPath, 'utf8');
|
|
31
|
+
return sourceContent !== targetContent;
|
|
32
|
+
} catch {
|
|
33
|
+
return true;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* 递归获取目录下所有文件
|
|
39
|
+
*/
|
|
40
|
+
async function getAllFiles(dirPath, basePath = dirPath) {
|
|
41
|
+
const files = [];
|
|
42
|
+
const items = await fs.readdir(dirPath);
|
|
43
|
+
|
|
44
|
+
for (const item of items) {
|
|
45
|
+
const fullPath = path.join(dirPath, item);
|
|
46
|
+
const stat = await fs.stat(fullPath);
|
|
47
|
+
|
|
48
|
+
if (stat.isDirectory()) {
|
|
49
|
+
const subFiles = await getAllFiles(fullPath, basePath);
|
|
50
|
+
files.push(...subFiles);
|
|
51
|
+
} else {
|
|
52
|
+
files.push({
|
|
53
|
+
fullPath,
|
|
54
|
+
relativePath: path.relative(basePath, fullPath)
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
return files;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
module.exports = async function (options = {}) {
|
|
63
|
+
const projectPath = process.cwd();
|
|
64
|
+
const { force = false, dryRun = false } = options;
|
|
65
|
+
|
|
66
|
+
try {
|
|
67
|
+
// 检查是否是 PRD 项目
|
|
68
|
+
const configPath = path.join(projectPath, '.prd-config.json');
|
|
69
|
+
if (!await fs.pathExists(configPath)) {
|
|
70
|
+
console.log(chalk.red('✗ 当前目录不是 PRD 项目'));
|
|
71
|
+
console.log(chalk.gray(' 请在 PRD 项目根目录下运行此命令'));
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
const packagePath = getPackagePath();
|
|
76
|
+
console.log(chalk.blue('📦 正在检查更新...'));
|
|
77
|
+
console.log(chalk.gray(` 包路径: ${packagePath}`));
|
|
78
|
+
|
|
79
|
+
// 获取包版本
|
|
80
|
+
const packageJsonPath = path.join(packagePath, 'package.json');
|
|
81
|
+
let packageVersion = 'unknown';
|
|
82
|
+
if (await fs.pathExists(packageJsonPath)) {
|
|
83
|
+
const pkg = await fs.readJSON(packageJsonPath);
|
|
84
|
+
packageVersion = pkg.version;
|
|
85
|
+
}
|
|
86
|
+
console.log(chalk.gray(` 包版本: ${packageVersion}`));
|
|
87
|
+
console.log('');
|
|
88
|
+
|
|
89
|
+
// 定义需要更新的文件/目录
|
|
90
|
+
const updateItems = [
|
|
91
|
+
{
|
|
92
|
+
name: 'Workflows (工作流)',
|
|
93
|
+
source: '.agent/workflows',
|
|
94
|
+
target: '.agent/workflows',
|
|
95
|
+
isDir: true
|
|
96
|
+
},
|
|
97
|
+
{
|
|
98
|
+
name: 'Cursor Rules',
|
|
99
|
+
source: '.cursorrules',
|
|
100
|
+
target: '.cursorrules',
|
|
101
|
+
isDir: false
|
|
102
|
+
},
|
|
103
|
+
{
|
|
104
|
+
name: 'Antigravity Rules',
|
|
105
|
+
source: '.antigravity',
|
|
106
|
+
target: '.antigravity',
|
|
107
|
+
isDir: true
|
|
108
|
+
},
|
|
109
|
+
{
|
|
110
|
+
name: 'AI Guide',
|
|
111
|
+
source: 'AI-GUIDE.md',
|
|
112
|
+
target: 'AI-GUIDE.md',
|
|
113
|
+
isDir: false
|
|
114
|
+
}
|
|
115
|
+
];
|
|
116
|
+
|
|
117
|
+
const updatedFiles = [];
|
|
118
|
+
const skippedFiles = [];
|
|
119
|
+
const newFiles = [];
|
|
120
|
+
|
|
121
|
+
for (const item of updateItems) {
|
|
122
|
+
const sourcePath = path.join(packagePath, item.source);
|
|
123
|
+
const targetPath = path.join(projectPath, item.target);
|
|
124
|
+
|
|
125
|
+
if (!await fs.pathExists(sourcePath)) {
|
|
126
|
+
console.log(chalk.yellow(`⚠ ${item.name}: 源文件不存在,跳过`));
|
|
127
|
+
continue;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
if (item.isDir) {
|
|
131
|
+
// 处理目录
|
|
132
|
+
const files = await getAllFiles(sourcePath);
|
|
133
|
+
|
|
134
|
+
for (const file of files) {
|
|
135
|
+
const targetFilePath = path.join(targetPath, file.relativePath);
|
|
136
|
+
const isDifferent = await filesAreDifferent(file.fullPath, targetFilePath);
|
|
137
|
+
const isNew = !await fs.pathExists(targetFilePath);
|
|
138
|
+
|
|
139
|
+
if (isDifferent || force) {
|
|
140
|
+
if (!dryRun) {
|
|
141
|
+
await fs.ensureDir(path.dirname(targetFilePath));
|
|
142
|
+
await fs.copy(file.fullPath, targetFilePath);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
const displayPath = path.join(item.target, file.relativePath);
|
|
146
|
+
if (isNew) {
|
|
147
|
+
newFiles.push(displayPath);
|
|
148
|
+
} else if (isDifferent) {
|
|
149
|
+
updatedFiles.push(displayPath);
|
|
150
|
+
}
|
|
151
|
+
} else {
|
|
152
|
+
skippedFiles.push(path.join(item.target, file.relativePath));
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
} else {
|
|
156
|
+
// 处理单个文件
|
|
157
|
+
const isDifferent = await filesAreDifferent(sourcePath, targetPath);
|
|
158
|
+
const isNew = !await fs.pathExists(targetPath);
|
|
159
|
+
|
|
160
|
+
if (isDifferent || force) {
|
|
161
|
+
if (!dryRun) {
|
|
162
|
+
await fs.ensureDir(path.dirname(targetPath));
|
|
163
|
+
await fs.copy(sourcePath, targetPath);
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
if (isNew) {
|
|
167
|
+
newFiles.push(item.target);
|
|
168
|
+
} else if (isDifferent) {
|
|
169
|
+
updatedFiles.push(item.target);
|
|
170
|
+
}
|
|
171
|
+
} else {
|
|
172
|
+
skippedFiles.push(item.target);
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
// 输出结果
|
|
178
|
+
console.log(chalk.bold('📋 更新结果:'));
|
|
179
|
+
console.log('');
|
|
180
|
+
|
|
181
|
+
if (dryRun) {
|
|
182
|
+
console.log(chalk.yellow('🔍 预览模式(未实际更新)'));
|
|
183
|
+
console.log('');
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
if (newFiles.length > 0) {
|
|
187
|
+
console.log(chalk.green(`✨ 新增 ${newFiles.length} 个文件:`));
|
|
188
|
+
for (const file of newFiles) {
|
|
189
|
+
console.log(chalk.green(` + ${file}`));
|
|
190
|
+
}
|
|
191
|
+
console.log('');
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
if (updatedFiles.length > 0) {
|
|
195
|
+
console.log(chalk.cyan(`🔄 更新 ${updatedFiles.length} 个文件:`));
|
|
196
|
+
for (const file of updatedFiles) {
|
|
197
|
+
console.log(chalk.cyan(` ~ ${file}`));
|
|
198
|
+
}
|
|
199
|
+
console.log('');
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
if (skippedFiles.length > 0 && options.verbose) {
|
|
203
|
+
console.log(chalk.gray(`⏭ 跳过 ${skippedFiles.length} 个文件(无变化):`));
|
|
204
|
+
for (const file of skippedFiles) {
|
|
205
|
+
console.log(chalk.gray(` - ${file}`));
|
|
206
|
+
}
|
|
207
|
+
console.log('');
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
const totalChanges = newFiles.length + updatedFiles.length;
|
|
211
|
+
|
|
212
|
+
if (totalChanges === 0) {
|
|
213
|
+
console.log(chalk.green('✓ 所有文件已是最新版本!'));
|
|
214
|
+
} else if (!dryRun) {
|
|
215
|
+
console.log(chalk.green(`✓ 升级完成!共更新 ${totalChanges} 个文件`));
|
|
216
|
+
console.log('');
|
|
217
|
+
console.log(chalk.gray('提示: 建议检查更新后的文件,确保与项目配置兼容'));
|
|
218
|
+
} else {
|
|
219
|
+
console.log(chalk.yellow(`ℹ 如需执行更新,请去掉 --dry-run 参数`));
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
} catch (error) {
|
|
223
|
+
console.log(chalk.red('✗ 升级失败:'), error.message);
|
|
224
|
+
if (options.verbose) {
|
|
225
|
+
console.error(error);
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
};
|