From 3d32162df6f0af1b89004140fd3191ba8753fb1d Mon Sep 17 00:00:00 2001
From: ai_xiaopei <xiaopei@aisim.cn>
Date: Sun, 26 Jul 2026 08:14:44 +0800
Subject: [PATCH] docs: add kb-cli enhancement implementation plan
---
docs/superpowers/plans/2026-07-26-kb-cli-enhancement.md | 1047 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 1,047 insertions(+), 0 deletions(-)
diff --git a/docs/superpowers/plans/2026-07-26-kb-cli-enhancement.md b/docs/superpowers/plans/2026-07-26-kb-cli-enhancement.md
new file mode 100644
index 0000000..a4f851a
--- /dev/null
+++ b/docs/superpowers/plans/2026-07-26-kb-cli-enhancement.md
@@ -0,0 +1,1047 @@
+# kb-cli 增强功能实现计划
+
+> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
+
+**Goal:** 为 kb-cli 添加 `--with-content` 和 `--with-links` 功能,并调整 kb-search 技能优先使用 kb-cli
+
+**Architecture:** 在现有 SearchResult 结构体上添加可选字段 Content 和 Links,通过 SearchOptions 控制是否填充这些字段。修改输出格式化器支持新字段的展示。
+
+**Tech Stack:** Go 1.22, SQLite FTS5, Cobra CLI
+
+## Global Constraints
+
+- 所有新功能必须有单元测试
+- JSON 输出使用 `omitempty` 标签
+- 保持向后兼容(不破坏现有命令)
+- 性能目标:< 100ms(包含内容和链接获取)
+
+---
+
+## Task 1: 添加 GetNodeLinks 数据库方法
+
+**Files:**
+- Modify: `internal/index/sqlite.go:120-140`
+- Test: `internal/index/sqlite_test.go`
+
+**Interfaces:**
+- Consumes: `Store.db` (SQLite 连接)
+- Produces: `GetNodeLinks(nodeID int64) ([]string, error)` 方法
+
+- [ ] **Step 1: 编写失败测试**
+
+在 `internal/index/sqlite_test.go` 添加:
+
+```go
+func TestGetNodeLinks(t *testing.T) {
+ store := setupTestStore(t)
+ defer store.Close()
+
+ // 插入测试节点
+ node1 := &graph.Node{
+ Path: "FAQ/001-测试.md",
+ Title: "测试文档",
+ Section: "FAQ",
+ Content: "# 测试\n\n内容",
+ }
+ node2 := &graph.Node{
+ Path: "FAQ/002-相关.md",
+ Title: "相关文档",
+ Section: "FAQ",
+ Content: "# 相关\n\n内容",
+ }
+
+ id1, err := store.InsertNode(node1)
+ require.NoError(t, err)
+
+ id2, err := store.InsertNode(node2)
+ require.NoError(t, err)
+
+ // 插入 wikilink 边
+ edge := &graph.Edge{
+ FromNode: id1,
+ ToNode: id2,
+ Relation: "wikilink",
+ Label: "相关文档",
+ }
+ err = store.InsertEdge(edge)
+ require.NoError(t, err)
+
+ // 测试获取链接
+ links, err := store.GetNodeLinks(id1)
+ require.NoError(t, err)
+ assert.Len(t, links, 1)
+ assert.Equal(t, "FAQ/002-相关.md", links[0])
+
+ // 测试无链接的节点
+ links, err = store.GetNodeLinks(id2)
+ require.NoError(t, err)
+ assert.Len(t, links, 0)
+}
+```
+
+- [ ] **Step 2: 运行测试确认失败**
+
+```bash
+cd ~/workspace/kb-cli
+go test ./internal/index -run TestGetNodeLinks -v
+```
+
+Expected: FAIL - "store.GetNodeLinks undefined"
+
+- [ ] **Step 3: 实现 GetNodeLinks 方法**
+
+在 `internal/index/sqlite.go` 的 `GetNodeContent` 方法后添加:
+
+```go
+// GetNodeLinks 获取节点的关联链接(wikilink 目标)
+func (s *Store) GetNodeLinks(nodeID int64) ([]string, error) {
+ query := `
+ SELECT n.path
+ FROM edges e
+ JOIN nodes n ON n.id = e.to_node
+ WHERE e.from_node = ? AND e.relation = 'wikilink'
+ `
+ rows, err := s.db.Query(query, nodeID)
+ if err != nil {
+ return nil, fmt.Errorf("查询链接失败: %w", err)
+ }
+ defer rows.Close()
+
+ var links []string
+ for rows.Next() {
+ var path string
+ if err := rows.Scan(&path); err != nil {
+ return nil, fmt.Errorf("扫描链接失败: %w", err)
+ }
+ links = append(links, path)
+ }
+
+ if err := rows.Err(); err != nil {
+ return nil, fmt.Errorf("遍历链接失败: %w", err)
+ }
+
+ return links, nil
+}
+```
+
+- [ ] **Step 4: 运行测试确认通过**
+
+```bash
+cd ~/workspace/kb-cli
+go test ./internal/index -run TestGetNodeLinks -v
+```
+
+Expected: PASS
+
+- [ ] **Step 5: 提交**
+
+```bash
+cd ~/workspace/kb-cli
+git add internal/index/sqlite.go internal/index/sqlite_test.go
+git commit -m "feat: add GetNodeLinks method for wikilink retrieval"
+```
+
+---
+
+## Task 2: 扩展 SearchResult 结构体
+
+**Files:**
+- Modify: `internal/search/engine.go:15-25`
+- Test: `internal/search/engine_test.go`
+
+**Interfaces:**
+- Consumes: 无
+- Produces: 扩展的 `SearchResult` 结构体(Content, Links 字段)
+
+- [ ] **Step 1: 编写失败测试**
+
+在 `internal/search/engine_test.go` 添加:
+
+```go
+func TestSearchResultJSON_OmitEmpty(t *testing.T) {
+ // 测试无内容和链接时,JSON 不包含这些字段
+ result := SearchResult{
+ ID: 1,
+ Path: "test.md",
+ Title: "测试",
+ Section: "FAQ",
+ Score: 10,
+ }
+
+ jsonBytes, err := json.Marshal(result)
+ require.NoError(t, err)
+
+ jsonStr := string(jsonBytes)
+ assert.NotContains(t, jsonStr, "content")
+ assert.NotContains(t, jsonStr, "links")
+
+ // 测试有内容时,JSON 包含 content 字段
+ result.Content = "# 测试内容"
+ jsonBytes, err = json.Marshal(result)
+ require.NoError(t, err)
+
+ jsonStr = string(jsonBytes)
+ assert.Contains(t, jsonStr, `"content":"# 测试内容"`)
+
+ // 测试有链接时,JSON 包含 links 字段
+ result.Links = []string{"related.md"}
+ jsonBytes, err = json.Marshal(result)
+ require.NoError(t, err)
+
+ jsonStr = string(jsonBytes)
+ assert.Contains(t, jsonStr, `"links":["related.md"]`)
+}
+```
+
+- [ ] **Step 2: 运行测试确认失败**
+
+```bash
+cd ~/workspace/kb-cli
+go test ./internal/search -run TestSearchResultJSON_OmitEmpty -v
+```
+
+Expected: FAIL - "Content undefined" 或测试失败(因为字段不存在)
+
+- [ ] **Step 3: 扩展 SearchResult 结构体**
+
+修改 `internal/search/engine.go` 中的 `SearchResult` 结构体:
+
+```go
+// SearchResult 搜索结果
+type SearchResult struct {
+ ID int64 `json:"id"`
+ Path string `json:"path"`
+ Title string `json:"title"`
+ Section string `json:"section"`
+ Score int `json:"score"`
+
+ // 可选字段(omitempty)
+ Content string `json:"content,omitempty"` // --with-content 时填充
+ Links []string `json:"links,omitempty"` // --with-links 时填充
+}
+```
+
+- [ ] **Step 4: 运行测试确认通过**
+
+```bash
+cd ~/workspace/kb-cli
+go test ./internal/search -run TestSearchResultJSON_OmitEmpty -v
+```
+
+Expected: PASS
+
+- [ ] **Step 5: 提交**
+
+```bash
+cd ~/workspace/kb-cli
+git add internal/search/engine.go internal/search/engine_test.go
+git commit -m "feat: extend SearchResult with Content and Links fields"
+```
+
+---
+
+## Task 3: 扩展 SearchOptions 并修改 Search 函数
+
+**Files:**
+- Modify: `internal/search/engine.go:30-40, 80-120`
+- Test: `internal/search/engine_test.go`
+
+**Interfaces:**
+- Consumes: `Store.GetNodeContent`, `Store.GetNodeLinks` (Task 1)
+- Produces: 扩展的 `SearchOptions` 和增强的 `Search` 函数
+
+- [ ] **Step 1: 编写失败测试**
+
+在 `internal/search/engine_test.go` 添加:
+
+```go
+func TestSearch_WithContent(t *testing.T) {
+ store := setupTestStore(t)
+ defer store.Close()
+
+ // 插入测试节点
+ node := &graph.Node{
+ Path: "FAQ/001-测试.md",
+ Title: "测试文档",
+ Section: "FAQ",
+ Content: "# 测试\n\n这是测试内容",
+ }
+ id, err := store.InsertNode(node)
+ require.NoError(t, err)
+
+ err = store.CreateFTS()
+ require.NoError(t, err)
+ err = store.PopulateFTS()
+ require.NoError(t, err)
+
+ // 测试带内容的搜索
+ opts := SearchOptions{
+ TopN: 5,
+ WithContent: true,
+ }
+ results, err := Search(store, []string{"测试"}, opts)
+ require.NoError(t, err)
+ require.Len(t, results, 1)
+ assert.Equal(t, id, results[0].ID)
+ assert.Equal(t, "# 测试\n\n这是测试内容", results[0].Content)
+
+ // 测试不带内容的搜索
+ opts.WithContent = false
+ results, err = Search(store, []string{"测试"}, opts)
+ require.NoError(t, err)
+ require.Len(t, results, 1)
+ assert.Empty(t, results[0].Content)
+}
+
+func TestSearch_WithLinks(t *testing.T) {
+ store := setupTestStore(t)
+ defer store.Close()
+
+ // 插入两个节点
+ node1 := &graph.Node{
+ Path: "FAQ/001-测试.md",
+ Title: "测试文档",
+ Section: "FAQ",
+ Content: "# 测试",
+ }
+ node2 := &graph.Node{
+ Path: "FAQ/002-相关.md",
+ Title: "相关文档",
+ Section: "FAQ",
+ Content: "# 相关",
+ }
+
+ id1, err := store.InsertNode(node1)
+ require.NoError(t, err)
+ id2, err := store.InsertNode(node2)
+ require.NoError(t, err)
+
+ // 插入 wikilink 边
+ edge := &graph.Edge{
+ FromNode: id1,
+ ToNode: id2,
+ Relation: "wikilink",
+ Label: "相关文档",
+ }
+ err = store.InsertEdge(edge)
+ require.NoError(t, err)
+
+ err = store.CreateFTS()
+ require.NoError(t, err)
+ err = store.PopulateFTS()
+ require.NoError(t, err)
+
+ // 测试带链接的搜索
+ opts := SearchOptions{
+ TopN: 5,
+ WithLinks: true,
+ }
+ results, err := Search(store, []string{"测试"}, opts)
+ require.NoError(t, err)
+ require.Len(t, results, 1)
+ assert.Equal(t, id1, results[0].ID)
+ assert.Len(t, results[0].Links, 1)
+ assert.Equal(t, "FAQ/002-相关.md", results[0].Links[0])
+
+ // 测试不带链接的搜索
+ opts.WithLinks = false
+ results, err = Search(store, []string{"测试"}, opts)
+ require.NoError(t, err)
+ require.Len(t, results, 1)
+ assert.Empty(t, results[0].Links)
+}
+```
+
+- [ ] **Step 2: 运行测试确认失败**
+
+```bash
+cd ~/workspace/kb-cli
+go test ./internal/search -run "TestSearch_WithContent|TestSearch_WithLinks" -v
+```
+
+Expected: FAIL - "WithContent undefined" 或测试失败
+
+- [ ] **Step 3: 扩展 SearchOptions 并修改 Search 函数**
+
+修改 `internal/search/engine.go`:
+
+```go
+// SearchOptions 搜索选项
+type SearchOptions struct {
+ Expanded []string
+ Symptom []string
+ TopN int
+ WithContent bool // 新增:是否返回内容
+ WithLinks bool // 新增:是否返回链接
+}
+
+// Search 执行搜索
+func Search(store *index.Store, keywords []string, opts SearchOptions) ([]SearchResult, error) {
+ // 合并所有关键词
+ allKeywords := append(keywords, opts.Expanded...)
+ allKeywords = append(allKeywords, opts.Symptom...)
+
+ // FTS5 搜索
+ ftsResults, err := store.FTSSearch(allKeywords, 100)
+ if err != nil {
+ return nil, err
+ }
+
+ // 评分
+ scoreMap := make(map[int64]int)
+ for _, r := range ftsResults {
+ score := 0
+ for _, kw := range keywords {
+ score += scoreResult(r, kw, ScoreNormal)
+ }
+ for _, kw := range opts.Expanded {
+ score += scoreResult(r, kw, ScoreExpanded)
+ }
+ for _, kw := range opts.Symptom {
+ score += scoreResult(r, kw, ScoreSymptom)
+ }
+ scoreMap[r.ID] = score
+ }
+
+ // 转换为结果列表
+ var results []SearchResult
+ for _, r := range ftsResults {
+ results = append(results, SearchResult{
+ ID: r.ID,
+ Path: r.Path,
+ Title: r.Title,
+ Section: r.Section,
+ Score: scoreMap[r.ID],
+ })
+ }
+
+ // 按分数排序
+ sort.Slice(results, func(i, j int) bool {
+ return results[i].Score > results[j].Score
+ })
+
+ // 限制返回数量
+ if opts.TopN > 0 && len(results) > opts.TopN {
+ results = results[:opts.TopN]
+ }
+
+ // 增强结果(新增)
+ for i := range results {
+ if opts.WithContent {
+ content, _, _, err := store.GetNodeContent(results[i].ID)
+ if err == nil {
+ results[i].Content = content
+ }
+ }
+ if opts.WithLinks {
+ links, err := store.GetNodeLinks(results[i].ID)
+ if err == nil {
+ results[i].Links = links
+ }
+ }
+ }
+
+ return results, nil
+}
+```
+
+- [ ] **Step 4: 运行测试确认通过**
+
+```bash
+cd ~/workspace/kb-cli
+go test ./internal/search -run "TestSearch_WithContent|TestSearch_WithLinks" -v
+```
+
+Expected: PASS
+
+- [ ] **Step 5: 运行所有测试确认无回归**
+
+```bash
+cd ~/workspace/kb-cli
+go test ./... -v
+```
+
+Expected: 所有测试通过
+
+- [ ] **Step 6: 提交**
+
+```bash
+cd ~/workspace/kb-cli
+git add internal/search/engine.go internal/search/engine_test.go
+git commit -m "feat: add WithContent and WithLinks options to Search"
+```
+
+---
+
+## Task 4: 添加 CLI flags 并传递给 SearchOptions
+
+**Files:**
+- Modify: `cmd/search.go:20-50`
+
+**Interfaces:**
+- Consumes: `SearchOptions.WithContent`, `SearchOptions.WithLinks` (Task 3)
+- Produces: 新增 `--with-content` 和 `--with-links` flags
+
+- [ ] **Step 1: 添加 flag 变量**
+
+在 `cmd/search.go` 的变量声明区域添加:
+
+```go
+var (
+ withContent bool
+ withLinks bool
+)
+```
+
+- [ ] **Step 2: 注册 flags**
+
+在 `init()` 函数中添加:
+
+```go
+func init() {
+ searchCmd.Flags().StringSliceVar(&expanded, "expanded", nil, "扩展词(提升相关实体权重)")
+ searchCmd.Flags().StringSliceVar(&symptom, "symptom", nil, "症状词(针对具体症状)")
+ searchCmd.Flags().IntVar(&topN, "top", 10, "返回前 N 条结果")
+ searchCmd.Flags().BoolVar(&jsonOut, "json", false, "JSON 格式输出")
+
+ // 新增 flags
+ searchCmd.Flags().BoolVar(&withContent, "with-content", false, "返回完整文件内容")
+ searchCmd.Flags().BoolVar(&withLinks, "with-links", false, "显示关联文档链接")
+}
+```
+
+- [ ] **Step 3: 传递 flags 到 SearchOptions**
+
+修改 `runSearch` 函数:
+
+```go
+func runSearch(cmd *cobra.Command, args []string) error {
+ // ... 现有代码 ...
+
+ opts := search.SearchOptions{
+ Expanded: expanded,
+ Symptom: symptom,
+ TopN: topN,
+ WithContent: withContent,
+ WithLinks: withLinks,
+ }
+
+ results, err := search.Search(store, args, opts)
+ // ... 现有代码 ...
+}
+```
+
+- [ ] **Step 4: 构建并测试 help 输出**
+
+```bash
+cd ~/workspace/kb-cli
+go build -o bin/kb .
+./bin/kb search --help
+```
+
+Expected: 显示 `--with-content` 和 `--with-links` flags
+
+- [ ] **Step 5: 提交**
+
+```bash
+cd ~/workspace/kb-cli
+git add cmd/search.go
+git commit -m "feat: add --with-content and --with-links CLI flags"
+```
+
+---
+
+## Task 5: 修改输出格式化器支持新字段
+
+**Files:**
+- Modify: `internal/output/formatter.go`
+- Test: `internal/output/formatter_test.go`
+
+**Interfaces:**
+- Consumes: `SearchResult.Content`, `SearchResult.Links` (Task 2)
+- Produces: 增强的 `FormatTable` 和 `FormatJSON` 方法
+
+- [ ] **Step 1: 编写失败测试**
+
+在 `internal/output/formatter_test.go` 添加:
+
+```go
+func TestFormatTable_WithContent(t *testing.T) {
+ results := []search.SearchResult{
+ {
+ ID: 1,
+ Path: "FAQ/001-测试.md",
+ Title: "测试文档",
+ Section: "FAQ",
+ Score: 10,
+ Content: "# 测试\n\n这是内容",
+ },
+ }
+
+ output := FormatTable(results)
+ assert.Contains(t, output, "FAQ/001-测试.md")
+ assert.Contains(t, output, "--- 内容 ---")
+ assert.Contains(t, output, "# 测试")
+}
+
+func TestFormatTable_WithLinks(t *testing.T) {
+ results := []search.SearchResult{
+ {
+ ID: 1,
+ Path: "FAQ/001-测试.md",
+ Title: "测试文档",
+ Section: "FAQ",
+ Score: 10,
+ Links: []string{"FAQ/002-相关.md"},
+ },
+ }
+
+ output := FormatTable(results)
+ assert.Contains(t, output, "--- 关联文档 ---")
+ assert.Contains(t, output, "FAQ/002-相关.md")
+}
+
+func TestFormatTable_WithBoth(t *testing.T) {
+ results := []search.SearchResult{
+ {
+ ID: 1,
+ Path: "FAQ/001-测试.md",
+ Title: "测试文档",
+ Section: "FAQ",
+ Score: 10,
+ Content: "# 测试",
+ Links: []string{"FAQ/002-相关.md"},
+ },
+ }
+
+ output := FormatTable(results)
+ assert.Contains(t, output, "--- 内容 ---")
+ assert.Contains(t, output, "--- 关联文档 ---")
+}
+
+func TestFormatJSON_WithContentAndLinks(t *testing.T) {
+ results := []search.SearchResult{
+ {
+ ID: 1,
+ Path: "FAQ/001-测试.md",
+ Title: "测试文档",
+ Section: "FAQ",
+ Score: 10,
+ Content: "# 测试",
+ Links: []string{"FAQ/002-相关.md"},
+ },
+ }
+
+ output, err := FormatJSON(results)
+ require.NoError(t, err)
+ assert.Contains(t, output, `"content":"# 测试"`)
+ assert.Contains(t, output, `"links":["FAQ/002-相关.md"]`)
+}
+```
+
+- [ ] **Step 2: 运行测试确认失败**
+
+```bash
+cd ~/workspace/kb-cli
+go test ./internal/output -run "TestFormatTable_With|TestFormatJSON_With" -v
+```
+
+Expected: FAIL - 测试失败(输出不包含新字段)
+
+- [ ] **Step 3: 修改 FormatTable 方法**
+
+修改 `internal/output/formatter.go` 中的 `FormatTable` 函数:
+
+```go
+// FormatTable 格式化表格输出
+func FormatTable(results []search.SearchResult) string {
+ if len(results) == 0 {
+ return "未找到匹配结果\n"
+ }
+
+ var buf bytes.Buffer
+
+ // 表头
+ buf.WriteString(fmt.Sprintf("%-50s %-30s %-10s %s\n", "路径", "标题", "板块", "得分"))
+ buf.WriteString(strings.Repeat("-", 100) + "\n")
+
+ // 每行结果
+ for _, r := range results {
+ title := r.Title
+ if len(title) > 28 {
+ title = title[:28] + ".."
+ }
+ buf.WriteString(fmt.Sprintf("%-50s %-30s %-10s %d\n", r.Path, title, r.Section, r.Score))
+
+ // 输出内容(如果有)
+ if r.Content != "" {
+ buf.WriteString("\n--- 内容 ---\n")
+ buf.WriteString(r.Content)
+ buf.WriteString("\n")
+ }
+
+ // 输出关联文档(如果有)
+ if len(r.Links) > 0 {
+ buf.WriteString("\n--- 关联文档 ---\n")
+ for _, link := range r.Links {
+ buf.WriteString(fmt.Sprintf("- %s\n", link))
+ }
+ }
+ }
+
+ buf.WriteString(fmt.Sprintf("\n共 %d 条结果\n", len(results)))
+ return buf.String()
+}
+```
+
+- [ ] **Step 4: 运行测试确认通过**
+
+```bash
+cd ~/workspace/kb-cli
+go test ./internal/output -run "TestFormatTable_With|TestFormatJSON_With" -v
+```
+
+Expected: PASS
+
+- [ ] **Step 5: 运行所有测试确认无回归**
+
+```bash
+cd ~/workspace/kb-cli
+go test ./... -v
+```
+
+Expected: 所有测试通过
+
+- [ ] **Step 6: 提交**
+
+```bash
+cd ~/workspace/kb-cli
+git add internal/output/formatter.go internal/output/formatter_test.go
+git commit -m "feat: enhance output formatter with content and links support"
+```
+
+---
+
+## Task 6: 集成测试与性能验证
+
+**Files:**
+- 无新文件(使用现有测试数据)
+
+**Interfaces:**
+- Consumes: 所有之前的任务
+- Produces: 验证完整功能链
+
+- [ ] **Step 1: 构建最新版本**
+
+```bash
+cd ~/workspace/kb-cli
+go build -o bin/kb .
+```
+
+- [ ] **Step 2: 测试基础搜索(无增强)**
+
+```bash
+./bin/kb search 充装 --top 3
+```
+
+Expected: 显示 3 条结果,不包含内容和链接
+
+- [ ] **Step 3: 测试带内容搜索**
+
+```bash
+./bin/kb search 充装 --with-content --top 3
+```
+
+Expected: 显示 3 条结果,每条后跟 `--- 内容 ---` 和文件内容
+
+- [ ] **Step 4: 测试带链接搜索**
+
+```bash
+./bin/kb search 充装 --with-links --top 3
+```
+
+Expected: 显示 3 条结果,有链接的显示 `--- 关联文档 ---`
+
+- [ ] **Step 5: 测试两者都有**
+
+```bash
+./bin/kb search 充装 --with-content --with-links --top 3
+```
+
+Expected: 显示 3 条结果,同时包含内容和链接
+
+- [ ] **Step 6: 测试 JSON 输出**
+
+```bash
+./bin/kb search 充装 --with-content --with-links --json --top 3
+```
+
+Expected: JSON 格式输出,包含 `content` 和 `links` 字段
+
+- [ ] **Step 7: 性能测试**
+
+```bash
+time ./bin/kb search 充装 --with-content --with-links --top 10
+```
+
+Expected: 总耗时 < 100ms
+
+- [ ] **Step 8: 提交(如有修复)**
+
+```bash
+cd ~/workspace/kb-cli
+git add -A
+git commit -m "test: integration tests for enhanced search"
+```
+
+---
+
+## Task 7: 更新 kb-search 技能文档
+
+**Files:**
+- Modify: `~/.hermes/skills/kb-knowledge/kb-search/SKILL.md`
+
+**Interfaces:**
+- Consumes: kb-cli 新功能
+- Produces: 更新的技能文档
+
+- [ ] **Step 1: 备份现有文档**
+
+```bash
+cp ~/.hermes/skills/kb-knowledge/kb-search/SKILL.md ~/.hermes/skills/kb-knowledge/kb-search/SKILL.md.bak
+```
+
+- [ ] **Step 2: 修改核心工具部分**
+
+将 `## 核心工具` 部分从:
+
+```markdown
+## 核心工具
+
+**kb-search.py**:知识库搜索
+```
+
+改为:
+
+```markdown
+## 核心工具
+
+**kb-cli**:知识库搜索(优先使用)
+
+**完整路径**:`~/go/bin/kb`
+
+**基本用法**:
+```bash
+# 基础搜索
+kb search "关键词" --top 5
+
+# 带内容
+kb search "关键词" --with-content --top 3
+
+# 带链接
+kb search "关键词" --with-links --top 3
+
+# 带扩展词和症状词
+kb search "充装" --expanded "重量 规格" --symptom "报错" --with-content --top 3
+
+# JSON 输出
+kb search "关键词" --json --top 5
+```
+
+**kb-search.py**:知识库搜索(降级方案,仅当 kb-cli 不可用时)
+```
+
+- [ ] **Step 3: 修改执行流程部分**
+
+将 `## 执行流程` 部分的命令示例从 `python3 kb-search.py` 改为 `kb`:
+
+```bash
+# 方式1: 基础搜索
+kb search "用户问题关键词" --top 3
+
+# 方式2: 带内容(推荐)
+kb search "用户问题关键词" --with-content --top 3
+
+# 方式3: 带内容和链接
+kb search "用户问题关键词" --with-content --with-links --top 3
+
+# 方式4: 带扩展词和症状词
+kb search "关键词" --expanded "扩展词" --symptom "症状词" --with-content --top 3
+```
+
+- [ ] **Step 4: 添加降级方案章节**
+
+在文档末尾添加:
+
+```markdown
+## 降级方案
+
+**仅当 kb-cli 不可用时**,使用 kb-search.py:
+
+```bash
+export KB_VAULT=/home/aisim-p/aisim/note/001/笔记001
+python3 ~/.hermes/skills/kb-knowledge/kb-search/scripts/kb-search.py search "关键词" --with-content --top 3
+```
+
+**注意**:kb-search.py 当前有索引问题,可能返回空结果。优先使用 kb-cli。
+```
+
+- [ ] **Step 5: 验证文档格式**
+
+```bash
+cat ~/.hermes/skills/kb-knowledge/kb-search/SKILL.md | head -50
+```
+
+Expected: 显示更新后的文档,核心工具为 kb-cli
+
+- [ ] **Step 6: 提交到知识库**
+
+```bash
+cd ~/aisim/note/001/笔记001
+git add .
+git commit -m "docs: update kb-search skill to prioritize kb-cli"
+git push
+```
+
+---
+
+## Task 8: 最终验证与文档更新
+
+**Files:**
+- Modify: `~/workspace/kb-cli/README.md`
+
+**Interfaces:**
+- Consumes: 所有功能
+- Produces: 完整的文档和验证
+
+- [ ] **Step 1: 更新 README**
+
+在 `~/workspace/kb-cli/README.md` 的 `## 使用方法` 部分添加新功能示例:
+
+```markdown
+### 高级搜索
+
+```bash
+# 带文件内容
+kb search 充装规格 --with-content --top 3
+
+# 带关联文档
+kb search 充装规格 --with-links --top 3
+
+# 带内容和关联
+kb search 充装规格 --with-content --with-links --top 3
+
+# 带扩展词和症状词
+kb search 充装 --expanded "重量 规格" --symptom "报错" --with-content --top 3
+```
+```
+
+- [ ] **Step 2: 运行完整测试套件**
+
+```bash
+cd ~/workspace/kb-cli
+go test ./... -v
+```
+
+Expected: 所有测试通过
+
+- [ ] **Step 3: 构建并安装**
+
+```bash
+cd ~/workspace/kb-cli
+go build -o bin/kb .
+cp bin/kb ~/go/bin/
+```
+
+- [ ] **Step 4: 最终功能验证**
+
+```bash
+# 测试所有功能
+kb search 充装 --top 3
+kb search 充装 --with-content --top 3
+kb search 充装 --with-links --top 3
+kb search 充装 --with-content --with-links --top 3
+kb search 充装 --with-content --json --top 3
+```
+
+Expected: 所有命令正常工作
+
+- [ ] **Step 5: 提交所有更改**
+
+```bash
+cd ~/workspace/kb-cli
+git add README.md
+git commit -m "docs: update README with new features"
+git push
+```
+
+- [ ] **Step 6: 创建发布说明**
+
+```bash
+cat > RELEASE_NOTES.md << 'EOF'
+# kb-cli v1.1.0 发布说明
+
+## 新功能
+
+- `--with-content`: 搜索时返回完整文件内容
+- `--with-links`: 搜索时显示关联文档链接
+
+## 使用示例
+
+```bash
+# 带内容搜索
+kb search 充装规格 --with-content --top 3
+
+# 带关联文档
+kb search 充装规格 --with-links --top 3
+
+# 两者都有
+kb search 充装规格 --with-content --with-links --top 3
+```
+
+## 性能
+
+- 搜索 + 内容 + 链接:< 100ms
+- 向后兼容:现有命令不受影响
+
+## 技能调整
+
+- kb-search 技能现在优先使用 kb-cli
+- kb-search.py 降级为备选方案
+EOF
+```
+
+- [ ] **Step 7: 提交发布说明**
+
+```bash
+cd ~/workspace/kb-cli
+git add RELEASE_NOTES.md
+git commit -m "docs: add release notes for v1.1.0"
+git push
+```
+
+---
+
+## 完成标准
+
+所有任务完成后,必须满足:
+
+1. ✅ 所有单元测试通过
+2. ✅ 集成测试通过
+3. ✅ 性能 < 100ms
+4. ✅ kb-search 技能文档已更新
+5. ✅ README 已更新
+6. ✅ 发布说明已创建
+7. ✅ 代码已推送到远程仓库
+
+---
+
+## 执行选项
+
+**Plan complete and saved to `docs/superpowers/plans/2026-07-26-kb-cli-enhancement.md`. Two execution options:**
+
+**1. Subagent-Driven (recommended)** - I dispatch a fresh subagent per task, review between tasks, fast iteration
+
+**2. Inline Execution** - Execute tasks in this session using executing-plans, batch execution with checkpoints
+
+**Which approach?**
--
Gitblit v1.9.1