mirror of
https://github.com/siteboon/claudecodeui.git
synced 2026-05-15 17:03:20 +00:00
Provider skills were hidden behind provider-specific filesystem rules. That made the backend and UI unable to offer one discovery path for skills. Add a normalized skills contract, provider service, and provider skills API. Keep provider-specific lookup rules inside adapters so routes and UI stay generic. Claude needs plugin handling because enabled plugins resolve through installed_plugins.json. Plugin folders can expose commands or skills, so Claude scans both forms. Claude plugin commands are namespaced to avoid collisions with user and project skills. Codex, Gemini, and Cursor adapters map their expected skill roots into the same contract. The slash menu now shows skills beside built-in and custom commands for discovery. The menu avoids mid-message activation, duplicate rows, loose namespace matches, and input overlap. Provider tests cover discovery locations and Claude plugin edge cases.
112 lines
3.8 KiB
TypeScript
112 lines
3.8 KiB
TypeScript
import type {
|
|
FetchHistoryOptions,
|
|
FetchHistoryResult,
|
|
LLMProvider,
|
|
McpScope,
|
|
NormalizedMessage,
|
|
ProviderSkill,
|
|
ProviderSkillListOptions,
|
|
ProviderAuthStatus,
|
|
ProviderMcpServer,
|
|
UpsertProviderMcpServerInput,
|
|
} from '@/shared/types.js';
|
|
|
|
//----------------- PROVIDER CONTRACT INTERFACES ------------
|
|
/**
|
|
* Main provider contract for CLI and SDK integrations.
|
|
*
|
|
* Each concrete provider owns its MCP/auth handlers plus the provider-specific
|
|
* logic for converting native events/history into the app's normalized shape.
|
|
*/
|
|
export interface IProvider {
|
|
readonly id: LLMProvider;
|
|
readonly mcp: IProviderMcp;
|
|
readonly auth: IProviderAuth;
|
|
readonly skills: IProviderSkills;
|
|
readonly sessions: IProviderSessions;
|
|
readonly sessionSynchronizer: IProviderSessionSynchronizer;
|
|
}
|
|
|
|
// ---------------------------
|
|
//----------------- PROVIDER AUTH INTERFACE ------------
|
|
/**
|
|
* Auth contract for one provider.
|
|
*
|
|
* Implementations should return a complete installation/authentication status
|
|
* without throwing for normal "not installed" or "not authenticated" states.
|
|
*/
|
|
export interface IProviderAuth {
|
|
/**
|
|
* Checks whether the provider is installed and has usable credentials.
|
|
*/
|
|
getStatus(): Promise<ProviderAuthStatus>;
|
|
}
|
|
|
|
// ---------------------------
|
|
//----------------- PROVIDER SKILLS INTERFACE ------------
|
|
/**
|
|
* Skills contract for one provider.
|
|
*
|
|
* Implementations discover provider-native skill markdown locations and return
|
|
* normalized skill records with the exact command syntax expected by that
|
|
* provider. Each skill is read from a `SKILL.md` file under its skill directory.
|
|
*/
|
|
export interface IProviderSkills {
|
|
/**
|
|
* Lists all skills visible to this provider for the optional workspace.
|
|
*/
|
|
listSkills(options?: ProviderSkillListOptions): Promise<ProviderSkill[]>;
|
|
}
|
|
|
|
// ---------------------------
|
|
//----------------- PROVIDER MCP INTERFACE ------------
|
|
/**
|
|
* MCP contract for one provider.
|
|
*
|
|
* Implementations must map provider-native MCP config formats to shared
|
|
* `ProviderMcpServer` records used by routes and frontend state.
|
|
*/
|
|
export interface IProviderMcp {
|
|
listServers(options?: { workspacePath?: string }): Promise<Record<McpScope, ProviderMcpServer[]>>;
|
|
listServersForScope(scope: McpScope, options?: { workspacePath?: string }): Promise<ProviderMcpServer[]>;
|
|
upsertServer(input: UpsertProviderMcpServerInput): Promise<ProviderMcpServer>;
|
|
removeServer(
|
|
input: { name: string; scope?: McpScope; workspacePath?: string },
|
|
): Promise<{ removed: boolean; provider: LLMProvider; name: string; scope: McpScope }>;
|
|
}
|
|
|
|
// ---------------------------
|
|
//----------------- PROVIDER SESSION INTERFACE ------------
|
|
/**
|
|
* Session/history contract for one provider.
|
|
*
|
|
* Implementations normalize provider-specific events and message history into
|
|
* shared transport shapes consumed by API routes and realtime streams.
|
|
*/
|
|
export interface IProviderSessions {
|
|
normalizeMessage(raw: unknown, sessionId: string | null): NormalizedMessage[];
|
|
fetchHistory(sessionId: string, options?: FetchHistoryOptions): Promise<FetchHistoryResult>;
|
|
}
|
|
|
|
// ---------------------------
|
|
//----------------- PROVIDER SESSION SYNCHRONIZER INTERFACE ------------
|
|
/**
|
|
* Session indexing contract for one provider.
|
|
*
|
|
* Implementations scan provider-specific session artifacts on disk and upsert
|
|
* normalized session metadata into the database. The service layer uses this
|
|
* interface for both full rescans and single-file incremental sync triggered
|
|
* by filesystem watcher events.
|
|
*/
|
|
export interface IProviderSessionSynchronizer {
|
|
/**
|
|
* Scans provider session artifacts and upserts discovered sessions into DB.
|
|
*/
|
|
synchronize(since?: Date): Promise<number>;
|
|
|
|
/**
|
|
* Parses and upserts one provider artifact file without running a full scan.
|
|
*/
|
|
synchronizeFile(filePath: string): Promise<string | null>;
|
|
}
|