ai_xiaopei
6 days ago 1196f409d86bc61e7596eb274840244a62ce84ba
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
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
}