Welcome to the fourth chapter of the Plugins project!
In the previous chapter, Runtime Plugin State, we learned how to determine which plugins are "Enabled" based on user settings. We ended up with a list of LoadedPlugin objects.
However, we have a compatibility problem. The core logic of our CLI was built to understand Commands (like /help or /clear). Our new plugins provide Skills.
How do we make the old system understand the new features?
Imagine you are traveling to a different country.
Command interface).SkillDefinition).You cannot plug your device directly into the wall. You need a Travel Adapter.
This is exactly what Skill-to-Command Adaptation does. It takes the rich, specific metadata of a Plugin Skill and wraps it in a standard container that the CLI Core can understand and execute.
We have the git-helper plugin enabled. It has a skill called commit.
/commit.Command.commit skill into a generic Command object so the system can run it.To build this adapter, we need to map the concepts from one world to the other.
A Skill has specific AI-related fields: allowedTools, model, context.
A Command has generic execution fields: executionFunction, name, description.
Our adapter simply moves data from one bucket to the other.
source: 'bundled')This is a subtle but critical detail.
Why?
builtin, the system treats it like /help or /exitβboring utilities that the AI shouldn't worry about.bundled, we tell the system: "This is a smart AI feature that came with the app. Track it in analytics and let the AI model use it as a tool."We don't run this manually. It happens automatically when the application gathers all available commands.
Here is a high-level view of the input and output.
Input (Raw Skill):
{
name: 'commit',
description: 'Generate a git commit message',
allowedTools: ['git_diff'],
model: 'gpt-4'
}
Output (Adapted Command):
{
name: 'commit',
description: 'Generate a git commit message',
type: 'prompt',
source: 'bundled', // <--- The important tag
// ... hidden implementation details
}
Let's look at how the code performs this translation.
skillDefinitionToCommand function.
The magic happens in builtinPlugins.ts inside a helper function.
We take a BundledSkillDefinition (the plugin way) and return a Command (the core way).
// builtinPlugins.ts
function skillDefinitionToCommand(definition: BundledSkillDefinition): Command {
return {
type: 'prompt', // Skills are usually prompts
name: definition.name,
description: definition.description,
// ... more mapping below
}
}
Explanation: We start creating the object. We map the name and description directly.
Skills define what tools they need (like reading files or running git). We pass this through.
// ... inside the object ...
allowedTools: definition.allowedTools ?? [],
argumentHint: definition.argumentHint,
model: definition.model,
context: definition.context,
// ...
Explanation: The CLI core doesn't "execute" these itself, but it passes them to the AI Agent when the command runs.
This is the logic we discussed in the "Key Concepts" section.
// ... inside the object ...
// Crucial: We mark this as 'bundled' so it appears in tool lists
// and analytics, unlike generic 'builtin' slash commands.
source: 'bundled',
loadedFrom: 'bundled',
// ...
Explanation: This ensures that even though the code is pre-installed, the system treats it as a "Smart Feature" rather than a "System Utility."
Finally, we map the logic that determines if the command is usable.
// ... inside the object ...
// Is the user actually allowed to type this?
userInvocable: definition.userInvocable ?? true,
// Pass the isEnabled check function through
isEnabled: definition.isEnabled ?? (() => true),
getPromptForCommand: definition.getPromptForCommand,
}
}
Explanation: We preserve the isEnabled logic. If the plugin is disabled in settings (as discussed in Chapter 3), this command object will respect that.
In this chapter, we learned:
Command object to make them compatible.bundled Tag: We label these commands as source: 'bundled' to ensure they are treated as first-class AI features, not just system utilities.We now have the parts:
In the final chapter, we will see how to wire all these pieces together during the application startup.
Next Chapter: Initialization Scaffolding
Generated by Code IQ