From 8de9d03cad67d6944d40567bff90350d132d60b0 Mon Sep 17 00:00:00 2001
From: ai_xiaopei <xiaopei@aisim.cn>
Date: Sun, 26 Jul 2026 09:54:11 +0800
Subject: [PATCH] feat: add index gc command for cleaning orphan nodes

---
 cmd/index.go |   68 +++++++++++++++++++++++++++++++--
 1 files changed, 63 insertions(+), 5 deletions(-)

diff --git a/cmd/index.go b/cmd/index.go
index c5399f6..a5f7c77 100644
--- a/cmd/index.go
+++ b/cmd/index.go
@@ -3,6 +3,8 @@
 import (
 	"fmt"
 	"os"
+	"path/filepath"
+	"strings"
 
 	"github.com/aisim/kb-cli/internal/index"
 	"github.com/spf13/cobra"
@@ -11,27 +13,31 @@
 var indexCmd = &cobra.Command{
 	Use:   "index",
 	Short: "索引管理",
-	Long:  `管理知识库索引`,
 }
 
 var indexBuildCmd = &cobra.Command{
 	Use:   "build",
-	Short: "构建/重建索引",
-	Long:  `构建或重建知识库索引`,
+	Short: "构建或重建知识库索引",
 	RunE:  runIndexBuild,
 }
 
 var indexStatusCmd = &cobra.Command{
 	Use:   "status",
-	Short: "查看索引状态",
-	Long:  `查看索引状态信息`,
+	Short: "查看索引状态信息",
 	RunE:  runIndexStatus,
+}
+
+var indexGcCmd = &cobra.Command{
+	Use:   "gc",
+	Short: "清理孤立节点和边",
+	RunE:  runIndexGc,
 }
 
 func init() {
 	rootCmd.AddCommand(indexCmd)
 	indexCmd.AddCommand(indexBuildCmd)
 	indexCmd.AddCommand(indexStatusCmd)
+	indexCmd.AddCommand(indexGcCmd)
 }
 
 func runIndexBuild(cmd *cobra.Command, args []string) error {
@@ -115,3 +121,55 @@
 
 	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
+}

--
Gitblit v1.9.1