Files
orca/src/shared/skill-install-providers.ts

116 lines
3.2 KiB
TypeScript

/**
* Agents Orca can place an installed skill for.
*
* `null` segments mean the agent reads the canonical `.agents/skills` root at
* that scope, so it needs no placement of its own. Ids match the detection ids
* in `tui-agent-config.ts` so a detected agent maps straight to a destination.
*/
export type SkillInstallProviderId =
| 'codex'
| 'claude'
| 'cursor'
| 'gemini'
| 'droid'
| 'continue'
| 'trae'
| 'grok'
| 'aug'
export type SkillInstallProviderDefinition = {
id: SkillInstallProviderId
displayName: string
globalSegments: readonly string[] | null
workspaceSegments: readonly string[] | null
}
export const SKILL_INSTALL_PROVIDERS: readonly SkillInstallProviderDefinition[] = [
// Why: Codex reads the canonical .agents/skills root at both scopes.
{
id: 'codex',
displayName: 'Codex',
globalSegments: null,
workspaceSegments: null
},
{
id: 'claude',
displayName: 'Claude Code',
globalSegments: ['.claude', 'skills'],
workspaceSegments: ['.claude', 'skills']
},
// Why: Cursor and Gemini read the canonical root inside a project but keep
// their own home directory, so they only need a placement at global scope.
{
id: 'cursor',
displayName: 'Cursor',
globalSegments: ['.cursor', 'skills'],
workspaceSegments: null
},
{
id: 'gemini',
displayName: 'Gemini CLI',
globalSegments: ['.gemini', 'skills'],
workspaceSegments: null
},
{
id: 'droid',
displayName: 'Droid',
globalSegments: ['.factory', 'skills'],
workspaceSegments: ['.factory', 'skills']
},
{
id: 'continue',
displayName: 'Continue',
globalSegments: ['.continue', 'skills'],
workspaceSegments: ['.continue', 'skills']
},
{
id: 'trae',
displayName: 'Trae',
globalSegments: ['.trae-cn', 'skills'],
workspaceSegments: ['.trae', 'skills']
},
{
id: 'grok',
displayName: 'Grok',
globalSegments: ['.grok', 'skills'],
workspaceSegments: ['.grok', 'skills']
},
{
id: 'aug',
displayName: 'Augment',
globalSegments: ['.augment', 'skills'],
workspaceSegments: ['.augment', 'skills']
}
]
const PROVIDERS_BY_ID = new Map(SKILL_INSTALL_PROVIDERS.map((provider) => [provider.id, provider]))
export function isSkillInstallProviderId(value: string): value is SkillInstallProviderId {
return PROVIDERS_BY_ID.has(value as SkillInstallProviderId)
}
export function skillInstallProvider(
id: SkillInstallProviderId
): SkillInstallProviderDefinition | undefined {
return PROVIDERS_BY_ID.get(id)
}
/** Detected agents Orca can actually place skills for, in registry order. */
export function installableSkillProviders(
detectedProviders: readonly string[]
): SkillInstallProviderDefinition[] {
const detected = new Set(detectedProviders)
return SKILL_INSTALL_PROVIDERS.filter((provider) => detected.has(provider.id))
}
/**
* An explicit choice is authoritative, including agents the target may install
* later. Removal passes no choice so it can clean every previously used root.
*/
export function selectedOrDetectedSkillProviders(
detectedProviders: readonly string[],
selectedProviders: readonly string[] | undefined
): readonly string[] {
return selectedProviders ?? detectedProviders
}