yg-team-cli 2.4.10 → 2.5.1
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/README.md +10 -1
- package/dist/cli.js +363 -240
- package/dist/cli.js.map +1 -1
- package/dist/index.js +362 -239
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -213,9 +213,11 @@ var init_utils = __esm({
|
|
|
213
213
|
return await fs.readFile(file, "utf-8");
|
|
214
214
|
}
|
|
215
215
|
/**
|
|
216
|
-
*
|
|
216
|
+
* 写入文件内容(自动创建父目录)
|
|
217
217
|
*/
|
|
218
218
|
static async write(file, content) {
|
|
219
|
+
const dir = path2.dirname(file);
|
|
220
|
+
await fs.ensureDir(dir);
|
|
219
221
|
await fs.writeFile(file, content, "utf-8");
|
|
220
222
|
}
|
|
221
223
|
/**
|
|
@@ -3171,12 +3173,12 @@ var addFeatureCommand = new Command4("add-feature").argument("<feature-name>", "
|
|
|
3171
3173
|
process.exit(1);
|
|
3172
3174
|
}
|
|
3173
3175
|
const featureSlug = StringUtils.toKebabCase(featureName);
|
|
3174
|
-
const
|
|
3175
|
-
const
|
|
3176
|
-
if (
|
|
3177
|
-
logger.error(`
|
|
3176
|
+
const prdFile = path10.join("docs/prd-docs", `${featureSlug}.md`);
|
|
3177
|
+
const prdExists = await FileUtils.exists(prdFile);
|
|
3178
|
+
if (prdExists) {
|
|
3179
|
+
logger.error(`PRD \u6587\u4EF6\u5DF2\u5B58\u5728: ${prdFile}`);
|
|
3178
3180
|
logger.info("\u5982\u9700\u91CD\u65B0\u751F\u6210\uFF0C\u8BF7\u5148\u5220\u9664\uFF1A");
|
|
3179
|
-
logger.info(` rm ${
|
|
3181
|
+
logger.info(` rm ${prdFile}`);
|
|
3180
3182
|
process.exit(1);
|
|
3181
3183
|
}
|
|
3182
3184
|
const { mode } = await inquirer4.prompt([
|
|
@@ -3191,9 +3193,9 @@ var addFeatureCommand = new Command4("add-feature").argument("<feature-name>", "
|
|
|
3191
3193
|
}
|
|
3192
3194
|
]);
|
|
3193
3195
|
if (mode === "prd") {
|
|
3194
|
-
await addFeatureFromPrd(featureName, featureSlug,
|
|
3196
|
+
await addFeatureFromPrd(featureName, featureSlug, prdFile);
|
|
3195
3197
|
} else {
|
|
3196
|
-
await addFeatureSimple(featureName, featureSlug,
|
|
3198
|
+
await addFeatureSimple(featureName, featureSlug, prdFile);
|
|
3197
3199
|
}
|
|
3198
3200
|
} catch (error) {
|
|
3199
3201
|
logger.error(`\u6DFB\u52A0\u529F\u80FD\u5931\u8D25: ${error.message}`);
|
|
@@ -3203,7 +3205,7 @@ var addFeatureCommand = new Command4("add-feature").argument("<feature-name>", "
|
|
|
3203
3205
|
process.exit(1);
|
|
3204
3206
|
}
|
|
3205
3207
|
});
|
|
3206
|
-
async function addFeatureFromPrd(featureName,
|
|
3208
|
+
async function addFeatureFromPrd(featureName, _featureSlug, prdOutputFile) {
|
|
3207
3209
|
const { prdPath } = await inquirer4.prompt([
|
|
3208
3210
|
{
|
|
3209
3211
|
type: "input",
|
|
@@ -3218,68 +3220,62 @@ async function addFeatureFromPrd(featureName, featureSlug, specFile) {
|
|
|
3218
3220
|
const tasks = new Listr3([
|
|
3219
3221
|
{
|
|
3220
3222
|
title: "\u8BFB\u53D6 PRD \u6587\u6863",
|
|
3221
|
-
task: async (
|
|
3222
|
-
|
|
3223
|
+
task: async (ctx) => {
|
|
3224
|
+
ctx.prdContent = await FileUtils.read(prdPath);
|
|
3223
3225
|
}
|
|
3224
3226
|
},
|
|
3225
3227
|
{
|
|
3226
3228
|
title: "\u626B\u63CF\u5DF2\u5B8C\u6210\u529F\u80FD",
|
|
3227
|
-
task: async (
|
|
3229
|
+
task: async (ctx) => {
|
|
3228
3230
|
const specDir = "docs/specs";
|
|
3229
3231
|
const files = await FileUtils.findFiles("*.md", specDir);
|
|
3230
3232
|
const specs = files.filter((f) => !f.includes("template"));
|
|
3231
|
-
|
|
3233
|
+
ctx.completedSpecs = [];
|
|
3232
3234
|
for (const file of specs) {
|
|
3233
3235
|
const status = await SpecUtils.getSpecStatus(path10.join(specDir, file));
|
|
3234
3236
|
if (status === "\u5DF2\u5B8C\u6210") {
|
|
3235
|
-
|
|
3237
|
+
ctx.completedSpecs.push(file.replace(".md", ""));
|
|
3236
3238
|
}
|
|
3237
3239
|
}
|
|
3238
3240
|
}
|
|
3239
3241
|
},
|
|
3240
3242
|
{
|
|
3241
3243
|
title: "\u6784\u5EFA\u9879\u76EE\u4E0A\u4E0B\u6587",
|
|
3242
|
-
task: async (
|
|
3243
|
-
|
|
3244
|
+
task: async (ctx) => {
|
|
3245
|
+
ctx.projectContext = await buildProjectContext();
|
|
3244
3246
|
}
|
|
3245
3247
|
},
|
|
3246
3248
|
{
|
|
3247
|
-
title: "\u8C03\u7528 Claude \u751F\u6210
|
|
3248
|
-
task: async (
|
|
3249
|
+
title: "\u8C03\u7528 Claude \u751F\u6210 PRD",
|
|
3250
|
+
task: async (ctx) => {
|
|
3249
3251
|
const prompt = buildPrdPrompt(
|
|
3250
3252
|
featureName,
|
|
3251
|
-
|
|
3252
|
-
|
|
3253
|
-
|
|
3253
|
+
ctx.prdContent,
|
|
3254
|
+
ctx.projectContext,
|
|
3255
|
+
ctx.completedSpecs
|
|
3254
3256
|
);
|
|
3255
3257
|
const result = await claudeAI.prompt(prompt, {
|
|
3256
3258
|
contextFiles: ["TECH_STACK.md", "CONVENTIONS.md", "AI_MEMORY.md"]
|
|
3257
3259
|
});
|
|
3258
|
-
|
|
3259
|
-
}
|
|
3260
|
-
},
|
|
3261
|
-
{
|
|
3262
|
-
title: "\u4FDD\u5B58 spec \u6587\u4EF6",
|
|
3263
|
-
task: async (ctx2) => {
|
|
3264
|
-
await FileUtils.write(specFile, ctx2.generatedSpec);
|
|
3260
|
+
ctx.generatedPrd = result;
|
|
3265
3261
|
}
|
|
3266
3262
|
},
|
|
3267
3263
|
{
|
|
3268
|
-
title: "\
|
|
3269
|
-
task: async () => {
|
|
3270
|
-
await
|
|
3264
|
+
title: "\u4FDD\u5B58 PRD \u6587\u4EF6",
|
|
3265
|
+
task: async (ctx) => {
|
|
3266
|
+
await FileUtils.write(prdOutputFile, ctx.generatedPrd);
|
|
3271
3267
|
}
|
|
3272
3268
|
}
|
|
3273
3269
|
]);
|
|
3274
|
-
|
|
3270
|
+
await tasks.run();
|
|
3275
3271
|
logger.newLine();
|
|
3276
3272
|
logger.separator("\u2500", 60);
|
|
3277
3273
|
logger.newLine();
|
|
3278
|
-
logger.success(`
|
|
3279
|
-
await
|
|
3280
|
-
await askToAdjust(
|
|
3274
|
+
logger.success(`PRD \u6587\u4EF6\u5DF2\u751F\u6210: ${prdOutputFile}`);
|
|
3275
|
+
await showPrdPreview(prdOutputFile);
|
|
3276
|
+
await askToAdjust(prdOutputFile, "prd");
|
|
3281
3277
|
}
|
|
3282
|
-
async function addFeatureSimple(featureName,
|
|
3278
|
+
async function addFeatureSimple(featureName, _featureSlug, prdOutputFile) {
|
|
3283
3279
|
const { description } = await inquirer4.prompt([
|
|
3284
3280
|
{
|
|
3285
3281
|
type: "input",
|
|
@@ -3291,24 +3287,24 @@ async function addFeatureSimple(featureName, featureSlug, specFile) {
|
|
|
3291
3287
|
const tasks = new Listr3([
|
|
3292
3288
|
{
|
|
3293
3289
|
title: "\u626B\u63CF\u5DF2\u5B8C\u6210\u529F\u80FD",
|
|
3294
|
-
task: async (
|
|
3290
|
+
task: async (ctx) => {
|
|
3295
3291
|
const specDir = "docs/specs";
|
|
3296
3292
|
const files = await FileUtils.findFiles("*.md", specDir);
|
|
3297
3293
|
const specs = files.filter((f) => !f.includes("template"));
|
|
3298
|
-
|
|
3294
|
+
ctx.completedSpecs = [];
|
|
3299
3295
|
for (const file of specs) {
|
|
3300
3296
|
const status = await SpecUtils.getSpecStatus(path10.join(specDir, file));
|
|
3301
3297
|
if (status === "\u5DF2\u5B8C\u6210") {
|
|
3302
|
-
|
|
3298
|
+
ctx.completedSpecs.push(file.replace(".md", ""));
|
|
3303
3299
|
}
|
|
3304
3300
|
}
|
|
3305
3301
|
}
|
|
3306
3302
|
},
|
|
3307
3303
|
{
|
|
3308
3304
|
title: "\u9009\u62E9\u4F9D\u8D56\u529F\u80FD",
|
|
3309
|
-
task: async (
|
|
3310
|
-
if (
|
|
3311
|
-
|
|
3305
|
+
task: async (ctx) => {
|
|
3306
|
+
if (ctx.completedSpecs.length === 0) {
|
|
3307
|
+
ctx.selectedDeps = [];
|
|
3312
3308
|
return;
|
|
3313
3309
|
}
|
|
3314
3310
|
const { dependencies } = await inquirer4.prompt([
|
|
@@ -3316,53 +3312,47 @@ async function addFeatureSimple(featureName, featureSlug, specFile) {
|
|
|
3316
3312
|
type: "checkbox",
|
|
3317
3313
|
name: "dependencies",
|
|
3318
3314
|
message: "\u9009\u62E9\u6B64\u529F\u80FD\u4F9D\u8D56\u7684\u5DF2\u6709\u529F\u80FD (\u53EF\u591A\u9009\uFF0C\u76F4\u63A5\u56DE\u8F66\u8DF3\u8FC7):",
|
|
3319
|
-
choices:
|
|
3315
|
+
choices: ctx.completedSpecs
|
|
3320
3316
|
}
|
|
3321
3317
|
]);
|
|
3322
|
-
|
|
3318
|
+
ctx.selectedDeps = dependencies;
|
|
3323
3319
|
}
|
|
3324
3320
|
},
|
|
3325
3321
|
{
|
|
3326
3322
|
title: "\u6784\u5EFA\u9879\u76EE\u4E0A\u4E0B\u6587",
|
|
3327
|
-
task: async (
|
|
3328
|
-
|
|
3323
|
+
task: async (ctx) => {
|
|
3324
|
+
ctx.projectContext = await buildProjectContext();
|
|
3329
3325
|
}
|
|
3330
3326
|
},
|
|
3331
3327
|
{
|
|
3332
|
-
title: "\u8C03\u7528 Claude \u751F\u6210
|
|
3333
|
-
task: async (
|
|
3328
|
+
title: "\u8C03\u7528 Claude \u751F\u6210 PRD",
|
|
3329
|
+
task: async (ctx) => {
|
|
3334
3330
|
const prompt = buildSimplePrompt(
|
|
3335
3331
|
featureName,
|
|
3336
3332
|
description,
|
|
3337
|
-
|
|
3338
|
-
|
|
3333
|
+
ctx.projectContext,
|
|
3334
|
+
ctx.selectedDeps
|
|
3339
3335
|
);
|
|
3340
3336
|
const result = await claudeAI.prompt(prompt, {
|
|
3341
3337
|
contextFiles: ["TECH_STACK.md", "CONVENTIONS.md", "AI_MEMORY.md"]
|
|
3342
3338
|
});
|
|
3343
|
-
|
|
3344
|
-
}
|
|
3345
|
-
},
|
|
3346
|
-
{
|
|
3347
|
-
title: "\u4FDD\u5B58 spec \u6587\u4EF6",
|
|
3348
|
-
task: async (ctx2) => {
|
|
3349
|
-
await FileUtils.write(specFile, ctx2.generatedSpec);
|
|
3339
|
+
ctx.generatedPrd = result;
|
|
3350
3340
|
}
|
|
3351
3341
|
},
|
|
3352
3342
|
{
|
|
3353
|
-
title: "\
|
|
3354
|
-
task: async () => {
|
|
3355
|
-
await
|
|
3343
|
+
title: "\u4FDD\u5B58 PRD \u6587\u4EF6",
|
|
3344
|
+
task: async (ctx) => {
|
|
3345
|
+
await FileUtils.write(prdOutputFile, ctx.generatedPrd);
|
|
3356
3346
|
}
|
|
3357
3347
|
}
|
|
3358
3348
|
]);
|
|
3359
|
-
|
|
3349
|
+
await tasks.run();
|
|
3360
3350
|
logger.newLine();
|
|
3361
3351
|
logger.separator("\u2500", 60);
|
|
3362
3352
|
logger.newLine();
|
|
3363
|
-
logger.success(`
|
|
3364
|
-
await
|
|
3365
|
-
await askToAdjust(
|
|
3353
|
+
logger.success(`PRD \u6587\u4EF6\u5DF2\u751F\u6210: ${prdOutputFile}`);
|
|
3354
|
+
await showPrdPreview(prdOutputFile);
|
|
3355
|
+
await askToAdjust(prdOutputFile, "prd");
|
|
3366
3356
|
}
|
|
3367
3357
|
async function buildProjectContext() {
|
|
3368
3358
|
const context = [];
|
|
@@ -3563,79 +3553,19 @@ ${dependencies.map((d) => `- [x] ${d}`).join("\n") || "- (\u65E0)"}
|
|
|
3563
3553
|
5. \u6839\u636E\u529F\u80FD\u63CF\u8FF0\u63A8\u65AD\u5408\u7406\u7684 API \u548C\u6570\u636E\u6A21\u578B
|
|
3564
3554
|
6. \u53C2\u8003\u5DF2\u6709\u529F\u80FD\u7684\u6280\u672F\u98CE\u683C\u4FDD\u6301\u4E00\u81F4\u6027`;
|
|
3565
3555
|
}
|
|
3566
|
-
async function
|
|
3567
|
-
const aiMemoryFile = "AI_MEMORY.md";
|
|
3568
|
-
const exists = await FileUtils.exists(aiMemoryFile);
|
|
3569
|
-
if (!exists) {
|
|
3570
|
-
logger.warn("AI_MEMORY.md \u4E0D\u5B58\u5728\uFF0C\u8DF3\u8FC7\u66F4\u65B0");
|
|
3571
|
-
return;
|
|
3572
|
-
}
|
|
3573
|
-
let content = await FileUtils.read(aiMemoryFile);
|
|
3574
|
-
const featureDisplay = featureName.replace(/[-_]/g, " ").split(" ").map((word) => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase()).join(" ");
|
|
3575
|
-
const newRow = `| ${featureDisplay} | ${featureSlug}.md | \u25CB \u672A\u5F00\u59CB | 0/0 | - | |`;
|
|
3576
|
-
if (!content.includes("## \u529F\u80FD\u6E05\u5355")) {
|
|
3577
|
-
content += `
|
|
3578
|
-
## \u529F\u80FD\u6E05\u5355 (Feature Inventory)
|
|
3579
|
-
|
|
3580
|
-
| \u529F\u80FD | Spec \u6587\u4EF6 | \u72B6\u6001 | \u8FDB\u5EA6 | \u5B8C\u6210\u65E5\u671F | \u5907\u6CE8 |
|
|
3581
|
-
|------|----------|------|------|---------|------|
|
|
3582
|
-
${newRow}
|
|
3583
|
-
`;
|
|
3584
|
-
} else {
|
|
3585
|
-
const lines = content.split("\n");
|
|
3586
|
-
let featureInventorySection = false;
|
|
3587
|
-
let insertIndex = -1;
|
|
3588
|
-
for (let i = 0; i < lines.length; i++) {
|
|
3589
|
-
const line = lines[i];
|
|
3590
|
-
if (line.includes("## \u529F\u80FD\u6E05\u5355")) {
|
|
3591
|
-
featureInventorySection = true;
|
|
3592
|
-
continue;
|
|
3593
|
-
}
|
|
3594
|
-
if (featureInventorySection && line.startsWith("## ") && !line.includes("\u529F\u80FD\u6E05\u5355")) {
|
|
3595
|
-
break;
|
|
3596
|
-
}
|
|
3597
|
-
if (featureInventorySection && /^\|[-]+\|/.test(line.trim())) {
|
|
3598
|
-
insertIndex = i + 1;
|
|
3599
|
-
break;
|
|
3600
|
-
}
|
|
3601
|
-
}
|
|
3602
|
-
if (insertIndex !== -1) {
|
|
3603
|
-
lines.splice(insertIndex, 0, newRow);
|
|
3604
|
-
content = lines.join("\n");
|
|
3605
|
-
} else {
|
|
3606
|
-
const sectionIndex = lines.findIndex((line) => line.includes("## \u529F\u80FD\u6E05\u5355"));
|
|
3607
|
-
if (sectionIndex !== -1) {
|
|
3608
|
-
const tableLines = [
|
|
3609
|
-
"",
|
|
3610
|
-
"| \u529F\u80FD | Spec \u6587\u4EF6 | \u72B6\u6001 | \u8FDB\u5EA6 | \u5B8C\u6210\u65E5\u671F | \u5907\u6CE8 |",
|
|
3611
|
-
"|------|----------|------|------|---------|------|",
|
|
3612
|
-
newRow,
|
|
3613
|
-
""
|
|
3614
|
-
];
|
|
3615
|
-
lines.splice(sectionIndex + 1, 0, ...tableLines);
|
|
3616
|
-
content = lines.join("\n");
|
|
3617
|
-
} else {
|
|
3618
|
-
content += `
|
|
3619
|
-
${newRow}
|
|
3620
|
-
`;
|
|
3621
|
-
}
|
|
3622
|
-
}
|
|
3623
|
-
}
|
|
3624
|
-
await FileUtils.write(aiMemoryFile, content);
|
|
3625
|
-
}
|
|
3626
|
-
async function showSpecPreview(specFile) {
|
|
3556
|
+
async function showPrdPreview(prdFile) {
|
|
3627
3557
|
logger.newLine();
|
|
3628
|
-
logger.header("\u751F\u6210\u7684
|
|
3558
|
+
logger.header("\u751F\u6210\u7684 PRD \u9884\u89C8:");
|
|
3629
3559
|
logger.newLine();
|
|
3630
3560
|
try {
|
|
3631
|
-
const content = await FileUtils.read(
|
|
3561
|
+
const content = await FileUtils.read(prdFile);
|
|
3632
3562
|
if (!content || content.trim().length === 0) {
|
|
3633
|
-
logger.warn("
|
|
3563
|
+
logger.warn("PRD \u6587\u4EF6\u5185\u5BB9\u4E3A\u7A7A");
|
|
3634
3564
|
return;
|
|
3635
3565
|
}
|
|
3636
3566
|
const lines = content.split("\n");
|
|
3637
3567
|
if (lines.length === 0) {
|
|
3638
|
-
logger.warn("
|
|
3568
|
+
logger.warn("PRD \u6587\u4EF6\u6CA1\u6709\u5185\u5BB9");
|
|
3639
3569
|
return;
|
|
3640
3570
|
}
|
|
3641
3571
|
const preview = lines.slice(0, 40).join("\n");
|
|
@@ -3646,15 +3576,16 @@ async function showSpecPreview(specFile) {
|
|
|
3646
3576
|
}
|
|
3647
3577
|
console.log("");
|
|
3648
3578
|
} catch (error) {
|
|
3649
|
-
logger.error(`\u8BFB\u53D6
|
|
3579
|
+
logger.error(`\u8BFB\u53D6 PRD \u9884\u89C8\u5931\u8D25: ${error.message}`);
|
|
3650
3580
|
}
|
|
3651
3581
|
}
|
|
3652
|
-
async function askToAdjust(
|
|
3582
|
+
async function askToAdjust(file, type = "spec") {
|
|
3583
|
+
const typeLabel = type === "prd" ? "PRD" : "Spec";
|
|
3653
3584
|
const { needAdjust } = await inquirer4.prompt([
|
|
3654
3585
|
{
|
|
3655
3586
|
type: "confirm",
|
|
3656
3587
|
name: "needAdjust",
|
|
3657
|
-
message:
|
|
3588
|
+
message: `\u662F\u5426\u9700\u8981\u8C03\u6574 ${typeLabel} \u5185\u5BB9?`,
|
|
3658
3589
|
default: false
|
|
3659
3590
|
}
|
|
3660
3591
|
]);
|
|
@@ -3662,12 +3593,17 @@ async function askToAdjust(specFile) {
|
|
|
3662
3593
|
logger.info("\u6B63\u5728\u6253\u5F00\u7F16\u8F91\u5668...");
|
|
3663
3594
|
const editor = process.env.EDITOR || "vim";
|
|
3664
3595
|
const { execaCommand } = await import("execa");
|
|
3665
|
-
await execaCommand(`${editor} ${
|
|
3596
|
+
await execaCommand(`${editor} ${file}`, { stdio: "inherit" });
|
|
3666
3597
|
}
|
|
3667
3598
|
logger.newLine();
|
|
3668
3599
|
logger.info("\u4E0B\u4E00\u6B65:");
|
|
3669
|
-
|
|
3670
|
-
|
|
3600
|
+
if (type === "prd") {
|
|
3601
|
+
logger.step(`1. \u8FD0\u884C 'team-cli split-prd ${file}' \u751F\u6210 Spec \u89C4\u683C\u6587\u6863`);
|
|
3602
|
+
logger.step("2. \u8FD0\u884C 'team-cli breakdown <spec-file>' \u62C6\u5206\u4E3A milestones");
|
|
3603
|
+
} else {
|
|
3604
|
+
logger.step(`1. \u8FD0\u884C 'team-cli breakdown ${file}' \u62C6\u5206\u4E3A milestones`);
|
|
3605
|
+
logger.step("2. \u8FD0\u884C 'team-cli dev' \u9009\u62E9 milestone \u8FDB\u884C\u5F00\u53D1");
|
|
3606
|
+
}
|
|
3671
3607
|
logger.newLine();
|
|
3672
3608
|
}
|
|
3673
3609
|
|
|
@@ -3678,9 +3614,9 @@ init_logger();
|
|
|
3678
3614
|
import { Command as Command5 } from "commander";
|
|
3679
3615
|
import path11 from "path";
|
|
3680
3616
|
import { Listr as Listr4 } from "listr2";
|
|
3681
|
-
var splitPrdCommand = new Command5("split-prd").argument("<prd-
|
|
3617
|
+
var splitPrdCommand = new Command5("split-prd").argument("<prd-path>", "PRD \u6587\u6863\u8DEF\u5F84 (\u652F\u6301\u5355\u6587\u4EF6\u6216\u76EE\u5F55)").description("\u5C06 PRD \u8F6C\u6362\u4E3A Spec \u89C4\u683C\u6587\u6863").action(async (prdPath) => {
|
|
3682
3618
|
try {
|
|
3683
|
-
logger.header("PRD \
|
|
3619
|
+
logger.header("PRD \u2192 Spec \u8F6C\u6362");
|
|
3684
3620
|
logger.newLine();
|
|
3685
3621
|
const hasTechStack = await FileUtils.exists("TECH_STACK.md");
|
|
3686
3622
|
if (!hasTechStack) {
|
|
@@ -3688,9 +3624,9 @@ var splitPrdCommand = new Command5("split-prd").argument("<prd-folder>", "PRD \u
|
|
|
3688
3624
|
logger.info("\u8BF7\u5148\u8FD0\u884C 'team-cli init <project-name>' \u6216\u5207\u6362\u5230\u9879\u76EE\u76EE\u5F55");
|
|
3689
3625
|
process.exit(1);
|
|
3690
3626
|
}
|
|
3691
|
-
const
|
|
3692
|
-
if (!
|
|
3693
|
-
throw new Error(`PRD \
|
|
3627
|
+
const pathExists = await FileUtils.exists(prdPath);
|
|
3628
|
+
if (!pathExists) {
|
|
3629
|
+
throw new Error(`PRD \u8DEF\u5F84\u4E0D\u5B58\u5728: ${prdPath}`);
|
|
3694
3630
|
}
|
|
3695
3631
|
const hasClaude = await claudeAI.checkInstalled();
|
|
3696
3632
|
if (!hasClaude) {
|
|
@@ -3698,113 +3634,300 @@ var splitPrdCommand = new Command5("split-prd").argument("<prd-folder>", "PRD \u
|
|
|
3698
3634
|
logger.info("\u8BF7\u5B89\u88C5 Claude CLI: npm install -g @anthropic-ai/claude-code");
|
|
3699
3635
|
process.exit(1);
|
|
3700
3636
|
}
|
|
3701
|
-
const
|
|
3702
|
-
|
|
3703
|
-
|
|
3704
|
-
|
|
3705
|
-
|
|
3706
|
-
|
|
3707
|
-
|
|
3708
|
-
|
|
3709
|
-
|
|
3710
|
-
|
|
3711
|
-
|
|
3712
|
-
|
|
3713
|
-
|
|
3714
|
-
|
|
3715
|
-
|
|
3716
|
-
|
|
3717
|
-
|
|
3718
|
-
|
|
3719
|
-
|
|
3720
|
-
|
|
3721
|
-
|
|
3637
|
+
const isSingleFile = prdPath.endsWith(".md") || prdPath.endsWith(".txt") || prdPath.endsWith(".markdown");
|
|
3638
|
+
if (isSingleFile) {
|
|
3639
|
+
await processSinglePrd(prdPath);
|
|
3640
|
+
} else {
|
|
3641
|
+
await processMultiplePrds(prdPath);
|
|
3642
|
+
}
|
|
3643
|
+
} catch (error) {
|
|
3644
|
+
logger.error(`PRD \u8F6C\u6362\u5931\u8D25: ${error.message}`);
|
|
3645
|
+
if (process.env.DEBUG) {
|
|
3646
|
+
console.error(error);
|
|
3647
|
+
}
|
|
3648
|
+
process.exit(1);
|
|
3649
|
+
}
|
|
3650
|
+
});
|
|
3651
|
+
async function processSinglePrd(prdFile) {
|
|
3652
|
+
const baseName = path11.basename(prdFile, path11.extname(prdFile));
|
|
3653
|
+
const featureSlug = StringUtils.toKebabCase(baseName);
|
|
3654
|
+
const specFile = path11.join("docs/specs", `${featureSlug}.md`);
|
|
3655
|
+
const specExists = await FileUtils.exists(specFile);
|
|
3656
|
+
if (specExists) {
|
|
3657
|
+
logger.error(`Spec \u6587\u4EF6\u5DF2\u5B58\u5728: ${specFile}`);
|
|
3658
|
+
logger.info("\u5982\u9700\u91CD\u65B0\u751F\u6210\uFF0C\u8BF7\u5148\u5220\u9664\uFF1A");
|
|
3659
|
+
logger.info(` rm ${specFile}`);
|
|
3660
|
+
process.exit(1);
|
|
3661
|
+
}
|
|
3662
|
+
const tasks = new Listr4([
|
|
3663
|
+
{
|
|
3664
|
+
title: "\u8BFB\u53D6 PRD \u5185\u5BB9",
|
|
3665
|
+
task: async (ctx) => {
|
|
3666
|
+
ctx.prdContent = await FileUtils.read(prdFile);
|
|
3667
|
+
logger.success(`\u8BFB\u53D6 PRD: ${prdFile}`);
|
|
3668
|
+
}
|
|
3669
|
+
},
|
|
3670
|
+
{
|
|
3671
|
+
title: "\u8C03\u7528 Claude \u751F\u6210 Spec",
|
|
3672
|
+
task: async (ctx) => {
|
|
3673
|
+
const prompt = buildSinglePrdToSpecPrompt(ctx.prdContent, featureSlug);
|
|
3674
|
+
logger.newLine();
|
|
3675
|
+
logger.separator("\u2500", 60);
|
|
3676
|
+
logger.info("Claude \u6267\u884C\u4E2D...");
|
|
3677
|
+
logger.separator("\u2500", 60);
|
|
3678
|
+
logger.newLine();
|
|
3679
|
+
ctx.specContent = await claudeAI.prompt(prompt, {
|
|
3680
|
+
contextFiles: ["TECH_STACK.md", "CONVENTIONS.md", "AI_MEMORY.md"]
|
|
3681
|
+
});
|
|
3682
|
+
}
|
|
3683
|
+
},
|
|
3684
|
+
{
|
|
3685
|
+
title: "\u4FDD\u5B58 Spec \u6587\u4EF6",
|
|
3686
|
+
task: async (ctx) => {
|
|
3687
|
+
await FileUtils.write(specFile, ctx.specContent);
|
|
3688
|
+
}
|
|
3689
|
+
},
|
|
3690
|
+
{
|
|
3691
|
+
title: "\u66F4\u65B0 AI_MEMORY",
|
|
3692
|
+
task: async () => {
|
|
3693
|
+
await updateAiMemory(baseName, featureSlug);
|
|
3694
|
+
}
|
|
3695
|
+
}
|
|
3696
|
+
]);
|
|
3697
|
+
await tasks.run();
|
|
3698
|
+
logger.newLine();
|
|
3699
|
+
logger.separator("\u2500", 60);
|
|
3700
|
+
logger.newLine();
|
|
3701
|
+
logger.header("PRD \u2192 Spec \u8F6C\u6362\u5B8C\u6210!");
|
|
3702
|
+
logger.success(`Spec \u6587\u4EF6\u5DF2\u751F\u6210: ${specFile}`);
|
|
3703
|
+
logger.newLine();
|
|
3704
|
+
logger.info("\u4E0B\u4E00\u6B65:");
|
|
3705
|
+
logger.step(`1. \u8FD0\u884C 'team-cli breakdown ${specFile}' \u62C6\u5206\u4E3A milestones`);
|
|
3706
|
+
logger.step("2. \u8FD0\u884C 'team-cli dev' \u9009\u62E9 milestone \u8FDB\u884C\u5F00\u53D1");
|
|
3707
|
+
logger.newLine();
|
|
3708
|
+
}
|
|
3709
|
+
async function processMultiplePrds(prdFolder) {
|
|
3710
|
+
const tasks = new Listr4([
|
|
3711
|
+
{
|
|
3712
|
+
title: "\u626B\u63CF PRD \u6587\u4EF6",
|
|
3713
|
+
task: async (ctx) => {
|
|
3714
|
+
const supportedExtensions = ["md", "txt", "markdown"];
|
|
3715
|
+
ctx.prdFiles = [];
|
|
3716
|
+
for (const ext of supportedExtensions) {
|
|
3717
|
+
const files = await FileUtils.findFiles(`*.${ext}`, prdFolder);
|
|
3718
|
+
ctx.prdFiles.push(...files.map((f) => path11.join(prdFolder, f)));
|
|
3722
3719
|
}
|
|
3723
|
-
|
|
3724
|
-
|
|
3725
|
-
|
|
3726
|
-
|
|
3727
|
-
const screenshotDir = path11.join(prdFolder, "screenshots");
|
|
3728
|
-
const dirExists = await FileUtils.exists(screenshotDir);
|
|
3729
|
-
if (!dirExists) {
|
|
3730
|
-
logger.info("\u672A\u627E\u5230 screenshots \u76EE\u5F55\uFF0C\u8DF3\u8FC7\u622A\u56FE");
|
|
3731
|
-
ctx2.screenshots = [];
|
|
3732
|
-
return;
|
|
3733
|
-
}
|
|
3734
|
-
const imageExtensions = ["png", "jpg", "jpeg", "gif", "webp"];
|
|
3735
|
-
ctx2.screenshots = [];
|
|
3736
|
-
for (const ext of imageExtensions) {
|
|
3737
|
-
const files = await FileUtils.findFiles(`*.${ext}`, screenshotDir);
|
|
3738
|
-
ctx2.screenshots.push(...files.map((f) => path11.join(screenshotDir, f)));
|
|
3739
|
-
}
|
|
3740
|
-
logger.success(`\u627E\u5230 ${ctx2.screenshots.length} \u4E2A\u622A\u56FE\u6587\u4EF6`);
|
|
3720
|
+
if (ctx.prdFiles.length === 0) {
|
|
3721
|
+
throw new Error(
|
|
3722
|
+
"\u672A\u627E\u5230 PRD \u6587\u6863 (\u652F\u6301 .md, .txt, .markdown)"
|
|
3723
|
+
);
|
|
3741
3724
|
}
|
|
3742
|
-
|
|
3743
|
-
|
|
3744
|
-
|
|
3745
|
-
|
|
3746
|
-
|
|
3747
|
-
ctx2.demoRepos = [];
|
|
3748
|
-
for (const entry of entries) {
|
|
3749
|
-
const dirPath = path11.join(prdFolder, entry);
|
|
3750
|
-
const gitDir = path11.join(dirPath, ".git");
|
|
3751
|
-
const hasGit = await FileUtils.exists(gitDir);
|
|
3752
|
-
if (hasGit) {
|
|
3753
|
-
ctx2.demoRepos.push(dirPath);
|
|
3754
|
-
}
|
|
3755
|
-
}
|
|
3756
|
-
if (ctx2.demoRepos.length > 0) {
|
|
3757
|
-
logger.success(`\u627E\u5230 demo \u4ED3\u5E93: ${ctx2.demoRepos.join(", ")}`);
|
|
3758
|
-
} else {
|
|
3759
|
-
logger.info("\u672A\u627E\u5230 demo \u4EE3\u7801\u4ED3\u5E93\uFF0C\u8DF3\u8FC7");
|
|
3760
|
-
}
|
|
3725
|
+
ctx.prdFile = ctx.prdFiles[0];
|
|
3726
|
+
if (ctx.prdFiles.length > 1) {
|
|
3727
|
+
logger.info(`\u627E\u5230\u591A\u4E2A PRD \u6587\u6863\uFF0C\u4F7F\u7528: ${ctx.prdFile}`);
|
|
3728
|
+
} else {
|
|
3729
|
+
logger.success(`\u627E\u5230 PRD \u6587\u6863: ${ctx.prdFile}`);
|
|
3761
3730
|
}
|
|
3762
|
-
}
|
|
3763
|
-
|
|
3764
|
-
|
|
3765
|
-
|
|
3766
|
-
|
|
3731
|
+
}
|
|
3732
|
+
},
|
|
3733
|
+
{
|
|
3734
|
+
title: "\u626B\u63CF\u622A\u56FE\u6587\u4EF6",
|
|
3735
|
+
task: async (ctx) => {
|
|
3736
|
+
const screenshotDir = path11.join(prdFolder, "screenshots");
|
|
3737
|
+
const dirExists = await FileUtils.exists(screenshotDir);
|
|
3738
|
+
if (!dirExists) {
|
|
3739
|
+
logger.info("\u672A\u627E\u5230 screenshots \u76EE\u5F55\uFF0C\u8DF3\u8FC7\u622A\u56FE");
|
|
3740
|
+
ctx.screenshots = [];
|
|
3741
|
+
return;
|
|
3767
3742
|
}
|
|
3768
|
-
|
|
3769
|
-
|
|
3770
|
-
|
|
3771
|
-
|
|
3772
|
-
|
|
3773
|
-
ctx2.prdContent,
|
|
3774
|
-
ctx2.screenshots,
|
|
3775
|
-
ctx2.demoRepos
|
|
3776
|
-
);
|
|
3777
|
-
logger.newLine();
|
|
3778
|
-
logger.separator("\u2500", 60);
|
|
3779
|
-
logger.info("Claude \u6267\u884C\u4E2D...");
|
|
3780
|
-
logger.separator("\u2500", 60);
|
|
3781
|
-
logger.newLine();
|
|
3782
|
-
return await claudeAI.prompt(prompt, {
|
|
3783
|
-
contextFiles: ["TECH_STACK.md", "CONVENTIONS.md"]
|
|
3784
|
-
});
|
|
3743
|
+
const imageExtensions = ["png", "jpg", "jpeg", "gif", "webp"];
|
|
3744
|
+
ctx.screenshots = [];
|
|
3745
|
+
for (const ext of imageExtensions) {
|
|
3746
|
+
const files = await FileUtils.findFiles(`*.${ext}`, screenshotDir);
|
|
3747
|
+
ctx.screenshots.push(...files.map((f) => path11.join(screenshotDir, f)));
|
|
3785
3748
|
}
|
|
3749
|
+
logger.success(`\u627E\u5230 ${ctx.screenshots.length} \u4E2A\u622A\u56FE\u6587\u4EF6`);
|
|
3750
|
+
}
|
|
3751
|
+
},
|
|
3752
|
+
{
|
|
3753
|
+
title: "\u626B\u63CF demo \u4EE3\u7801\u4ED3\u5E93",
|
|
3754
|
+
task: async (ctx) => {
|
|
3755
|
+
const entries = await FileUtils.findFiles("*/", prdFolder);
|
|
3756
|
+
ctx.demoRepos = [];
|
|
3757
|
+
for (const entry of entries) {
|
|
3758
|
+
const dirPath = path11.join(prdFolder, entry);
|
|
3759
|
+
const gitDir = path11.join(dirPath, ".git");
|
|
3760
|
+
const hasGit = await FileUtils.exists(gitDir);
|
|
3761
|
+
if (hasGit) {
|
|
3762
|
+
ctx.demoRepos.push(dirPath);
|
|
3763
|
+
}
|
|
3764
|
+
}
|
|
3765
|
+
if (ctx.demoRepos.length > 0) {
|
|
3766
|
+
logger.success(`\u627E\u5230 demo \u4ED3\u5E93: ${ctx.demoRepos.join(", ")}`);
|
|
3767
|
+
} else {
|
|
3768
|
+
logger.info("\u672A\u627E\u5230 demo \u4EE3\u7801\u4ED3\u5E93\uFF0C\u8DF3\u8FC7");
|
|
3769
|
+
}
|
|
3770
|
+
}
|
|
3771
|
+
},
|
|
3772
|
+
{
|
|
3773
|
+
title: "\u8BFB\u53D6 PRD \u5185\u5BB9",
|
|
3774
|
+
task: async (ctx) => {
|
|
3775
|
+
ctx.prdContent = await FileUtils.read(ctx.prdFile);
|
|
3776
|
+
}
|
|
3777
|
+
},
|
|
3778
|
+
{
|
|
3779
|
+
title: "\u8C03\u7528 Claude \u62C6\u5206 PRD",
|
|
3780
|
+
task: async (ctx) => {
|
|
3781
|
+
const prompt = buildSplitPrdPrompt(
|
|
3782
|
+
ctx.prdContent,
|
|
3783
|
+
ctx.screenshots,
|
|
3784
|
+
ctx.demoRepos
|
|
3785
|
+
);
|
|
3786
|
+
logger.newLine();
|
|
3787
|
+
logger.separator("\u2500", 60);
|
|
3788
|
+
logger.info("Claude \u6267\u884C\u4E2D...");
|
|
3789
|
+
logger.separator("\u2500", 60);
|
|
3790
|
+
logger.newLine();
|
|
3791
|
+
return await claudeAI.prompt(prompt, {
|
|
3792
|
+
contextFiles: ["TECH_STACK.md", "CONVENTIONS.md"]
|
|
3793
|
+
});
|
|
3786
3794
|
}
|
|
3787
|
-
]);
|
|
3788
|
-
const ctx = await tasks.run();
|
|
3789
|
-
logger.newLine();
|
|
3790
|
-
logger.separator("\u2500", 60);
|
|
3791
|
-
logger.newLine();
|
|
3792
|
-
logger.header("PRD \u62C6\u5206\u5B8C\u6210!");
|
|
3793
|
-
logger.success("Spec \u6587\u4EF6\u5DF2\u751F\u6210\u5230 docs/specs/ \u76EE\u5F55");
|
|
3794
|
-
logger.newLine();
|
|
3795
|
-
logger.info("\u4E0B\u4E00\u6B65:");
|
|
3796
|
-
logger.step("1. \u68C0\u67E5\u751F\u6210\u7684 spec \u6587\u4EF6");
|
|
3797
|
-
logger.step("2. \u8FD0\u884C 'team-cli breakdown <spec-file>' \u62C6\u5206 milestones");
|
|
3798
|
-
logger.step("3. \u8FD0\u884C 'team-cli dev' \u5F00\u59CB\u5F00\u53D1");
|
|
3799
|
-
logger.newLine();
|
|
3800
|
-
} catch (error) {
|
|
3801
|
-
logger.error(`PRD \u62C6\u5206\u5931\u8D25: ${error.message}`);
|
|
3802
|
-
if (process.env.DEBUG) {
|
|
3803
|
-
console.error(error);
|
|
3804
3795
|
}
|
|
3805
|
-
|
|
3796
|
+
]);
|
|
3797
|
+
await tasks.run();
|
|
3798
|
+
logger.newLine();
|
|
3799
|
+
logger.separator("\u2500", 60);
|
|
3800
|
+
logger.newLine();
|
|
3801
|
+
logger.header("PRD \u62C6\u5206\u5B8C\u6210!");
|
|
3802
|
+
logger.success("Spec \u6587\u4EF6\u5DF2\u751F\u6210\u5230 docs/specs/ \u76EE\u5F55");
|
|
3803
|
+
logger.newLine();
|
|
3804
|
+
logger.info("\u4E0B\u4E00\u6B65:");
|
|
3805
|
+
logger.step("1. \u68C0\u67E5\u751F\u6210\u7684 spec \u6587\u4EF6");
|
|
3806
|
+
logger.step("2. \u8FD0\u884C 'team-cli breakdown <spec-file>' \u62C6\u5206 milestones");
|
|
3807
|
+
logger.step("3. \u8FD0\u884C 'team-cli dev' \u5F00\u59CB\u5F00\u53D1");
|
|
3808
|
+
logger.newLine();
|
|
3809
|
+
}
|
|
3810
|
+
async function updateAiMemory(featureName, featureSlug) {
|
|
3811
|
+
const aiMemoryFile = "AI_MEMORY.md";
|
|
3812
|
+
const memoryExists = await FileUtils.exists(aiMemoryFile);
|
|
3813
|
+
if (!memoryExists) {
|
|
3814
|
+
logger.warn("AI_MEMORY.md \u4E0D\u5B58\u5728\uFF0C\u8DF3\u8FC7\u66F4\u65B0");
|
|
3815
|
+
return;
|
|
3806
3816
|
}
|
|
3807
|
-
|
|
3817
|
+
let content = await FileUtils.read(aiMemoryFile);
|
|
3818
|
+
const featureDisplay = featureName.replace(/[-_]/g, " ").split(" ").map((word) => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase()).join(" ");
|
|
3819
|
+
const newRow = `| ${featureDisplay} | ${featureSlug}.md | \u25CB \u672A\u5F00\u59CB | 0/0 | - | |`;
|
|
3820
|
+
if (content.includes(`| ${featureDisplay} |`) || content.includes(`${featureSlug}.md`)) {
|
|
3821
|
+
logger.info(`\u529F\u80FD ${featureDisplay} \u5DF2\u5B58\u5728\u4E8E AI_MEMORY\uFF0C\u8DF3\u8FC7`);
|
|
3822
|
+
return;
|
|
3823
|
+
}
|
|
3824
|
+
const lines = content.split("\n");
|
|
3825
|
+
let insertIndex = -1;
|
|
3826
|
+
let featureInventorySection = false;
|
|
3827
|
+
for (let i = 0; i < lines.length; i++) {
|
|
3828
|
+
const line = lines[i];
|
|
3829
|
+
if (line.includes("\u529F\u80FD\u6E05\u5355") || line.includes("Feature Inventory")) {
|
|
3830
|
+
featureInventorySection = true;
|
|
3831
|
+
}
|
|
3832
|
+
if (featureInventorySection && /^\|[-]+\|/.test(line.trim())) {
|
|
3833
|
+
insertIndex = i + 1;
|
|
3834
|
+
break;
|
|
3835
|
+
}
|
|
3836
|
+
}
|
|
3837
|
+
if (insertIndex > 0) {
|
|
3838
|
+
lines.splice(insertIndex, 0, newRow);
|
|
3839
|
+
content = lines.join("\n");
|
|
3840
|
+
} else {
|
|
3841
|
+
content += `
|
|
3842
|
+
${newRow}
|
|
3843
|
+
`;
|
|
3844
|
+
}
|
|
3845
|
+
await FileUtils.write(aiMemoryFile, content);
|
|
3846
|
+
}
|
|
3847
|
+
function buildSinglePrdToSpecPrompt(prdContent, featureSlug) {
|
|
3848
|
+
return `Role: Senior Technical Architect
|
|
3849
|
+
|
|
3850
|
+
Task: Convert the following PRD (Product Requirements Document) into a detailed technical Spec.
|
|
3851
|
+
|
|
3852
|
+
Context:
|
|
3853
|
+
- Read TECH_STACK.md for technology constraints
|
|
3854
|
+
- Read CONVENTIONS.md for coding standards
|
|
3855
|
+
- Read AI_MEMORY.md for project status
|
|
3856
|
+
|
|
3857
|
+
PRD Content:
|
|
3858
|
+
\`\`\`
|
|
3859
|
+
${prdContent}
|
|
3860
|
+
\`\`\`
|
|
3861
|
+
|
|
3862
|
+
Output Requirements:
|
|
3863
|
+
Generate a complete Spec document with the following sections:
|
|
3864
|
+
|
|
3865
|
+
\`\`\`markdown
|
|
3866
|
+
# [\u529F\u80FD\u6807\u9898]
|
|
3867
|
+
|
|
3868
|
+
## \u529F\u80FD\u6982\u8FF0
|
|
3869
|
+
**\u529F\u80FD\u540D\u79F0**: [\u529F\u80FD\u4E2D\u6587\u540D]
|
|
3870
|
+
**\u4F18\u5148\u7EA7**: P0/P1/P2 (\u6839\u636E\u529F\u80FD\u91CD\u8981\u6027\u5224\u65AD)
|
|
3871
|
+
**\u9884\u4F30\u5DE5\u65F6**: X \u5929 (\u6839\u636E\u590D\u6742\u5EA6\u8BC4\u4F30\uFF1A\u7B80\u53551-2\u5929\uFF0C\u4E2D\u7B493-5\u5929\uFF0C\u590D\u67425-10\u5929)
|
|
3872
|
+
**\u72B6\u6001**: \u5F85\u62C6\u5206
|
|
3873
|
+
**\u521B\u5EFA\u65E5\u671F**: ${(/* @__PURE__ */ new Date()).toISOString().split("T")[0]}
|
|
3874
|
+
|
|
3875
|
+
## \u4F9D\u8D56\u5173\u7CFB
|
|
3876
|
+
**\u524D\u7F6E\u4F9D\u8D56**:
|
|
3877
|
+
- (\u5217\u51FA\u4F9D\u8D56\u7684\u5176\u4ED6\u529F\u80FD)
|
|
3878
|
+
|
|
3879
|
+
**\u88AB\u4F9D\u8D56\u4E8E**:
|
|
3880
|
+
- (\u81EA\u52A8\u751F\u6210)
|
|
3881
|
+
|
|
3882
|
+
## \u80CC\u666F\u4E0E\u76EE\u6807
|
|
3883
|
+
[\u6839\u636E PRD \u63D0\u53D6\u80CC\u666F\u548C\u76EE\u6807]
|
|
3884
|
+
|
|
3885
|
+
## \u529F\u80FD\u9700\u6C42
|
|
3886
|
+
### \u7528\u6237\u6545\u4E8B
|
|
3887
|
+
\`\`\`
|
|
3888
|
+
\u4F5C\u4E3A [\u5177\u4F53\u89D2\u8272]
|
|
3889
|
+
\u6211\u5E0C\u671B [\u5177\u4F53\u529F\u80FD]
|
|
3890
|
+
\u4EE5\u4FBF [\u5B9E\u73B0\u7684\u4EF7\u503C]
|
|
3891
|
+
\`\`\`
|
|
3892
|
+
|
|
3893
|
+
### \u529F\u80FD\u70B9
|
|
3894
|
+
[\u4ECE PRD \u63D0\u53D6 3-8 \u4E2A\u4E3B\u8981\u529F\u80FD\u70B9]
|
|
3895
|
+
|
|
3896
|
+
## \u6280\u672F\u8BBE\u8BA1
|
|
3897
|
+
### API \u8BBE\u8BA1
|
|
3898
|
+
\u5217\u51FA\u4E3B\u8981\u7684 API \u7AEF\u70B9\uFF0C\u683C\u5F0F\uFF1A
|
|
3899
|
+
- \`METHOD /api/path\` - \u7B80\u77ED\u8BF4\u660E
|
|
3900
|
+
|
|
3901
|
+
### \u6570\u636E\u6A21\u578B
|
|
3902
|
+
\u5217\u51FA\u9700\u8981\u7684\u6570\u636E\u8868\uFF0C\u683C\u5F0F\uFF1A
|
|
3903
|
+
- \`table_name\` (\u8868\u8BF4\u660E) - \u5B57\u6BB5\u8BF4\u660E
|
|
3904
|
+
|
|
3905
|
+
### \u524D\u7AEF\u7EC4\u4EF6
|
|
3906
|
+
\u5217\u51FA\u9700\u8981\u7684\u4E3B\u8981\u524D\u7AEF\u7EC4\u4EF6
|
|
3907
|
+
|
|
3908
|
+
### \u540E\u7AEF\u6A21\u5757
|
|
3909
|
+
\u5217\u51FA\u9700\u8981\u7684\u540E\u7AEF\u6A21\u5757\u7ED3\u6784
|
|
3910
|
+
|
|
3911
|
+
## \u9A8C\u6536\u6807\u51C6
|
|
3912
|
+
- [ ] \u9A8C\u6536\u6807\u51C6 1
|
|
3913
|
+
- [ ] \u9A8C\u6536\u6807\u51C6 2
|
|
3914
|
+
- [ ] \u9A8C\u6536\u6807\u51C6 3
|
|
3915
|
+
|
|
3916
|
+
## \u91CC\u7A0B\u7891 (Milestones)
|
|
3917
|
+
> \u6CE8: \u4F7F\u7528 \`team-cli breakdown ${featureSlug}.md\` \u62C6\u5206\u6B64 spec \u4E3A milestones \u548C todos
|
|
3918
|
+
|
|
3919
|
+
----
|
|
3920
|
+
*\u751F\u6210\u4E8E: ${(/* @__PURE__ */ new Date()).toISOString()} by Claude*
|
|
3921
|
+
\`\`\`
|
|
3922
|
+
|
|
3923
|
+
IMPORTANT:
|
|
3924
|
+
1. Only output the Spec document content, no additional text
|
|
3925
|
+
2. Make sure to include comprehensive API and data model design
|
|
3926
|
+
3. Priority should be justified based on feature importance
|
|
3927
|
+
4. Work estimation should be realistic
|
|
3928
|
+
5. Extract key requirements from the PRD accurately
|
|
3929
|
+
`;
|
|
3930
|
+
}
|
|
3808
3931
|
function buildSplitPrdPrompt(prdContent, screenshots, demoRepos) {
|
|
3809
3932
|
let prompt = `Role: Senior Product Manager and Technical Architect
|
|
3810
3933
|
|