source dump of claude code
at main 159 lines 5.0 kB view raw
1/** 2 * Built-in Plugin Registry 3 * 4 * Manages built-in plugins that ship with the CLI and can be enabled/disabled 5 * by users via the /plugin UI. 6 * 7 * Built-in plugins differ from bundled skills (src/skills/bundled/) in that: 8 * - They appear in the /plugin UI under a "Built-in" section 9 * - Users can enable/disable them (persisted to user settings) 10 * - They can provide multiple components (skills, hooks, MCP servers) 11 * 12 * Plugin IDs use the format `{name}@builtin` to distinguish them from 13 * marketplace plugins (`{name}@{marketplace}`). 14 */ 15 16import type { Command } from '../commands.js' 17import type { BundledSkillDefinition } from '../skills/bundledSkills.js' 18import type { BuiltinPluginDefinition, LoadedPlugin } from '../types/plugin.js' 19import { getSettings_DEPRECATED } from '../utils/settings/settings.js' 20 21const BUILTIN_PLUGINS: Map<string, BuiltinPluginDefinition> = new Map() 22 23export const BUILTIN_MARKETPLACE_NAME = 'builtin' 24 25/** 26 * Register a built-in plugin. Call this from initBuiltinPlugins() at startup. 27 */ 28export function registerBuiltinPlugin( 29 definition: BuiltinPluginDefinition, 30): void { 31 BUILTIN_PLUGINS.set(definition.name, definition) 32} 33 34/** 35 * Check if a plugin ID represents a built-in plugin (ends with @builtin). 36 */ 37export function isBuiltinPluginId(pluginId: string): boolean { 38 return pluginId.endsWith(`@${BUILTIN_MARKETPLACE_NAME}`) 39} 40 41/** 42 * Get a specific built-in plugin definition by name. 43 * Useful for the /plugin UI to show the skills/hooks/MCP list without 44 * a marketplace lookup. 45 */ 46export function getBuiltinPluginDefinition( 47 name: string, 48): BuiltinPluginDefinition | undefined { 49 return BUILTIN_PLUGINS.get(name) 50} 51 52/** 53 * Get all registered built-in plugins as LoadedPlugin objects, split into 54 * enabled/disabled based on user settings (with defaultEnabled as fallback). 55 * Plugins whose isAvailable() returns false are omitted entirely. 56 */ 57export function getBuiltinPlugins(): { 58 enabled: LoadedPlugin[] 59 disabled: LoadedPlugin[] 60} { 61 const settings = getSettings_DEPRECATED() 62 const enabled: LoadedPlugin[] = [] 63 const disabled: LoadedPlugin[] = [] 64 65 for (const [name, definition] of BUILTIN_PLUGINS) { 66 if (definition.isAvailable && !definition.isAvailable()) { 67 continue 68 } 69 70 const pluginId = `${name}@${BUILTIN_MARKETPLACE_NAME}` 71 const userSetting = settings?.enabledPlugins?.[pluginId] 72 // Enabled state: user preference > plugin default > true 73 const isEnabled = 74 userSetting !== undefined 75 ? userSetting === true 76 : (definition.defaultEnabled ?? true) 77 78 const plugin: LoadedPlugin = { 79 name, 80 manifest: { 81 name, 82 description: definition.description, 83 version: definition.version, 84 }, 85 path: BUILTIN_MARKETPLACE_NAME, // sentinel — no filesystem path 86 source: pluginId, 87 repository: pluginId, 88 enabled: isEnabled, 89 isBuiltin: true, 90 hooksConfig: definition.hooks, 91 mcpServers: definition.mcpServers, 92 } 93 94 if (isEnabled) { 95 enabled.push(plugin) 96 } else { 97 disabled.push(plugin) 98 } 99 } 100 101 return { enabled, disabled } 102} 103 104/** 105 * Get skills from enabled built-in plugins as Command objects. 106 * Skills from disabled plugins are not returned. 107 */ 108export function getBuiltinPluginSkillCommands(): Command[] { 109 const { enabled } = getBuiltinPlugins() 110 const commands: Command[] = [] 111 112 for (const plugin of enabled) { 113 const definition = BUILTIN_PLUGINS.get(plugin.name) 114 if (!definition?.skills) continue 115 for (const skill of definition.skills) { 116 commands.push(skillDefinitionToCommand(skill)) 117 } 118 } 119 120 return commands 121} 122 123/** 124 * Clear built-in plugins registry (for testing). 125 */ 126export function clearBuiltinPlugins(): void { 127 BUILTIN_PLUGINS.clear() 128} 129 130// -- 131 132function skillDefinitionToCommand(definition: BundledSkillDefinition): Command { 133 return { 134 type: 'prompt', 135 name: definition.name, 136 description: definition.description, 137 hasUserSpecifiedDescription: true, 138 allowedTools: definition.allowedTools ?? [], 139 argumentHint: definition.argumentHint, 140 whenToUse: definition.whenToUse, 141 model: definition.model, 142 disableModelInvocation: definition.disableModelInvocation ?? false, 143 userInvocable: definition.userInvocable ?? true, 144 contentLength: 0, 145 // 'bundled' not 'builtin' — 'builtin' in Command.source means hardcoded 146 // slash commands (/help, /clear). Using 'bundled' keeps these skills in 147 // the Skill tool's listing, analytics name logging, and prompt-truncation 148 // exemption. The user-toggleable aspect is tracked on LoadedPlugin.isBuiltin. 149 source: 'bundled', 150 loadedFrom: 'bundled', 151 hooks: definition.hooks, 152 context: definition.context, 153 agent: definition.agent, 154 isEnabled: definition.isEnabled ?? (() => true), 155 isHidden: !(definition.userInvocable ?? true), 156 progressMessage: 'running', 157 getPromptForCommand: definition.getPromptForCommand, 158 } 159}