Files
claudecodeui/src/components/settings/view/tabs/agents-settings/AgentsSettingsTab.tsx
Haileyesus be9fdd165e feat(skills): add provider skill management
Users need one settings surface to discover and install skills without manually navigating provider-specific directories.

Add provider-backed global skill installation for Claude, Codex, Gemini, and Cursor, while keeping OpenCode read-only because it reuses other providers' skill locations.

Add a responsive Skills settings tab with scoped discovery, search, refresh controls, markdown and folder uploads, upload feedback, and overflow-safe layouts.

Validate bundled skill files and paths before writing them, preserve scripts and assets, and cover provider discovery and installation behavior with tests.
2026-06-21 01:17:23 +03:00

106 lines
3.6 KiB
TypeScript

import { useEffect, useMemo, useState } from 'react';
import type { AgentCategory, AgentProvider } from '../../../types/types';
import type { AgentContext, AgentsSettingsTabProps } from './types';
import AgentCategoryContentSection from './sections/AgentCategoryContentSection';
import AgentCategoryTabsSection from './sections/AgentCategoryTabsSection';
import AgentSelectorSection from './sections/AgentSelectorSection';
export default function AgentsSettingsTab({
providerAuthStatus,
onProviderLogin,
claudePermissions,
onClaudePermissionsChange,
cursorPermissions,
onCursorPermissionsChange,
codexPermissionMode,
onCodexPermissionModeChange,
geminiPermissionMode,
onGeminiPermissionModeChange,
projects,
}: AgentsSettingsTabProps) {
const [selectedAgent, setSelectedAgent] = useState<AgentProvider>('claude');
const [selectedCategory, setSelectedCategory] = useState<AgentCategory>('account');
const visibleCategories = useMemo<AgentCategory[]>(() => (
selectedAgent === 'opencode'
? ['account', 'permissions', 'mcp']
: ['account', 'permissions', 'mcp', 'skills']
), [selectedAgent]);
const visibleAgents = useMemo<AgentProvider[]>(() => {
return ['claude', 'cursor', 'codex', 'gemini', 'opencode'];
}, []);
const agentContextById = useMemo<Record<AgentProvider, AgentContext>>(() => ({
claude: {
authStatus: providerAuthStatus.claude,
onLogin: () => onProviderLogin('claude'),
},
cursor: {
authStatus: providerAuthStatus.cursor,
onLogin: () => onProviderLogin('cursor'),
},
codex: {
authStatus: providerAuthStatus.codex,
onLogin: () => onProviderLogin('codex'),
},
gemini: {
authStatus: providerAuthStatus.gemini,
onLogin: () => onProviderLogin('gemini'),
},
opencode: {
authStatus: providerAuthStatus.opencode,
onLogin: () => onProviderLogin('opencode'),
},
}), [
onProviderLogin,
providerAuthStatus.claude,
providerAuthStatus.codex,
providerAuthStatus.cursor,
providerAuthStatus.gemini,
providerAuthStatus.opencode,
]);
useEffect(() => {
if (!visibleCategories.includes(selectedCategory)) {
setSelectedCategory(visibleCategories[0] ?? 'account');
}
}, [selectedCategory, visibleCategories]);
return (
<div className="-mx-4 -mb-4 -mt-2 flex min-h-[300px] min-w-0 flex-col overflow-hidden md:-mx-6 md:-mb-6 md:-mt-2 md:min-h-[500px]">
<AgentSelectorSection
agents={visibleAgents}
selectedAgent={selectedAgent}
onSelectAgent={setSelectedAgent}
agentContextById={agentContextById}
/>
<div className="flex min-w-0 flex-1 flex-col overflow-hidden">
<AgentCategoryTabsSection
categories={visibleCategories}
selectedAgent={selectedAgent}
selectedCategory={selectedCategory}
onSelectCategory={setSelectedCategory}
/>
<AgentCategoryContentSection
selectedAgent={selectedAgent}
selectedCategory={selectedCategory}
agentContextById={agentContextById}
claudePermissions={claudePermissions}
onClaudePermissionsChange={onClaudePermissionsChange}
cursorPermissions={cursorPermissions}
onCursorPermissionsChange={onCursorPermissionsChange}
codexPermissionMode={codexPermissionMode}
onCodexPermissionModeChange={onCodexPermissionModeChange}
geminiPermissionMode={geminiPermissionMode}
onGeminiPermissionModeChange={onGeminiPermissionModeChange}
projects={projects}
/>
</div>
</div>
);
}