mirror of
https://github.com/farion1231/cc-switch.git
synced 2026-07-28 16:56:16 +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:
@@ -0,0 +1,151 @@
|
||||
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
|
||||
import {
|
||||
skillsApi,
|
||||
type AppType,
|
||||
type DiscoverableSkill,
|
||||
type InstalledSkill,
|
||||
} from "@/lib/api/skills";
|
||||
|
||||
/**
|
||||
* 查询所有已安装的 Skills
|
||||
*/
|
||||
export function useInstalledSkills() {
|
||||
return useQuery({
|
||||
queryKey: ["skills", "installed"],
|
||||
queryFn: () => skillsApi.getInstalled(),
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 发现可安装的 Skills(从仓库获取)
|
||||
*/
|
||||
export function useDiscoverableSkills() {
|
||||
return useQuery({
|
||||
queryKey: ["skills", "discoverable"],
|
||||
queryFn: () => skillsApi.discoverAvailable(),
|
||||
staleTime: 5 * 60 * 1000, // 5 分钟内不重新获取
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 安装 Skill
|
||||
*/
|
||||
export function useInstallSkill() {
|
||||
const queryClient = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: ({
|
||||
skill,
|
||||
currentApp,
|
||||
}: {
|
||||
skill: DiscoverableSkill;
|
||||
currentApp: AppType;
|
||||
}) => skillsApi.installUnified(skill, currentApp),
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries({ queryKey: ["skills", "installed"] });
|
||||
queryClient.invalidateQueries({ queryKey: ["skills", "discoverable"] });
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 卸载 Skill
|
||||
*/
|
||||
export function useUninstallSkill() {
|
||||
const queryClient = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: (id: string) => skillsApi.uninstallUnified(id),
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries({ queryKey: ["skills", "installed"] });
|
||||
queryClient.invalidateQueries({ queryKey: ["skills", "discoverable"] });
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 切换 Skill 在特定应用的启用状态
|
||||
*/
|
||||
export function useToggleSkillApp() {
|
||||
const queryClient = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: ({
|
||||
id,
|
||||
app,
|
||||
enabled,
|
||||
}: {
|
||||
id: string;
|
||||
app: AppType;
|
||||
enabled: boolean;
|
||||
}) => skillsApi.toggleApp(id, app, enabled),
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries({ queryKey: ["skills", "installed"] });
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 扫描未管理的 Skills
|
||||
*/
|
||||
export function useScanUnmanagedSkills() {
|
||||
return useQuery({
|
||||
queryKey: ["skills", "unmanaged"],
|
||||
queryFn: () => skillsApi.scanUnmanaged(),
|
||||
enabled: false, // 手动触发
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 从应用目录导入 Skills
|
||||
*/
|
||||
export function useImportSkillsFromApps() {
|
||||
const queryClient = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: (directories: string[]) => skillsApi.importFromApps(directories),
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries({ queryKey: ["skills", "installed"] });
|
||||
queryClient.invalidateQueries({ queryKey: ["skills", "unmanaged"] });
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取仓库列表
|
||||
*/
|
||||
export function useSkillRepos() {
|
||||
return useQuery({
|
||||
queryKey: ["skills", "repos"],
|
||||
queryFn: () => skillsApi.getRepos(),
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加仓库
|
||||
*/
|
||||
export function useAddSkillRepo() {
|
||||
const queryClient = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: skillsApi.addRepo,
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries({ queryKey: ["skills", "repos"] });
|
||||
queryClient.invalidateQueries({ queryKey: ["skills", "discoverable"] });
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除仓库
|
||||
*/
|
||||
export function useRemoveSkillRepo() {
|
||||
const queryClient = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: ({ owner, name }: { owner: string; name: string }) =>
|
||||
skillsApi.removeRepo(owner, name),
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries({ queryKey: ["skills", "repos"] });
|
||||
queryClient.invalidateQueries({ queryKey: ["skills", "discoverable"] });
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
// ========== 辅助类型 ==========
|
||||
|
||||
export type { InstalledSkill, DiscoverableSkill, AppType };
|
||||
Reference in New Issue
Block a user