From 93f4e39c1605c68d3fd8a12efc367c03e339bb6f Mon Sep 17 00:00:00 2001
From: ai_xiaopei <xiaopei@aisim.cn>
Date: Sun, 26 Jul 2026 10:07:59 +0800
Subject: [PATCH] refactor: 统一命令名称为 kb-cli

---
 internal/index/sqlite.go |  111 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 111 insertions(+), 0 deletions(-)

diff --git a/internal/index/sqlite.go b/internal/index/sqlite.go
index e1086df..db194fe 100644
--- a/internal/index/sqlite.go
+++ b/internal/index/sqlite.go
@@ -6,6 +6,7 @@
 	"fmt"
 	"os"
 	"path/filepath"
+	"strings"
 
 	_ "github.com/mattn/go-sqlite3"
 	"github.com/aisim/kb-cli/internal/graph"
@@ -147,3 +148,113 @@
 	err := s.db.QueryRow("SELECT COUNT(*) FROM edges").Scan(&count)
 	return count, err
 }
+
+// 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
+}
+
+// NodeInfo 节点基本信息(用于 GC)
+type NodeInfo struct {
+	ID   int64
+	Path string
+}
+
+// GetAllNodes 获取所有节点(用于 GC 检查)
+func (s *Store) GetAllNodes() ([]NodeInfo, error) {
+	rows, err := s.db.Query("SELECT id, path FROM nodes")
+	if err != nil {
+		return nil, fmt.Errorf("查询节点失败: %w", err)
+	}
+	defer rows.Close()
+
+	var nodes []NodeInfo
+	for rows.Next() {
+		var n NodeInfo
+		if err := rows.Scan(&n.ID, &n.Path); err != nil {
+			return nil, fmt.Errorf("扫描节点失败: %w", err)
+		}
+		nodes = append(nodes, n)
+	}
+
+	if err := rows.Err(); err != nil {
+		return nil, fmt.Errorf("遍历节点失败: %w", err)
+	}
+
+	return nodes, nil
+}
+
+// DeleteNodesByPaths 删除指定路径的节点及其关联边
+func (s *Store) DeleteNodesByPaths(paths []string) (int, error) {
+	if len(paths) == 0 {
+		return 0, nil
+	}
+
+	// 构建 IN 子句
+	placeholders := make([]string, len(paths))
+	args := make([]interface{}, len(paths))
+	for i, path := range paths {
+		placeholders[i] = "?"
+		args[i] = path
+	}
+
+	tx, err := s.db.Begin()
+	if err != nil {
+		return 0, fmt.Errorf("开始事务失败: %w", err)
+	}
+
+	// 先删除关联边
+	query := fmt.Sprintf(`
+		DELETE FROM edges 
+		WHERE from_node IN (SELECT id FROM nodes WHERE path IN (%s))
+		   OR to_node IN (SELECT id FROM nodes WHERE path IN (%s))
+	`, strings.Join(placeholders, ","), strings.Join(placeholders, ","))
+	
+	// 参数需要重复两次
+	allArgs := append(args, args...)
+	_, err = tx.Exec(query, allArgs...)
+	if err != nil {
+		tx.Rollback()
+		return 0, fmt.Errorf("删除边失败: %w", err)
+	}
+
+	// 删除节点
+	query = fmt.Sprintf("DELETE FROM nodes WHERE path IN (%s)", strings.Join(placeholders, ","))
+	result, err := tx.Exec(query, args...)
+	if err != nil {
+		tx.Rollback()
+		return 0, fmt.Errorf("删除节点失败: %w", err)
+	}
+
+	if err := tx.Commit(); err != nil {
+		return 0, fmt.Errorf("提交事务失败: %w", err)
+	}
+
+	affected, _ := result.RowsAffected()
+	return int(affected), nil
+}

--
Gitblit v1.9.1