feat(cli): CLI支持成熟度阶段执行与业务参谋锦囊可视化
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
committed by
weichen
co-authored by
Cursor
parent
6ea0043b49
commit
288dbeffdf
+127
-7
@@ -14,6 +14,7 @@ import (
|
||||
)
|
||||
|
||||
func main() {
|
||||
stageFlag := flag.String("stage", "", "执行成熟度阶段 (可选: V0.1_triage, V0.5_feasibility, V1.0_quoting)")
|
||||
taskKey := flag.String("task", "intake", "任务标识 (intake, research, cdo_quote 等)")
|
||||
backend := flag.String("backend", "", "智能体后端 (留空则读取 .env 配置,可选: stub, pi-agent, cursor-cli)")
|
||||
project := flag.String("project", "医疗器械研发演示项目", "项目名称")
|
||||
@@ -22,10 +23,12 @@ func main() {
|
||||
use := flag.String("use", "临床诊疗", "预期用途")
|
||||
market := flag.String("market", "国内", "目标市场")
|
||||
materials := flag.String("materials", "", "输入补充材料内容")
|
||||
materialsDir := flag.String("materials-dir", "", "输入材料文件夹路径 (批量载入目录下所有文件)")
|
||||
envPath := flag.String("env", ".env", "环境配置文件路径")
|
||||
inspectDir := flag.String("inspect", "", "审查并查看指定工作区状态目录")
|
||||
autoRepair := flag.Bool("auto-repair", true, "启用质量门禁自动闭环修正")
|
||||
listTasks := flag.Bool("list-tasks", false, "列出所有受支持的商务对接任务清单")
|
||||
listStages := flag.Bool("list-stages", false, "列出所有受支持的商务成熟度阶段清单")
|
||||
addStep := flag.String("add-step", "", "动态向工作区追加任务步骤 (格式: role:title,配合 -inspect)")
|
||||
flag.Parse()
|
||||
|
||||
@@ -37,6 +40,17 @@ func main() {
|
||||
|
||||
svc := bizdev.NewService(cfg)
|
||||
|
||||
// 0. 列出支持的阶段
|
||||
if *listStages {
|
||||
fmt.Println("=== 商务对接成熟度螺旋收敛阶段清单 ===")
|
||||
for _, st := range svc.ListStages() {
|
||||
fmt.Printf("- %-18s: %s\n", st.Stage, st.Title)
|
||||
fmt.Printf(" 说明: %s\n", st.Description)
|
||||
fmt.Printf(" 包含任务: %v\n", st.Tasks)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// 1. 列出支持的任务
|
||||
if *listTasks {
|
||||
fmt.Println("=== 商务对接小队支持的独立任务清单 ===")
|
||||
@@ -55,7 +69,7 @@ func main() {
|
||||
return
|
||||
}
|
||||
|
||||
// 3. 执行常规任务
|
||||
// 3. 执行阶段或常规任务
|
||||
activeBackend := cfg.AgentBackend
|
||||
if *backend != "" {
|
||||
activeBackend = *backend
|
||||
@@ -63,13 +77,40 @@ func main() {
|
||||
}
|
||||
|
||||
var materialsList []bizdev.MaterialInput
|
||||
if *materials != "" {
|
||||
materialsList = []bizdev.MaterialInput{
|
||||
{
|
||||
Filename: "user_input.md",
|
||||
Content: []byte(*materials),
|
||||
},
|
||||
if *materialsDir != "" {
|
||||
entries, err := os.ReadDir(*materialsDir)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "读取材料目录失败 %s: %v\n", *materialsDir, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
for _, entry := range entries {
|
||||
if entry.IsDir() || strings.HasPrefix(entry.Name(), ".") {
|
||||
continue
|
||||
}
|
||||
fPath := filepath.Join(*materialsDir, entry.Name())
|
||||
content, err := os.ReadFile(fPath)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "读取材料文件失败 %s: %v\n", fPath, err)
|
||||
continue
|
||||
}
|
||||
materialsList = append(materialsList, bizdev.MaterialInput{
|
||||
Filename: entry.Name(),
|
||||
Content: content,
|
||||
})
|
||||
fmt.Printf("已载入输入材料: %s (%d 字节)\n", entry.Name(), len(content))
|
||||
}
|
||||
}
|
||||
if *materials != "" {
|
||||
materialsList = append(materialsList, bizdev.MaterialInput{
|
||||
Filename: "user_input.md",
|
||||
Content: []byte(*materials),
|
||||
})
|
||||
}
|
||||
|
||||
// 如果指定了 -stage,执行阶段编排
|
||||
if *stageFlag != "" {
|
||||
handleStageRun(context.Background(), svc, *stageFlag, *project, *customer, *device, *use, *market, activeBackend, *autoRepair, materialsList)
|
||||
return
|
||||
}
|
||||
|
||||
res, err := svc.ExecuteTask(context.Background(), bizdev.TaskRunRequest{
|
||||
@@ -129,6 +170,85 @@ func main() {
|
||||
fmt.Printf(" - %s (%d bytes) -> %s\n", a.Filename, len(a.Data), p)
|
||||
}
|
||||
}
|
||||
|
||||
// 打印 B 轨业务参谋锦囊
|
||||
if res.AdvisorBrief != nil {
|
||||
printAdvisorBrief(res.AdvisorBrief)
|
||||
}
|
||||
}
|
||||
|
||||
func handleStageRun(
|
||||
ctx context.Context,
|
||||
svc bizdev.BizdevService,
|
||||
stageName string,
|
||||
project, customer, device, use, market, backend string,
|
||||
autoRepair bool,
|
||||
materials []bizdev.MaterialInput,
|
||||
) {
|
||||
fmt.Printf("\n>>> 启动商务对接成熟度阶段: [%s] <<<\n", stageName)
|
||||
stageRes, err := svc.ExecuteStage(ctx, bizdev.StageRunRequest{
|
||||
Stage: bizdev.MaturityStage(stageName),
|
||||
ProjectName: project,
|
||||
CustomerName: customer,
|
||||
DeviceType: device,
|
||||
IntendedUse: use,
|
||||
TargetMarket: market,
|
||||
Backend: backend,
|
||||
AutoRepair: autoRepair,
|
||||
Materials: materials,
|
||||
})
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "阶段执行失败: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
fmt.Printf("\n=== 阶段执行成功: %s ===\n", stageRes.StageTitle)
|
||||
fmt.Printf("总耗时: %v\n", stageRes.Duration)
|
||||
fmt.Printf("阶段工作区根目录: %s\n", stageRes.WorkDir)
|
||||
fmt.Printf("包含交付任务数: %d\n", len(stageRes.SubArtifacts))
|
||||
for idx, sub := range stageRes.SubArtifacts {
|
||||
fmt.Printf(" [%d] %-18s: %s -> %s\n", idx+1, sub.TaskKey, sub.ArtifactTitle, filepath.Join(sub.WorkDir, "output.md"))
|
||||
}
|
||||
|
||||
// 优先呈现 B 轨:贴身业务参谋锦囊
|
||||
if stageRes.AdvisorBrief != nil {
|
||||
printAdvisorBrief(stageRes.AdvisorBrief)
|
||||
}
|
||||
}
|
||||
|
||||
func printAdvisorBrief(brief *bizdev.AdvisorBriefing) {
|
||||
fmt.Println("\n==================================================================")
|
||||
fmt.Println(" 💡【B轨:贴身业务参谋决策锦囊】 ")
|
||||
fmt.Println("==================================================================")
|
||||
fmt.Printf("【成熟度诊断】 等级: %s | 完备度评分: %d / 100\n", brief.MaturityLevel, brief.MaturityScore)
|
||||
fmt.Printf("【进展研判】 %s\n", brief.MaturityHeadline)
|
||||
|
||||
if len(brief.BaselineAssumptions) > 0 {
|
||||
fmt.Println("\n【基准预设假设(替代填空,为您先行兜底)】:")
|
||||
for _, a := range brief.BaselineAssumptions {
|
||||
fmt.Printf(" • %s\n", a)
|
||||
}
|
||||
}
|
||||
|
||||
if len(brief.PreemptiveRisks) > 0 {
|
||||
fmt.Println("\n【预见性风险雷达(提前预警后续法规/商务卡点)】:")
|
||||
for _, r := range brief.PreemptiveRisks {
|
||||
fmt.Printf(" ⚠️ %s\n", r)
|
||||
}
|
||||
}
|
||||
|
||||
if len(brief.NextBestActions) > 0 {
|
||||
fmt.Println("\n【建议下一步行动(提供选项供您掌舵决策)】:")
|
||||
for idx, act := range brief.NextBestActions {
|
||||
fmt.Printf(" [%d] %s\n", idx+1, act)
|
||||
}
|
||||
}
|
||||
|
||||
if brief.CommunicationKit != "" {
|
||||
fmt.Println("\n【对外赋能:建议沟通话术(可直接复制使用)】:")
|
||||
fmt.Println(brief.CommunicationKit)
|
||||
}
|
||||
fmt.Println("==================================================================")
|
||||
}
|
||||
|
||||
func handleInspect(ctx context.Context, svc bizdev.BizdevService, workDir string, addStepFlag string, backend string) {
|
||||
|
||||
@@ -68,6 +68,9 @@ func TestBizdevCLIExecuteAndInspect(t *testing.T) {
|
||||
if !strings.Contains(outStr, "质量门禁评分") {
|
||||
t.Fatalf("cli output missing quality gate score: %s", outStr)
|
||||
}
|
||||
if !strings.Contains(outStr, "【B轨:贴身业务参谋决策锦囊】") {
|
||||
t.Fatalf("cli output missing B-Track advisor brief: %s", outStr)
|
||||
}
|
||||
|
||||
// 从输出提取工作区路径
|
||||
var workDir string
|
||||
@@ -102,3 +105,47 @@ func TestBizdevCLIExecuteAndInspect(t *testing.T) {
|
||||
t.Fatalf("expected dynamic step success: %s", inspectStr)
|
||||
}
|
||||
}
|
||||
|
||||
func TestBizdevCLIStageExecution(t *testing.T) {
|
||||
tmpDir := t.TempDir()
|
||||
envContent := "ENV=dev\nDATA_DIR=" + tmpDir + "\nAGENT_BACKEND=stub\n"
|
||||
envPath := filepath.Join(tmpDir, ".env")
|
||||
_ = os.WriteFile(envPath, []byte(envContent), 0o644)
|
||||
|
||||
// 1. 验证 -list-stages 输出
|
||||
listCmd := exec.Command("go", "run", "./cmd/bizdev-cli", "-list-stages", "-env", envPath)
|
||||
listCmd.Dir = moduleRoot(t)
|
||||
listOut, err := listCmd.CombinedOutput()
|
||||
if err != nil {
|
||||
t.Fatalf("cli -list-stages failed: %v, out: %s", err, string(listOut))
|
||||
}
|
||||
if !strings.Contains(string(listOut), "V0.1_triage") || !strings.Contains(string(listOut), "V1.0_quoting") {
|
||||
t.Fatalf("missing stages in list: %s", string(listOut))
|
||||
}
|
||||
|
||||
// 2. 运行 Stage 1 (V0.1_triage)
|
||||
stageCmd := exec.Command("go", "run", "./cmd/bizdev-cli",
|
||||
"-env", envPath,
|
||||
"-stage", "V0.1_triage",
|
||||
"-backend", "stub",
|
||||
"-project", "腹腔镜手术机器人系统",
|
||||
"-customer", "水木医用机器人中心",
|
||||
"-device", "三类手术机器人",
|
||||
)
|
||||
stageCmd.Dir = moduleRoot(t)
|
||||
stageOut, err := stageCmd.CombinedOutput()
|
||||
if err != nil {
|
||||
t.Fatalf("cli -stage run failed: %v, out: %s", err, string(stageOut))
|
||||
}
|
||||
|
||||
outStr := string(stageOut)
|
||||
if !strings.Contains(outStr, "阶段执行成功: V0.1 闪电摸底与盲区探测") {
|
||||
t.Errorf("missing stage success headline: %s", outStr)
|
||||
}
|
||||
if !strings.Contains(outStr, "【B轨:贴身业务参谋决策锦囊】") {
|
||||
t.Errorf("missing advisor brief section: %s", outStr)
|
||||
}
|
||||
if !strings.Contains(outStr, "水木医用机器人中心") {
|
||||
t.Errorf("missing customer name in communication kit: %s", outStr)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user