feat: enhance MCP server management and system settings

- Added functionality to save MCP server configurations as templates in the MCP Manager.
- Implemented new hooks for managing system settings including Chinese response, Windows platform, and Codex CLI enhancements.
- Updated API calls to support fetching and toggling system settings.
- Introduced UI components for displaying and managing response language settings and system status.
- Enhanced error handling and notifications for server deletion and template saving actions.
- Updated localization files for new settings and descriptions in English and Chinese.
This commit is contained in:
catlog22
2026-02-07 21:17:18 +08:00
parent d29527ae16
commit 2094c1085b
52 changed files with 2061 additions and 602 deletions

View File

@@ -993,22 +993,36 @@ export interface SkillsResponse {
* @param projectPath - Optional project path to filter data by workspace
*/
export async function fetchSkills(projectPath?: string): Promise<SkillsResponse> {
// Response type from backend when includeDisabled=true
interface ExtendedSkillsResponse {
skills?: Skill[];
projectSkills?: Skill[];
userSkills?: Skill[];
disabledProjectSkills?: Skill[];
disabledUserSkills?: Skill[];
}
// Helper to add location and enabled status to skills
// Backend only returns enabled skills (with SKILL.md), so we set enabled: true
const addMetadata = (skills: Skill[], location: 'project' | 'user'): Skill[] =>
const addEnabledMetadata = (skills: Skill[], location: 'project' | 'user'): Skill[] =>
skills.map(skill => ({ ...skill, location, enabled: true }));
const addDisabledMetadata = (skills: Skill[], location: 'project' | 'user'): Skill[] =>
skills.map(skill => ({ ...skill, location, enabled: false }));
const buildSkillsList = (data: ExtendedSkillsResponse): Skill[] => {
const projectSkillsEnabled = addEnabledMetadata(data.projectSkills ?? [], 'project');
const userSkillsEnabled = addEnabledMetadata(data.userSkills ?? [], 'user');
const projectSkillsDisabled = addDisabledMetadata(data.disabledProjectSkills ?? [], 'project');
const userSkillsDisabled = addDisabledMetadata(data.disabledUserSkills ?? [], 'user');
return [...projectSkillsEnabled, ...userSkillsEnabled, ...projectSkillsDisabled, ...userSkillsDisabled];
};
// Try with project path first, fall back to global on 403/404
if (projectPath) {
try {
const url = `/api/skills?path=${encodeURIComponent(projectPath)}`;
const data = await fetchApi<{ skills?: Skill[]; projectSkills?: Skill[]; userSkills?: Skill[] }>(url);
const projectSkillsWithMetadata = addMetadata(data.projectSkills ?? [], 'project');
const userSkillsWithMetadata = addMetadata(data.userSkills ?? [], 'user');
const allSkills = [...projectSkillsWithMetadata, ...userSkillsWithMetadata];
return {
skills: data.skills ?? allSkills,
};
const url = `/api/skills?path=${encodeURIComponent(projectPath)}&includeDisabled=true`;
const data = await fetchApi<ExtendedSkillsResponse>(url);
return { skills: buildSkillsList(data) };
} catch (error: unknown) {
const apiError = error as ApiError;
if (apiError.status === 403 || apiError.status === 404) {
@@ -1020,13 +1034,8 @@ export async function fetchSkills(projectPath?: string): Promise<SkillsResponse>
}
}
// Fallback: fetch global skills
const data = await fetchApi<{ skills?: Skill[]; projectSkills?: Skill[]; userSkills?: Skill[] }>('/api/skills');
const projectSkillsWithMetadata = addMetadata(data.projectSkills ?? [], 'project');
const userSkillsWithMetadata = addMetadata(data.userSkills ?? [], 'user');
const allSkills = [...projectSkillsWithMetadata, ...userSkillsWithMetadata];
return {
skills: data.skills ?? allSkills,
};
const data = await fetchApi<ExtendedSkillsResponse>('/api/skills?includeDisabled=true');
return { skills: buildSkillsList(data) };
}
/**
@@ -1074,6 +1083,38 @@ export async function fetchSkillDetail(
return fetchApi<{ skill: Skill }>(url);
}
/**
* Validate a skill folder for import
*/
export async function validateSkillImport(sourcePath: string): Promise<{
valid: boolean;
errors?: string[];
skillInfo?: { name: string; description: string; version?: string; supportingFiles?: string[] };
}> {
return fetchApi('/api/skills/validate-import', {
method: 'POST',
body: JSON.stringify({ sourcePath }),
});
}
/**
* Create/import a skill
*/
export async function createSkill(params: {
mode: 'import' | 'cli-generate';
location: 'project' | 'user';
sourcePath?: string;
skillName?: string;
description?: string;
generationType?: 'description' | 'template';
projectPath?: string;
}): Promise<{ skillName: string; path: string }> {
return fetchApi('/api/skills/create', {
method: 'POST',
body: JSON.stringify(params),
});
}
// ========== Commands API ==========
export interface Command {
@@ -5062,3 +5103,142 @@ export async function fetchExecutionLogs(
const queryString = params.toString();
return fetchApi(`/api/orchestrator/executions/${encodeURIComponent(execId)}/logs${queryString ? `?${queryString}` : ''}`);
}
// ========== System Settings API ==========
/**
* Chinese response setting status
*/
export interface ChineseResponseStatus {
enabled: boolean;
claudeEnabled: boolean;
codexEnabled: boolean;
codexNeedsMigration: boolean;
guidelinesPath: string;
guidelinesExists: boolean;
userClaudeMdExists: boolean;
userCodexAgentsExists: boolean;
}
/**
* Fetch Chinese response setting status
*/
export async function fetchChineseResponseStatus(): Promise<ChineseResponseStatus> {
return fetchApi('/api/language/chinese-response');
}
/**
* Toggle Chinese response setting
*/
export async function toggleChineseResponse(
enabled: boolean,
target: 'claude' | 'codex' = 'claude'
): Promise<{ success: boolean; enabled: boolean; target: string }> {
return fetchApi('/api/language/chinese-response', {
method: 'POST',
body: JSON.stringify({ enabled, target }),
});
}
/**
* Windows platform setting status
*/
export interface WindowsPlatformStatus {
enabled: boolean;
guidelinesPath: string;
guidelinesExists: boolean;
userClaudeMdExists: boolean;
}
/**
* Fetch Windows platform setting status
*/
export async function fetchWindowsPlatformStatus(): Promise<WindowsPlatformStatus> {
return fetchApi('/api/language/windows-platform');
}
/**
* Toggle Windows platform setting
*/
export async function toggleWindowsPlatform(
enabled: boolean
): Promise<{ success: boolean; enabled: boolean }> {
return fetchApi('/api/language/windows-platform', {
method: 'POST',
body: JSON.stringify({ enabled }),
});
}
/**
* Codex CLI Enhancement setting status
*/
export interface CodexCliEnhancementStatus {
enabled: boolean;
guidelinesPath: string;
guidelinesExists: boolean;
userCodexAgentsExists: boolean;
}
/**
* Fetch Codex CLI Enhancement setting status
*/
export async function fetchCodexCliEnhancementStatus(): Promise<CodexCliEnhancementStatus> {
return fetchApi('/api/language/codex-cli-enhancement');
}
/**
* Toggle Codex CLI Enhancement setting
*/
export async function toggleCodexCliEnhancement(
enabled: boolean
): Promise<{ success: boolean; enabled: boolean }> {
return fetchApi('/api/language/codex-cli-enhancement', {
method: 'POST',
body: JSON.stringify({ enabled }),
});
}
/**
* Refresh Codex CLI Enhancement content
*/
export async function refreshCodexCliEnhancement(): Promise<{ success: boolean; refreshed: boolean }> {
return fetchApi('/api/language/codex-cli-enhancement', {
method: 'POST',
body: JSON.stringify({ action: 'refresh' }),
});
}
/**
* CCW Install status
*/
export interface CcwInstallStatus {
installed: boolean;
workflowsInstalled: boolean;
missingFiles: string[];
installPath: string;
}
/**
* Aggregated status response
*/
export interface AggregatedStatus {
cli: Record<string, { available: boolean; path?: string; version?: string }>;
codexLens: { ready: boolean };
semantic: { available: boolean; backend: string | null };
ccwInstall: CcwInstallStatus;
timestamp: string;
}
/**
* Fetch aggregated system status (includes CCW install status)
*/
export async function fetchAggregatedStatus(): Promise<AggregatedStatus> {
return fetchApi('/api/status/all');
}
/**
* Fetch CLI tool availability status
*/
export async function fetchCliToolStatus(): Promise<Record<string, { available: boolean; path?: string; version?: string }>> {
return fetchApi('/api/cli/status');
}