From 60eb89c12ee0661785395bff90940204b16dcb3a Mon Sep 17 00:00:00 2001
From: ax_rd <ax_rd@aisim.cn>
Date: Thu, 03 Sep 2026 13:21:39 +0800
Subject: [PATCH] docs: e2e 验证脚本 + README 更新(增量同步/explore/构建要求)
---
cmd/index.go | 181 ++++++++++++++++++++++++++++++++++++++++----
1 files changed, 162 insertions(+), 19 deletions(-)
diff --git a/cmd/index.go b/cmd/index.go
index c5399f6..4678803 100644
--- a/cmd/index.go
+++ b/cmd/index.go
@@ -3,6 +3,10 @@
import (
"fmt"
"os"
+ "os/exec"
+ "path/filepath"
+ "strings"
+ "time"
"github.com/aisim/kb-cli/internal/index"
"github.com/spf13/cobra"
@@ -11,27 +15,53 @@
var indexCmd = &cobra.Command{
Use: "index",
Short: "索引管理",
- Long: `管理知识库索引`,
}
+// forceRebuild index build 的 --force flag:全量重建索引
+var forceRebuild bool
+
var indexBuildCmd = &cobra.Command{
Use: "build",
- Short: "构建/重建索引",
- Long: `构建或重建知识库索引`,
+ Short: "构建或重建知识库索引",
+ Long: `kb-cli index build [--vault <路径>] [--db <路径>] [--force] # 构建或重建知识库索引(默认增量对账,--force 全量重建)`,
RunE: runIndexBuild,
}
var indexStatusCmd = &cobra.Command{
Use: "status",
- Short: "查看索引状态",
- Long: `查看索引状态信息`,
+ Short: "查看索引状态信息",
+ Long: `kb-cli index status [--vault <路径>] [--db <路径>] # 查看索引状态(节点数、边数、commit、构建时间)`,
RunE: runIndexStatus,
}
+var indexGcCmd = &cobra.Command{
+ Use: "gc",
+ Short: "清理孤立节点和边",
+ Long: `kb-cli index gc [--vault <路径>] [--db <路径>] # 清理索引中文件已删除的孤立节点和边`,
+ RunE: runIndexGc,
+}
+
+var gitCmd = &cobra.Command{
+ Use: "git",
+ Short: "Git 同步操作",
+}
+
+var gitSyncCmd = &cobra.Command{
+ Use: "sync",
+ Short: "同步知识库到 Git 仓库",
+ Long: `kb-cli git sync [--vault <路径>] # 执行 git add、commit、push 同步知识库`,
+ RunE: runGitSync,
+}
+
func init() {
rootCmd.AddCommand(indexCmd)
indexCmd.AddCommand(indexBuildCmd)
indexCmd.AddCommand(indexStatusCmd)
+ indexCmd.AddCommand(indexGcCmd)
+ indexBuildCmd.Flags().BoolVar(&forceRebuild, "force", false, "全量重建索引(默认走增量对账)")
+
+ rootCmd.AddCommand(gitCmd)
+ gitCmd.AddCommand(gitSyncCmd)
}
func runIndexBuild(cmd *cobra.Command, args []string) error {
@@ -42,19 +72,26 @@
}
defer store.Close()
- // 获取当前 commit
- commit, err := index.GetGitCommit(vaultPath)
- if err != nil {
- fmt.Fprintln(os.Stderr, "警告: 无法获取 git commit:", err)
- commit = ""
+ if forceRebuild {
+ // 全量重建
+ commit, err := index.GetGitCommit(vaultPath)
+ if err != nil {
+ fmt.Fprintln(os.Stderr, "警告: 无法获取 git commit:", err)
+ commit = ""
+ }
+ fmt.Fprintln(os.Stderr, "正在全量重建索引...")
+ if err := rebuildFull(store, commit); err != nil {
+ return fmt.Errorf("重建索引失败: %w", err)
+ }
+ fmt.Fprintln(os.Stderr, "索引重建完成")
+ return nil
}
- fmt.Fprintln(os.Stderr, "正在重建索引...")
- if err := rebuildIndex(store, commit); err != nil {
- return fmt.Errorf("重建索引失败: %w", err)
+ // 默认走增量对账
+ if err := syncIndex(store); err != nil {
+ return fmt.Errorf("增量同步失败: %w", err)
}
-
- fmt.Fprintln(os.Stderr, "索引重建完成")
+ fmt.Fprintln(os.Stderr, "索引同步完成")
return nil
}
@@ -101,17 +138,123 @@
fmt.Printf(" 当前 commit: %s\n", currentCommit)
fmt.Printf(" 构建时间: %s\n", builtAt)
- // 检查是否需要更新
- needsRebuild, _, err := index.NeedsRebuild(store, vaultPath)
+ // 检查是否有变更(stat 快速比对)
+ dirty, err := index.QuickCheck(store, vaultPath)
if err != nil {
return fmt.Errorf("检查索引状态失败: %w", err)
}
- if needsRebuild {
- fmt.Printf(" 状态: 需要更新\n")
+ if dirty {
+ fmt.Printf(" 状态: 有变更\n")
} else {
fmt.Printf(" 状态: 最新\n")
}
return nil
}
+
+func runIndexGc(cmd *cobra.Command, args []string) error {
+ // 展开 ~ 为实际路径
+ expandedVaultPath := vaultPath
+ if strings.HasPrefix(vaultPath, "~/") {
+ home, err := os.UserHomeDir()
+ if err != nil {
+ return fmt.Errorf("获取用户目录失败: %w", err)
+ }
+ expandedVaultPath = filepath.Join(home, vaultPath[2:])
+ }
+
+ // 打开索引
+ store, err := index.Open(dbPath)
+ if err != nil {
+ return fmt.Errorf("打开索引失败: %w", err)
+ }
+ defer store.Close()
+
+ fmt.Fprintln(os.Stderr, "正在清理孤立节点和边...")
+
+ // 获取所有节点路径
+ nodes, err := store.GetAllNodes()
+ if err != nil {
+ return fmt.Errorf("获取节点失败: %w", err)
+ }
+
+ // 检查文件是否存在
+ var orphanPaths []string
+ for _, node := range nodes {
+ fullPath := filepath.Join(expandedVaultPath, node.Path)
+ if _, err := os.Stat(fullPath); os.IsNotExist(err) {
+ orphanPaths = append(orphanPaths, node.Path)
+ }
+ }
+
+ if len(orphanPaths) == 0 {
+ fmt.Fprintln(os.Stderr, "没有发现孤立节点")
+ return nil
+ }
+
+ fmt.Fprintf(os.Stderr, "发现 %d 个孤立节点,正在清理...\n", len(orphanPaths))
+
+ // 删除孤立节点
+ deletedCount, err := store.DeleteNodesByPaths(orphanPaths)
+ if err != nil {
+ return fmt.Errorf("删除节点失败: %w", err)
+ }
+
+ fmt.Fprintf(os.Stderr, "已清理 %d 个孤立节点\n", deletedCount)
+ return nil
+}
+
+func runGitSync(cmd *cobra.Command, args []string) error {
+ // 展开 ~ 为实际路径
+ expandedVaultPath := vaultPath
+ if strings.HasPrefix(vaultPath, "~/") {
+ home, err := os.UserHomeDir()
+ if err != nil {
+ return fmt.Errorf("获取用户目录失败: %w", err)
+ }
+ expandedVaultPath = filepath.Join(home, vaultPath[2:])
+ }
+
+ fmt.Fprintln(os.Stderr, "正在同步知识库到 Git 仓库...")
+
+ // 检查是否是 git 仓库
+ gitDir := filepath.Join(expandedVaultPath, ".git")
+ if _, err := os.Stat(gitDir); os.IsNotExist(err) {
+ return fmt.Errorf("知识库目录不是 Git 仓库: %s", expandedVaultPath)
+ }
+
+ // 执行 git add -A
+ addCmd := exec.Command("git", "-C", expandedVaultPath, "add", "-A")
+ if output, err := addCmd.CombinedOutput(); err != nil {
+ return fmt.Errorf("git add 失败: %w\n%s", err, output)
+ }
+
+ // 检查是否有变更
+ statusCmd := exec.Command("git", "-C", expandedVaultPath, "status", "--porcelain")
+ statusOutput, err := statusCmd.Output()
+ if err != nil {
+ return fmt.Errorf("git status 失败: %w", err)
+ }
+
+ if len(statusOutput) == 0 {
+ fmt.Fprintln(os.Stderr, "没有变更需要提交")
+ return nil
+ }
+
+ // 生成 commit 信息
+ commitMsg := fmt.Sprintf("kb-cli: 自动同步 %s", time.Now().Format("2006-01-02 15:04:05"))
+ commitCmd := exec.Command("git", "-C", expandedVaultPath, "commit", "-m", commitMsg)
+ if output, err := commitCmd.CombinedOutput(); err != nil {
+ return fmt.Errorf("git commit 失败: %w\n%s", err, output)
+ }
+
+ // 执行 git push
+ pushCmd := exec.Command("git", "-C", expandedVaultPath, "push")
+ if output, err := pushCmd.CombinedOutput(); err != nil {
+ return fmt.Errorf("git push 失败: %w\n%s", err, output)
+ }
+
+ fmt.Fprintln(os.Stderr, "Git 同步完成")
+ return nil
+}
--
Gitblit v1.10.0