feat: add codex skills support and enhance system status UI

- Introduced new query keys for codex skills and their list.
- Updated English and Chinese locale files for system status messages.
- Enhanced the SettingsPage to display installation details and upgrade options.
- Integrated CLI mode toggle in SkillsManagerPage for better skill management.
- Modified skills routes to handle CLI type for skill operations and configurations.
This commit is contained in:
catlog22
2026-02-07 21:45:12 +08:00
parent 2094c1085b
commit 678be8d41f
14 changed files with 519 additions and 168 deletions

View File

@@ -207,6 +207,8 @@ export {
useRefreshCodexCliEnhancement,
useCcwInstallStatus,
useCliToolStatus,
useCcwInstallations,
useUpgradeCcwInstallation,
systemSettingsKeys,
} from './useSystemSettings';
export type {
@@ -215,6 +217,7 @@ export type {
UseCodexCliEnhancementStatusReturn,
UseCcwInstallStatusReturn,
UseCliToolStatusReturn,
UseCcwInstallationsReturn,
} from './useSystemSettings';
// ========== CLI Execution ==========

View File

@@ -39,6 +39,7 @@ export interface UseSkillsOptions {
filter?: SkillsFilter;
staleTime?: number;
enabled?: boolean;
cliType?: 'claude' | 'codex';
}
export interface UseSkillsReturn {
@@ -61,15 +62,19 @@ export interface UseSkillsReturn {
* Hook for fetching and filtering skills
*/
export function useSkills(options: UseSkillsOptions = {}): UseSkillsReturn {
const { filter, staleTime = STALE_TIME, enabled = true } = options;
const { filter, staleTime = STALE_TIME, enabled = true, cliType = 'claude' } = options;
const queryClient = useQueryClient();
const projectPath = useWorkflowStore(selectProjectPath);
const queryKey = cliType === 'codex'
? workspaceQueryKeys.codexSkillsList(projectPath)
: workspaceQueryKeys.skillsList(projectPath);
const query = useQuery({
queryKey: workspaceQueryKeys.skillsList(projectPath),
queryFn: () => fetchSkills(projectPath),
queryKey,
queryFn: () => fetchSkills(projectPath, cliType),
staleTime,
enabled: enabled, // Remove projectPath requirement - API works without it
enabled: enabled,
retry: 2,
});
@@ -133,7 +138,10 @@ export function useSkills(options: UseSkillsOptions = {}): UseSkillsReturn {
const invalidate = async () => {
if (projectPath) {
await queryClient.invalidateQueries({ queryKey: workspaceQueryKeys.skills(projectPath) });
const invalidateKey = cliType === 'codex'
? workspaceQueryKeys.codexSkills(projectPath)
: workspaceQueryKeys.skills(projectPath);
await queryClient.invalidateQueries({ queryKey: invalidateKey });
}
};
@@ -157,7 +165,7 @@ export function useSkills(options: UseSkillsOptions = {}): UseSkillsReturn {
// ========== Mutations ==========
export interface UseToggleSkillReturn {
toggleSkill: (skillName: string, enabled: boolean, location: 'project' | 'user') => Promise<Skill>;
toggleSkill: (skillName: string, enabled: boolean, location: 'project' | 'user', cliType?: 'claude' | 'codex') => Promise<Skill>;
isToggling: boolean;
error: Error | null;
}
@@ -168,10 +176,10 @@ export function useToggleSkill(): UseToggleSkillReturn {
const { addToast, removeToast, success, error } = useNotifications();
const mutation = useMutation({
mutationFn: ({ skillName, enabled, location }: { skillName: string; enabled: boolean; location: 'project' | 'user' }) =>
mutationFn: ({ skillName, enabled, location, cliType = 'claude' }: { skillName: string; enabled: boolean; location: 'project' | 'user'; cliType?: 'claude' | 'codex' }) =>
enabled
? enableSkill(skillName, location, projectPath)
: disableSkill(skillName, location, projectPath),
? enableSkill(skillName, location, projectPath, cliType)
: disableSkill(skillName, location, projectPath, cliType),
onMutate: (): { loadingId: string } => {
const loadingId = addToast('info', formatMessage('common.loading'), undefined, { duration: 0 });
return { loadingId };
@@ -183,7 +191,13 @@ export function useToggleSkill(): UseToggleSkillReturn {
const operation = variables.enabled ? 'skillEnable' : 'skillDisable';
success(formatMessage(`feedback.${operation}.success`));
queryClient.invalidateQueries({ queryKey: projectPath ? workspaceQueryKeys.skills(projectPath) : ['skills'] });
// Invalidate both claude and codex skills queries
if (projectPath) {
queryClient.invalidateQueries({ queryKey: workspaceQueryKeys.skills(projectPath) });
queryClient.invalidateQueries({ queryKey: workspaceQueryKeys.codexSkills(projectPath) });
} else {
queryClient.invalidateQueries({ queryKey: ['skills'] });
}
},
onError: (err, variables, context) => {
const { loadingId } = context ?? { loadingId: '' };
@@ -196,7 +210,7 @@ export function useToggleSkill(): UseToggleSkillReturn {
});
return {
toggleSkill: (skillName, enabled, location) => mutation.mutateAsync({ skillName, enabled, location }),
toggleSkill: (skillName, enabled, location, cliType) => mutation.mutateAsync({ skillName, enabled, location, cliType }),
isToggling: mutation.isPending,
error: mutation.error,
};

View File

@@ -14,10 +14,13 @@ import {
refreshCodexCliEnhancement,
fetchAggregatedStatus,
fetchCliToolStatus,
fetchCcwInstallations,
upgradeCcwInstallation,
type ChineseResponseStatus,
type WindowsPlatformStatus,
type CodexCliEnhancementStatus,
type CcwInstallStatus,
type CcwInstallationManifest,
} from '../lib/api';
// Query key factory
@@ -28,6 +31,7 @@ export const systemSettingsKeys = {
codexCliEnhancement: () => [...systemSettingsKeys.all, 'codexCliEnhancement'] as const,
aggregatedStatus: () => [...systemSettingsKeys.all, 'aggregatedStatus'] as const,
cliToolStatus: () => [...systemSettingsKeys.all, 'cliToolStatus'] as const,
ccwInstallations: () => [...systemSettingsKeys.all, 'ccwInstallations'] as const,
};
const STALE_TIME = 60 * 1000; // 1 minute
@@ -236,3 +240,48 @@ export function useCliToolStatus(): UseCliToolStatusReturn {
refetch: () => { query.refetch(); },
};
}
// ========================================
// CCW Installations Hooks
// ========================================
export interface UseCcwInstallationsReturn {
installations: CcwInstallationManifest[];
isLoading: boolean;
error: Error | null;
refetch: () => void;
}
export function useCcwInstallations(): UseCcwInstallationsReturn {
const query = useQuery({
queryKey: systemSettingsKeys.ccwInstallations(),
queryFn: fetchCcwInstallations,
staleTime: 5 * 60 * 1000, // 5 minutes
retry: 1,
});
return {
installations: query.data?.installations ?? [],
isLoading: query.isLoading,
error: query.error,
refetch: () => { query.refetch(); },
};
}
export function useUpgradeCcwInstallation() {
const queryClient = useQueryClient();
const mutation = useMutation({
mutationFn: (path?: string) => upgradeCcwInstallation(path),
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: systemSettingsKeys.ccwInstallations() });
queryClient.invalidateQueries({ queryKey: systemSettingsKeys.aggregatedStatus() });
},
});
return {
upgrade: mutation.mutateAsync,
isPending: mutation.isPending,
error: mutation.error,
};
}