package cmd import ( "encoding/json" "fmt" "os" "path/filepath" "strings" "github.com/aisim/kb-cli/internal/llm" "github.com/aisim/kb-cli/internal/review" "github.com/spf13/cobra" ) var reviewCmd = &cobra.Command{ Use: "review", Short: "审阅预览", Long: `# review - 审阅预览 kb-cli review <草稿目录> [--json] # 读取草稿和合并指示,使用 LLM 生成审阅预览`, Args: cobra.ExactArgs(1), RunE: runReview, } var reviewJSON bool func init() { reviewCmd.Flags().BoolVar(&reviewJSON, "json", false, "JSON 格式输出") rootCmd.AddCommand(reviewCmd) } func runReview(cmd *cobra.Command, args []string) error { draftDir := args[0] // 读取草稿和合并指示 preview, err := review.ReadDraftAndMerge(vaultPath, draftDir) if err != nil { return fmt.Errorf("读取草稿失败: %w", err) } // 如果有目标FAQ,用 LLM 生成3句话概要 if preview.TargetFAQ != nil && preview.TargetFAQ.Title != "" { targetPath := preview.TargetFAQ.Path if !filepath.IsAbs(targetPath) { targetPath = filepath.Join(vaultPath, targetPath) } targetContent, err := os.ReadFile(targetPath) if err == nil && len(targetContent) > 0 { llmClient := llm.NewClient() summary, err := llmClient.GenerateReviewPreview(preview.TargetFAQ.Title, string(targetContent)) if err != nil { fmt.Fprintf(os.Stderr, "警告: LLM 生成概要失败: %v\n", err) } else { preview.TargetFAQ.Summary = summary } } } // 输出 if reviewJSON { output, err := review.FormatPreviewJSON(preview) if err != nil { return fmt.Errorf("格式化输出失败: %w", err) } fmt.Println(output) } else { // 表格输出 fmt.Println(review.FormatPreviewTable(preview)) // 如果有草稿正文,也展示主要内容 expandedVault := expandHomePath(vaultPath) draftPath := filepath.Join(expandedVault, draftDir, "draft.md") draftContent, err := os.ReadFile(draftPath) if err == nil { content := extractMainContent(string(draftContent)) if content != "" { fmt.Println("### 草稿主要内容\n") fmt.Println(content) } } } return nil } // extractMainContent 从 draft.md 中提取正文(去掉 frontmatter) func extractMainContent(content string) string { if !strings.HasPrefix(content, "---") { return content } // 找到第二个 --- endIdx := strings.Index(content[3:], "\n---") if endIdx == -1 { return content } // 返回 frontmatter 之后的内容 mainContent := content[endIdx+6:] return strings.TrimSpace(mainContent) } // expandHomePath 展开路径中的 ~ func expandHomePath(path string) string { if strings.HasPrefix(path, "~/") { homeDir, _ := os.UserHomeDir() return filepath.Join(homeDir, path[2:]) } return path } // 保留 json 包引用 var _ = json.Marshal