yg-team-cli 2.5.2 → 2.5.4
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 +8 -0
- package/dist/cli.js +94 -30
- package/dist/cli.js.map +1 -1
- package/dist/index.js +94 -30
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -46,6 +46,14 @@
|
|
|
46
46
|
**v2.5.2** - init 命令新增 prd-docs 目录
|
|
47
47
|
- **新增**: `team-cli init` 现在默认创建 `docs/prd-docs/` 目录供存放 PRD 需求文档
|
|
48
48
|
|
|
49
|
+
**v2.5.3** - 优化 breakdown 命令合并逻辑
|
|
50
|
+
- **改进**: `breakdown` 现在仅请求生成里程碑部分,并精确合并到原 Spec 文件
|
|
51
|
+
- **修复**: 解决了 `breakdown` 命令可能会覆盖原文件技术设计内容的问题 (防止 Claude 的内容截断)
|
|
52
|
+
|
|
53
|
+
**v2.5.4** - 修复 dev 模式交互卡死问题
|
|
54
|
+
- **修复**: `dev` 命令现在真正进入交互式 Claude Code 会话,解决之前因非交互模式导致的长时间无反馈和卡死问题
|
|
55
|
+
- **改进**: `ClaudeAI` 类新增 `runTerminal` 方法支持 `stdio: 'inherit'` 交互模式
|
|
56
|
+
|
|
49
57
|
**v2.4.10** - 修复 Claude 返回权限确认而非实际内容问题
|
|
50
58
|
- **修复**: 添加 `--dangerously-skip-permissions` 参数跳过权限确认
|
|
51
59
|
- **问题**: v2.4.9 中 Claude CLI 在新目录首次运行时返回权限确认提示而非 spec 内容
|
package/dist/cli.js
CHANGED
|
@@ -544,6 +544,33 @@ ${promptText}`;
|
|
|
544
544
|
}
|
|
545
545
|
return await this.prompt(promptText, options);
|
|
546
546
|
}
|
|
547
|
+
/**
|
|
548
|
+
* 启动交互式会话
|
|
549
|
+
*/
|
|
550
|
+
async runTerminal(promptText, _options) {
|
|
551
|
+
logger.info("\u6B63\u5728\u542F\u52A8\u4EA4\u4E92\u5F0F Claude \u4F1A\u8BDD...");
|
|
552
|
+
const args = ["--dangerously-skip-permissions"];
|
|
553
|
+
args.push("--add-dir", process.cwd());
|
|
554
|
+
try {
|
|
555
|
+
logger.newLine();
|
|
556
|
+
logger.info("\u590D\u5236\u4EE5\u4E0B\u6307\u4EE4\u53D1\u9001\u7ED9 Claude:");
|
|
557
|
+
logger.separator("-", 20);
|
|
558
|
+
console.log(promptText);
|
|
559
|
+
logger.separator("-", 20);
|
|
560
|
+
logger.newLine();
|
|
561
|
+
await execa("claude", args, {
|
|
562
|
+
stdio: "inherit",
|
|
563
|
+
env: { ...process.env, FORCE_COLOR: "1" }
|
|
564
|
+
});
|
|
565
|
+
} catch (error) {
|
|
566
|
+
if (error.signal !== "SIGINT") {
|
|
567
|
+
throw error;
|
|
568
|
+
}
|
|
569
|
+
}
|
|
570
|
+
}
|
|
571
|
+
/**
|
|
572
|
+
* 发送对话(支持上下文)
|
|
573
|
+
*/
|
|
547
574
|
/**
|
|
548
575
|
* 发送对话(支持上下文)
|
|
549
576
|
*/
|
|
@@ -2517,6 +2544,58 @@ import { Command as Command2 } from "commander";
|
|
|
2517
2544
|
import inquirer2 from "inquirer";
|
|
2518
2545
|
import path8 from "path";
|
|
2519
2546
|
import { Listr as Listr2 } from "listr2";
|
|
2547
|
+
function mergeMilestones(original, milestones) {
|
|
2548
|
+
const milestoneHeader = "## \u91CC\u7A0B\u7891 (Milestones)";
|
|
2549
|
+
const lines = original.split("\n");
|
|
2550
|
+
const headerIndex = lines.findIndex((l) => l.trim().startsWith(milestoneHeader));
|
|
2551
|
+
let cleanedMilestones = milestones.trim();
|
|
2552
|
+
if (!cleanedMilestones.startsWith("## ")) {
|
|
2553
|
+
const firstHeaderIndex = cleanedMilestones.indexOf("## ");
|
|
2554
|
+
if (firstHeaderIndex !== -1) {
|
|
2555
|
+
cleanedMilestones = cleanedMilestones.substring(firstHeaderIndex);
|
|
2556
|
+
} else {
|
|
2557
|
+
cleanedMilestones = `${milestoneHeader}
|
|
2558
|
+
|
|
2559
|
+
${cleanedMilestones}`;
|
|
2560
|
+
}
|
|
2561
|
+
}
|
|
2562
|
+
if (headerIndex !== -1) {
|
|
2563
|
+
const beforePart = lines.slice(0, headerIndex).join("\n");
|
|
2564
|
+
const afterPart = lines.slice(headerIndex + 1);
|
|
2565
|
+
const nextHeaderIndex = afterPart.findIndex((l) => l.trim().startsWith("## "));
|
|
2566
|
+
if (nextHeaderIndex !== -1) {
|
|
2567
|
+
const remaining = afterPart.slice(nextHeaderIndex).join("\n");
|
|
2568
|
+
return `${beforePart.trim()}
|
|
2569
|
+
|
|
2570
|
+
${cleanedMilestones}
|
|
2571
|
+
|
|
2572
|
+
${remaining.trim()}`;
|
|
2573
|
+
} else {
|
|
2574
|
+
return `${beforePart.trim()}
|
|
2575
|
+
|
|
2576
|
+
${cleanedMilestones}`;
|
|
2577
|
+
}
|
|
2578
|
+
}
|
|
2579
|
+
const techDesignHeader = "## \u6280\u672F\u8BBE\u8BA1";
|
|
2580
|
+
const techIndex = lines.findIndex((l) => l.trim().startsWith(techDesignHeader));
|
|
2581
|
+
if (techIndex !== -1) {
|
|
2582
|
+
const afterTech = lines.slice(techIndex + 1);
|
|
2583
|
+
const nextHeaderIndex = afterTech.findIndex((l) => l.trim().startsWith("## "));
|
|
2584
|
+
if (nextHeaderIndex !== -1) {
|
|
2585
|
+
const splitPoint = techIndex + 1 + nextHeaderIndex;
|
|
2586
|
+
const before = lines.slice(0, splitPoint).join("\n");
|
|
2587
|
+
const after = lines.slice(splitPoint).join("\n");
|
|
2588
|
+
return `${before.trim()}
|
|
2589
|
+
|
|
2590
|
+
${cleanedMilestones}
|
|
2591
|
+
|
|
2592
|
+
${after.trim()}`;
|
|
2593
|
+
}
|
|
2594
|
+
}
|
|
2595
|
+
return `${original.trim()}
|
|
2596
|
+
|
|
2597
|
+
${cleanedMilestones}`;
|
|
2598
|
+
}
|
|
2520
2599
|
function buildBreakdownPrompt(specContent) {
|
|
2521
2600
|
return `Role: Senior Technical Lead and Agile Coach
|
|
2522
2601
|
|
|
@@ -2534,18 +2613,21 @@ ${specContent}
|
|
|
2534
2613
|
\`\`\`
|
|
2535
2614
|
|
|
2536
2615
|
Output Requirements:
|
|
2537
|
-
1. Parse the existing spec content
|
|
2538
|
-
2. Break it down into 2-5 milestones
|
|
2616
|
+
1. Parse the existing spec content and identify technical requirements.
|
|
2617
|
+
2. Break it down into 2-5 milestones.
|
|
2539
2618
|
3. Each milestone should have:
|
|
2540
2619
|
- Clear name and objective
|
|
2541
2620
|
- Estimated days (1-3 days per milestone)
|
|
2542
2621
|
- Todo list with 3-8 actionable items
|
|
2543
2622
|
4. Todo items should be:
|
|
2544
|
-
- Concrete and
|
|
2545
|
-
-
|
|
2546
|
-
- Independent as much as possible
|
|
2623
|
+
- Concrete, specific, and testable.
|
|
2624
|
+
- Independent as much as possible.
|
|
2547
2625
|
|
|
2548
|
-
|
|
2626
|
+
IMPORTANT:
|
|
2627
|
+
- Output ONLY the "## \u91CC\u7A0B\u7891 (Milestones)" section.
|
|
2628
|
+
- Do NOT output the entire spec file.
|
|
2629
|
+
- Do NOT output any preamble or postamble text.
|
|
2630
|
+
- Follow this exact format:
|
|
2549
2631
|
|
|
2550
2632
|
\`\`\`markdown
|
|
2551
2633
|
## \u91CC\u7A0B\u7891 (Milestones)
|
|
@@ -2559,24 +2641,9 @@ Format the milestones section as:
|
|
|
2559
2641
|
- [ ] Todo 3 - \u5177\u4F53\u53EF\u6267\u884C\u7684\u4EFB\u52A1
|
|
2560
2642
|
|
|
2561
2643
|
### Milestone 2: [\u91CC\u7A0B\u7891\u540D\u79F0]
|
|
2562
|
-
|
|
2563
|
-
**\u9884\u4F30**: 3 \u5929
|
|
2564
|
-
|
|
2565
|
-
- [ ] Todo 1
|
|
2566
|
-
- [ ] Todo 2
|
|
2567
|
-
- [ ] Todo 3
|
|
2568
|
-
- [ ] Todo 4
|
|
2644
|
+
...
|
|
2569
2645
|
\`\`\`
|
|
2570
|
-
|
|
2571
|
-
Important Instructions:
|
|
2572
|
-
1. Update the spec file directly with the milestone breakdown
|
|
2573
|
-
2. Keep all existing content, just add/update the milestones section
|
|
2574
|
-
3. If milestones section exists, replace it with new breakdown
|
|
2575
|
-
4. If milestones section doesn't exist, add it after "\u6280\u672F\u8BBE\u8BA1" section
|
|
2576
|
-
5. After updating the file, exit immediately
|
|
2577
|
-
6. Do not ask any questions
|
|
2578
|
-
7. Make sure todos are actionable and can be completed independently
|
|
2579
|
-
8. Consider dependencies when ordering todos within a milestone`;
|
|
2646
|
+
`;
|
|
2580
2647
|
}
|
|
2581
2648
|
var breakdownCommand;
|
|
2582
2649
|
var init_breakdown = __esm({
|
|
@@ -2669,7 +2736,8 @@ var init_breakdown = __esm({
|
|
|
2669
2736
|
{
|
|
2670
2737
|
title: "\u66F4\u65B0 spec \u6587\u4EF6",
|
|
2671
2738
|
task: async (ctx) => {
|
|
2672
|
-
|
|
2739
|
+
const mergedContent = mergeMilestones(ctx.specContent, ctx.breakdownResult);
|
|
2740
|
+
await FileUtils.write(ctx.selectedFile, mergedContent);
|
|
2673
2741
|
}
|
|
2674
2742
|
}
|
|
2675
2743
|
]);
|
|
@@ -2736,7 +2804,6 @@ async function selectSpec() {
|
|
|
2736
2804
|
logger.newLine();
|
|
2737
2805
|
const choices = sortedSpecs.map((spec, idx) => {
|
|
2738
2806
|
const statusIcon = getStatusIcon(spec.status);
|
|
2739
|
-
const statusColor = getStatusColor(spec.status);
|
|
2740
2807
|
const recommendInfo = idx === 0 ? "[\u63A8\u8350\u4ECE\u8FD9\u5F00\u59CB] " : "";
|
|
2741
2808
|
const depInfo = spec.dependencies.length > 0 ? `[\u4F9D\u8D56: ${spec.dependencies.join(", ")}] ` : "";
|
|
2742
2809
|
return {
|
|
@@ -2871,13 +2938,13 @@ async function executeDevelopment(specFile, milestone, todo) {
|
|
|
2871
2938
|
logger.info(' 2. \u6309 "a" \u63A5\u53D7\u6240\u6709\u7F16\u8F91\u5EFA\u8BAE\uFF0C\u6216\u9010\u4E2A\u9009\u62E9');
|
|
2872
2939
|
logger.info(" 3. \u5B8C\u6210\u540E\u4F7F\u7528 Ctrl+D \u6216\u8F93\u5165 :exit \u9000\u51FA Claude");
|
|
2873
2940
|
logger.newLine();
|
|
2874
|
-
|
|
2941
|
+
await claudeAI.runTerminal(prompt, {
|
|
2875
2942
|
contextFiles: ["TECH_STACK.md", "CONVENTIONS.md", "AI_MEMORY.md", specFile]
|
|
2876
2943
|
});
|
|
2877
2944
|
logger.newLine();
|
|
2878
2945
|
logger.separator("\u2500", 60);
|
|
2879
2946
|
logger.newLine();
|
|
2880
|
-
await generateSessionLog(specFile, milestone, todo, taskDescription,
|
|
2947
|
+
await generateSessionLog(specFile, milestone, todo, taskDescription, "\u4EA4\u4E92\u5F0F\u4F1A\u8BDD\u5DF2\u5B8C\u6210");
|
|
2881
2948
|
await askAndUpdateSpecStatus(specFile, milestone, todo);
|
|
2882
2949
|
logger.header("\u5F00\u53D1\u4EFB\u52A1\u5B8C\u6210!");
|
|
2883
2950
|
logger.success("\u4F1A\u8BDD\u65E5\u5FD7\u5DF2\u4FDD\u5B58");
|
|
@@ -3003,9 +3070,6 @@ function getStatusIcon(status) {
|
|
|
3003
3070
|
return "\u25CB";
|
|
3004
3071
|
}
|
|
3005
3072
|
}
|
|
3006
|
-
function getStatusColor(status) {
|
|
3007
|
-
return status;
|
|
3008
|
-
}
|
|
3009
3073
|
function buildDevPrompt(specFile, milestone, todo, taskDescription) {
|
|
3010
3074
|
return `Role: Senior Fullstack Developer
|
|
3011
3075
|
|