package cmd
|
|
import (
|
"encoding/json"
|
"fmt"
|
"os"
|
|
"github.com/aisim/kb-cli/internal/draft"
|
"github.com/aisim/kb-cli/internal/index"
|
"github.com/spf13/cobra"
|
)
|
|
var draftCmd = &cobra.Command{
|
Use: "draft",
|
Short: "草稿管理",
|
Long: `# draft - 草稿管理
|
kb-cli draft create --type <类型> --title <标题> --content-file <文件> [--source <来源>] [--force] # 创建草稿(AI 提取 tags + 生成合并指示)
|
kb-cli draft list [--json] # 列出待审阅区的所有活跃草稿
|
kb-cli draft convert [目录名] # 转换老格式单文件草稿到新格式(NNN-标题/draft.md + merge.md)`,
|
}
|
|
var draftCreateCmd = &cobra.Command{
|
Use: "create",
|
Short: "创建草稿",
|
Long: `# draft create - 创建草稿
|
kb-cli draft create --type <类型> --title <标题> --content-file <文件> [--source <来源>] [--force] # 创建草稿,自动提取 tags 并生成合并指示`,
|
RunE: runDraftCreate,
|
}
|
|
var draftListCmd = &cobra.Command{
|
Use: "list",
|
Short: "列出所有活跃草稿",
|
Long: `# draft list - 列出所有活跃草稿
|
kb-cli draft list [--json] # 列出待审阅区的所有草稿(目录结构 + 老格式单文件)`,
|
RunE: runDraftList,
|
}
|
|
var draftConvertCmd = &cobra.Command{
|
Use: "convert [目录名]",
|
Short: "转换老格式草稿到新格式",
|
Long: `# draft convert - 转换老格式草稿到新格式
|
kb-cli draft convert # 扫描所有待审阅子目录,转换老格式单文件
|
kb-cli draft convert TAPD提取 # 只转换指定子目录`,
|
RunE: runDraftConvert,
|
}
|
|
var tagsCmd = &cobra.Command{
|
Use: "tags",
|
Short: "标签管理",
|
Long: `kb-cli tags rebuild # 批量重建现有文档的 tags`,
|
}
|
|
var tagsRebuildCmd = &cobra.Command{
|
Use: "rebuild",
|
Short: "批量重建 tags",
|
Long: `# tags rebuild - 批量重建 tags
|
kb-cli tags rebuild [--vault <路径>] [--limit <数量>] [--dry-run] # 使用 LLM 重新提取现有文档的 tags`,
|
RunE: runTagsRebuild,
|
}
|
|
var (
|
draftType string
|
draftTitle string
|
draftContentFile string
|
draftSource string
|
draftForce bool
|
draftJSON bool
|
tagsLimit int
|
tagsDryRun bool
|
convertTarget string
|
)
|
|
func init() {
|
// draft create 命令
|
draftCreateCmd.Flags().StringVar(&draftType, "type", "", "草稿类型(售后/产品/运营/行业/TAPD)")
|
draftCreateCmd.Flags().StringVar(&draftTitle, "title", "", "草稿标题")
|
draftCreateCmd.Flags().StringVar(&draftContentFile, "content-file", "", "草稿内容文件路径")
|
draftCreateCmd.Flags().StringVar(&draftSource, "source", "", "来源标识")
|
draftCreateCmd.Flags().BoolVar(&draftForce, "force", false, "强制创建(跳过重复检查)")
|
draftCreateCmd.MarkFlagRequired("type")
|
draftCreateCmd.MarkFlagRequired("title")
|
draftCreateCmd.MarkFlagRequired("content-file")
|
|
// draft list 命令
|
draftListCmd.Flags().BoolVar(&draftJSON, "json", false, "JSON 格式输出")
|
|
// draft convert 命令(无额外参数,target 从 args 读取)
|
|
draftCmd.AddCommand(draftCreateCmd)
|
draftCmd.AddCommand(draftListCmd)
|
draftCmd.AddCommand(draftConvertCmd)
|
|
// tags rebuild 命令
|
tagsRebuildCmd.Flags().IntVar(&tagsLimit, "limit", 0, "限制处理文档数量(0=全部)")
|
tagsRebuildCmd.Flags().BoolVar(&tagsDryRun, "dry-run", false, "仅预览,不实际更新")
|
|
tagsCmd.AddCommand(tagsRebuildCmd)
|
|
rootCmd.AddCommand(draftCmd)
|
rootCmd.AddCommand(tagsCmd)
|
}
|
|
func runDraftCreate(cmd *cobra.Command, args []string) error {
|
// 读取内容文件
|
content, err := os.ReadFile(draftContentFile)
|
if err != nil {
|
return fmt.Errorf("读取内容文件失败: %w", err)
|
}
|
|
if len(content) == 0 {
|
return fmt.Errorf("内容文件为空")
|
}
|
|
// 打开索引
|
store, err := index.Open(dbPath)
|
if err != nil {
|
return fmt.Errorf("打开索引失败: %w", err)
|
}
|
defer store.Close()
|
|
// 创建草稿录入器
|
intake := draft.NewIntake(vaultPath, store)
|
|
// 创建草稿
|
if err := intake.CreateDraft(draftType, draftTitle, string(content), draftSource, draftForce); err != nil {
|
return fmt.Errorf("创建草稿失败: %w", err)
|
}
|
|
return nil
|
}
|
|
func runTagsRebuild(cmd *cobra.Command, args []string) error {
|
// 打开索引
|
store, err := index.Open(dbPath)
|
if err != nil {
|
return fmt.Errorf("打开索引失败: %w", err)
|
}
|
defer store.Close()
|
|
// 创建草稿录入器
|
intake := draft.NewIntake(vaultPath, store)
|
|
// 重建 tags
|
if err := intake.RebuildTags(tagsLimit, tagsDryRun); err != nil {
|
return fmt.Errorf("重建 tags 失败: %w", err)
|
}
|
|
return nil
|
}
|
|
// runDraftList 列出所有活跃草稿
|
func runDraftList(cmd *cobra.Command, args []string) error {
|
// 创建草稿录入器
|
intake := draft.NewIntake(vaultPath, nil)
|
|
// 列出草稿
|
drafts, err := intake.ListDrafts()
|
if err != nil {
|
return fmt.Errorf("列出草稿失败: %w", err)
|
}
|
|
if draftJSON {
|
// JSON 输出
|
output := struct {
|
Drafts []draft.DraftInfo `json:"drafts"`
|
Total int `json:"total"`
|
}{
|
Drafts: drafts,
|
Total: len(drafts),
|
}
|
encoder := json.NewEncoder(os.Stdout)
|
encoder.SetIndent("", " ")
|
return encoder.Encode(output)
|
}
|
|
// 表格输出
|
if len(drafts) == 0 {
|
fmt.Println("没有活跃草稿")
|
return nil
|
}
|
|
fmt.Printf("找到 %d 个活跃草稿:\n\n", len(drafts))
|
for _, d := range drafts {
|
mergeStatus := "❌"
|
if d.HasMergeHint {
|
mergeStatus = "✅"
|
}
|
fmt.Printf("• %s\n", d.Title)
|
fmt.Printf(" 路径: %s\n", d.Filepath)
|
fmt.Printf(" 类型: %s | 状态: %s | 合并指示: %s\n", d.Type, d.Status, mergeStatus)
|
fmt.Printf(" 创建: %s\n\n", d.Created)
|
}
|
|
return nil
|
}
|
|
// runDraftConvert 转换老格式草稿到新格式
|
func runDraftConvert(cmd *cobra.Command, args []string) error {
|
// 可选参数:指定子目录
|
target := ""
|
if len(args) > 0 {
|
target = args[0]
|
}
|
|
// 创建草稿录入器
|
intake := draft.NewIntake(vaultPath, nil)
|
|
// 转换草稿
|
converted, err := intake.ConvertOldDrafts(target)
|
if err != nil {
|
return fmt.Errorf("转换草稿失败: %w", err)
|
}
|
|
fmt.Printf("\n✅ 成功转换 %d 个草稿\n", converted)
|
return nil
|
}
|