mirror of
https://github.com/farion1231/cc-switch.git
synced 2026-04-28 05:33:10 +08:00
feat(skills): unified management architecture with SSOT and React Query
- Introduce SSOT (Single Source of Truth) at ~/.cc-switch/skills/ - Add three-app toggle support (Claude/Codex/Gemini) for each skill - Refactor frontend to use TanStack Query hooks instead of manual state - Add UnifiedSkillsPanel for managing installed skills with app toggles - Add useSkills.ts with declarative data fetching hooks - Extend skills.ts API with unified install/uninstall/toggle methods - Support importing unmanaged skills from app directories - Add v2→v3 database migration for new skills table structure
This commit is contained in:
@@ -71,11 +71,21 @@ impl Database {
|
||||
PRIMARY KEY (id, app_type)
|
||||
)", []).map_err(|e| AppError::Database(e.to_string()))?;
|
||||
|
||||
// 5. Skills 表
|
||||
// 5. Skills 表(v3.10.0+ 统一结构)
|
||||
conn.execute(
|
||||
"CREATE TABLE IF NOT EXISTS skills (
|
||||
directory TEXT NOT NULL, app_type TEXT NOT NULL, installed BOOLEAN NOT NULL DEFAULT 0,
|
||||
installed_at INTEGER NOT NULL DEFAULT 0, PRIMARY KEY (directory, app_type)
|
||||
id TEXT PRIMARY KEY,
|
||||
name TEXT NOT NULL,
|
||||
description TEXT,
|
||||
directory TEXT NOT NULL,
|
||||
repo_owner TEXT,
|
||||
repo_name TEXT,
|
||||
repo_branch TEXT DEFAULT 'main',
|
||||
readme_url TEXT,
|
||||
enabled_claude BOOLEAN NOT NULL DEFAULT 0,
|
||||
enabled_codex BOOLEAN NOT NULL DEFAULT 0,
|
||||
enabled_gemini BOOLEAN NOT NULL DEFAULT 0,
|
||||
installed_at INTEGER NOT NULL DEFAULT 0
|
||||
)",
|
||||
[],
|
||||
)
|
||||
@@ -331,6 +341,11 @@ impl Database {
|
||||
Self::migrate_v1_to_v2(conn)?;
|
||||
Self::set_user_version(conn, 2)?;
|
||||
}
|
||||
2 => {
|
||||
log::info!("迁移数据库从 v2 到 v3(Skills 统一管理架构)");
|
||||
Self::migrate_v2_to_v3(conn)?;
|
||||
Self::set_user_version(conn, 3)?;
|
||||
}
|
||||
_ => {
|
||||
return Err(AppError::Database(format!(
|
||||
"未知的数据库版本 {version},无法迁移到 {SCHEMA_VERSION}"
|
||||
@@ -689,6 +704,17 @@ impl Database {
|
||||
|
||||
/// 迁移 skills 表:从单 key 主键改为 (directory, app_type) 复合主键
|
||||
fn migrate_skills_table(conn: &Connection) -> Result<(), AppError> {
|
||||
// v3 结构(统一管理架构)已经是更高版本的 skills 表:
|
||||
// - 主键为 id
|
||||
// - 包含 enabled_claude / enabled_codex / enabled_gemini 等列
|
||||
// 在这种情况下,不应再执行 v1 -> v2 的迁移逻辑,否则会因列不匹配而失败。
|
||||
if Self::has_column(conn, "skills", "enabled_claude")?
|
||||
|| Self::has_column(conn, "skills", "id")?
|
||||
{
|
||||
log::info!("skills 表已经是 v3 结构,跳过 v1 -> v2 迁移");
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
// 检查是否已经是新表结构
|
||||
if Self::has_column(conn, "skills", "app_type")? {
|
||||
log::info!("skills 表已经包含 app_type 字段,跳过迁移");
|
||||
@@ -760,6 +786,69 @@ impl Database {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// v2 -> v3 迁移:Skills 统一管理架构
|
||||
///
|
||||
/// 将 skills 表从 (directory, app_type) 复合主键结构迁移到统一的 id 主键结构,
|
||||
/// 支持三应用启用标志(enabled_claude, enabled_codex, enabled_gemini)。
|
||||
///
|
||||
/// 迁移策略:
|
||||
/// 1. 旧数据库只存储安装记录,真正的 skill 文件在文件系统
|
||||
/// 2. 直接重建新表结构,后续由 SkillService 在首次启动时扫描文件系统重建数据
|
||||
fn migrate_v2_to_v3(conn: &Connection) -> Result<(), AppError> {
|
||||
// 检查是否已经是新结构(通过检查是否有 enabled_claude 列)
|
||||
if Self::has_column(conn, "skills", "enabled_claude")? {
|
||||
log::info!("skills 表已经是 v3 结构,跳过迁移");
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
log::info!("开始迁移 skills 表到 v3 结构(统一管理架构)...");
|
||||
|
||||
// 1. 备份旧数据(用于日志)
|
||||
let old_count: i64 = conn
|
||||
.query_row("SELECT COUNT(*) FROM skills", [], |row| row.get(0))
|
||||
.unwrap_or(0);
|
||||
log::info!("旧 skills 表有 {old_count} 条记录");
|
||||
|
||||
// 标记:需要在启动后从文件系统扫描并重建 Skills 数据
|
||||
// 说明:v3 结构将 Skills 的 SSOT 迁移到 ~/.cc-switch/skills/,
|
||||
// 旧表只存“安装记录”,无法直接无损迁移到新结构,因此改为启动后扫描 app 目录导入。
|
||||
let _ = conn.execute(
|
||||
"INSERT OR REPLACE INTO settings (key, value) VALUES ('skills_ssot_migration_pending', 'true')",
|
||||
[],
|
||||
);
|
||||
|
||||
// 2. 删除旧表
|
||||
conn.execute("DROP TABLE IF EXISTS skills", [])
|
||||
.map_err(|e| AppError::Database(format!("删除旧 skills 表失败: {e}")))?;
|
||||
|
||||
// 3. 创建新表
|
||||
conn.execute(
|
||||
"CREATE TABLE skills (
|
||||
id TEXT PRIMARY KEY,
|
||||
name TEXT NOT NULL,
|
||||
description TEXT,
|
||||
directory TEXT NOT NULL,
|
||||
repo_owner TEXT,
|
||||
repo_name TEXT,
|
||||
repo_branch TEXT DEFAULT 'main',
|
||||
readme_url TEXT,
|
||||
enabled_claude BOOLEAN NOT NULL DEFAULT 0,
|
||||
enabled_codex BOOLEAN NOT NULL DEFAULT 0,
|
||||
enabled_gemini BOOLEAN NOT NULL DEFAULT 0,
|
||||
installed_at INTEGER NOT NULL DEFAULT 0
|
||||
)",
|
||||
[],
|
||||
)
|
||||
.map_err(|e| AppError::Database(format!("创建新 skills 表失败: {e}")))?;
|
||||
|
||||
log::info!(
|
||||
"skills 表已迁移到 v3 结构。\n\
|
||||
注意:旧的安装记录已清除,首次启动时将自动扫描文件系统重建数据。"
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// 插入默认模型定价数据
|
||||
/// 格式: (model_id, display_name, input, output, cache_read, cache_creation)
|
||||
/// 注意: model_id 使用短横线格式(如 claude-haiku-4-5),与 API 返回的模型名称标准化后一致
|
||||
|
||||
Reference in New Issue
Block a user