fix: replace implicit app inference with explicit selection for Skills import and sync

Skills import previously inferred app enablement from filesystem presence,
causing incorrect multi-app activation when the same skill directory existed
under multiple app paths. Now the frontend submits explicit app selections
via ImportSkillSelection, and schema migration preserves a snapshot of
legacy app mappings to avoid lossy reconstruction.

Also adds reconciliation to sync_to_app (removes disabled/orphaned symlinks)
and MCP sync_all_enabled (removes disabled servers from live config).
This commit is contained in:
Jason
2026-03-14 17:20:01 +08:00
parent f1d2c6045b
commit 7097a0d710
13 changed files with 560 additions and 39 deletions
+31 -1
View File
@@ -5,6 +5,13 @@
use super::{lock_conn, Database, SCHEMA_VERSION};
use crate::error::AppError;
use rusqlite::Connection;
use serde::Serialize;
#[derive(Serialize)]
struct LegacySkillMigrationRow {
directory: String,
app_type: String,
}
impl Database {
/// 创建所有数据库表
@@ -846,12 +853,31 @@ impl Database {
log::info!("开始迁移 skills 表到 v3 结构(统一管理架构)...");
// 1. 备份旧数据(用于日志)
// 1. 备份旧数据(用于日志和后续启动迁移
let old_count: i64 = conn
.query_row("SELECT COUNT(*) FROM skills", [], |row| row.get(0))
.unwrap_or(0);
log::info!("旧 skills 表有 {old_count} 条记录");
let mut stmt = conn
.prepare(
"SELECT directory, app_type FROM skills
WHERE installed = 1",
)
.map_err(|e| AppError::Database(format!("查询旧 skills 快照失败: {e}")))?;
let snapshot_rows: Vec<LegacySkillMigrationRow> = stmt
.query_map([], |row| {
Ok(LegacySkillMigrationRow {
directory: row.get(0)?,
app_type: row.get(1)?,
})
})
.map_err(|e| AppError::Database(format!("读取旧 skills 快照失败: {e}")))?
.collect::<Result<Vec<_>, _>>()
.map_err(|e| AppError::Database(format!("解析旧 skills 快照失败: {e}")))?;
let snapshot_json = serde_json::to_string(&snapshot_rows)
.map_err(|e| AppError::Database(format!("序列化旧 skills 快照失败: {e}")))?;
// 标记:需要在启动后从文件系统扫描并重建 Skills 数据
// 说明:v3 结构将 Skills 的 SSOT 迁移到 ~/.cc-switch/skills/
// 旧表只存“安装记录”,无法直接无损迁移到新结构,因此改为启动后扫描 app 目录导入。
@@ -859,6 +885,10 @@ impl Database {
"INSERT OR REPLACE INTO settings (key, value) VALUES ('skills_ssot_migration_pending', 'true')",
[],
);
let _ = conn.execute(
"INSERT OR REPLACE INTO settings (key, value) VALUES ('skills_ssot_migration_snapshot', ?1)",
[snapshot_json],
);
// 2. 删除旧表
conn.execute("DROP TABLE IF EXISTS skills", [])