pub struct Installer { /* private fields */ }Expand description
Installs embedded skills into agent directories.
Everything is set once, on the way in:
use std::sync::LazyLock;
use include_dir::{Dir, include_dir};
use skill_embed::{Installer, SkillSet};
static SKILLS_DIR: Dir<'static> = include_dir!("$CARGO_MANIFEST_DIR/skills");
static SKILLS: LazyLock<Installer> = LazyLock::new(|| {
Installer::new(SkillSet::from_include_dir(&SKILLS_DIR).expect("valid skills"))
.with_tool_name("mytool")
.with_version(env!("CARGO_PKG_VERSION"))
});Implementations§
Source§impl Installer
impl Installer
Sourcepub fn run(&self, args: &[String]) -> Result<()>
pub fn run(&self, args: &[String]) -> Result<()>
Executes the skill command.
args are the arguments after the command name, so mytool skill install --scope user passes ["install", "--scope", "user"].
It never exits the process, so a driver keeps control. It is what the
adapters call underneath, and what Installer::intercept wraps.
§Errors
Returns Error::Help when help was asked for, which is not a failure.
Everything else is.
Sourcepub fn intercept(&self) -> Option<ExitCode>
pub fn intercept(&self) -> Option<ExitCode>
Runs the skill command when it is the first argument, and reports the exit code the process should end with.
None means the arguments were not for this command and the tool
should carry on:
use std::process::ExitCode;
use std::sync::LazyLock;
fn main() -> ExitCode {
if let Some(code) = SKILLS.intercept() {
return code;
}
// the rest of your tool
ExitCode::SUCCESS
}The guard is the first argument and nothing else. mytool skill install
reaches it. mytool -v skill install does not. Put your own flags after
the subcommand, or before a normal run.
Sourcepub fn intercept_args<S: AsRef<OsStr>>(&self, argv: &[S]) -> Option<ExitCode>
pub fn intercept_args<S: AsRef<OsStr>>(&self, argv: &[S]) -> Option<ExitCode>
Installer::intercept against arguments the caller supplies, starting
with the program name.
Source§impl Installer
impl Installer
Sourcepub fn new(set: SkillSet) -> Self
pub fn new(set: SkillSet) -> Self
Creates an installer for a set of embedded skills.
The tool name defaults to the running binary’s name, the subcommand to
skill, the scope to Scope::Project and the agents to detected.
Sourcepub fn with_tool_name(self, name: impl Into<String>) -> Self
pub fn with_tool_name(self, name: impl Into<String>) -> Self
Sets the name recorded in installed skills and shown in help.
An empty name is not a name, and is passed over. A skill stamped
x-embedded-by: "" is foreign to every run, including this tool’s own.
Sourcepub fn with_version(self, version: impl Into<String>) -> Self
pub fn with_version(self, version: impl Into<String>) -> Self
Sets the version recorded in installed skills.
Sourcepub fn with_command_name(self, name: impl Into<String>) -> Self
pub fn with_command_name(self, name: impl Into<String>) -> Self
Sets the subcommand name Installer::run and Installer::intercept
answer to. It defaults to skill.
Sourcepub fn with_agents(self, agents: impl IntoIterator<Item = Agent>) -> Self
pub fn with_agents(self, agents: impl IntoIterator<Item = Agent>) -> Self
Restricts the agents the tool offers. It defaults to every built-in one.
Sourcepub fn with_default_agents(
self,
agents: impl IntoIterator<Item = AgentSelector>,
) -> Self
pub fn with_default_agents( self, agents: impl IntoIterator<Item = AgentSelector>, ) -> Self
Sets the agents used when --agent is not given. It defaults to
AgentSelector::Detected.
detected keeps the agents whose directory is already there, and falls
back to all when it finds none. In a fresh repository that is two
directories and reaches everything. In a home directory it is the agents
in use, rather than six directories of which most are litter. Pass
Agent::GITHUB_COPILOT for the default gh skill install uses.
Sourcepub fn with_default_scope(self, scope: Scope) -> Self
pub fn with_default_scope(self, scope: Scope) -> Self
Sets the scope used when --scope is not given. It defaults to
Scope::Project, matching gh skill install.
Sourcepub fn with_project_root(self, dir: impl Into<PathBuf>) -> Self
pub fn with_project_root(self, dir: impl Into<PathBuf>) -> Self
Sets the directory project scope resolves against.
Without it the root is searched for: the walk starts at the working directory and stops at the repository root, and the first directory already holding an agent directory wins. Outside a repository the working directory is the only candidate. The home directory is refused, since the user scope directories live there.
Sourcepub fn with_metadata(self, on: bool) -> Self
pub fn with_metadata(self, on: bool) -> Self
Controls whether installed skills carry x-embedded-* frontmatter. It
is on by default.
Without it, install cannot tell an outdated copy from an edited one and
every existing directory reads as State::Foreign.
Sourcepub fn with_executable(
self,
rule: impl Fn(&str, &[u8]) -> bool + Send + Sync + 'static,
) -> Self
pub fn with_executable( self, rule: impl Fn(&str, &[u8]) -> bool + Send + Sync + 'static, ) -> Self
Decides which files are written with the executable bit.
An embedded file carries no mode, so the default marks any file starting
with a #! shebang.
Sourcepub fn with_output(self, w: impl Write + Send + 'static) -> Self
pub fn with_output(self, w: impl Write + Send + 'static) -> Self
Sets where Installer::run writes what was asked for: the report, and
the help text when help was requested. It defaults to standard output.
Sourcepub fn with_error_output(self, w: impl Write + Send + 'static) -> Self
pub fn with_error_output(self, w: impl Write + Send + 'static) -> Self
Sets where Installer::run writes diagnostics: the message for a bad
flag or an unknown subcommand, and the usage that goes with it. It
defaults to standard error.
The two are separate for the sake of redirection. mytool skill list > skills.txt puts the list in the file and the complaint on the terminal.
Sourcepub fn command_name(&self) -> &str
pub fn command_name(&self) -> &str
The subcommand name.
Sourcepub fn default_scope(&self) -> Scope
pub fn default_scope(&self) -> Scope
The scope used when none is given.
Sourcepub fn default_agents(&self) -> &[AgentSelector]
pub fn default_agents(&self) -> &[AgentSelector]
The agents used when --agent is not given.
Sourcepub fn agent_choices(&self) -> String
pub fn agent_choices(&self) -> String
Renders the --agent help string, such as {a|b|c}.
An adapter that writes its own flag help uses it to name the same agents.
Sourcepub fn write_report(&self, text: &str) -> Result<()>
pub fn write_report(&self, text: &str) -> Result<()>
Writes a report to wherever Installer::with_output points.
A front end that renders its own output calls it, so that one setting
reaches every one of them. crate::render_results and
crate::render_status are what it is given.
§Errors
Whatever the writer refused.
Sourcepub fn targets(&self, options: &InstallOptions) -> Result<Vec<InstallTarget>>
pub fn targets(&self, options: &InstallOptions) -> Result<Vec<InstallTarget>>
Resolves the destination directories for options.
At project scope every agent but Claude Code shares .agents/skills, so
those are merged into one target and a skill is never written there
twice.
§Errors
Fails when --agent names nothing, when the project root cannot be
found, or when a project scope destination would land outside it.
Sourcepub fn status(&self, options: &InstallOptions) -> Result<Vec<InstallStatus>>
pub fn status(&self, options: &InstallOptions) -> Result<Vec<InstallStatus>>
Reports what is installed where, without changing anything.
It also reports what a destination holds that this tool wrote and the
binary no longer carries, as State::Orphaned.
§Errors
Fails for the reasons Installer::targets does, when a named skill is
not embedded, or when a destination cannot be read at all.
Sourcepub fn install(
&self,
options: &InstallOptions,
) -> (Vec<InstallResult>, Result<()>)
pub fn install( &self, options: &InstallOptions, ) -> (Vec<InstallResult>, Result<()>)
Writes the selected skills into the resolved targets.
A run over the whole set also removes what this tool wrote and the
binary no longer carries, reported as State::Orphaned. Nothing else
would ever reach those directories again. A run naming skills installs
those and sweeps nothing it was not told to.
A destination this tool did not write, or one edited after it did, is
left alone unless InstallOptions::force is set. Those skills come
back as Action::Skipped with a reason, exactly as
Installer::uninstall reports them, and the outcome is
Error::NeedsForce. One blocked destination does not stop the others
from being written.
The results are worth reporting even when the outcome is an error. They describe everything that happened before it.
Sourcepub fn uninstall(
&self,
options: &InstallOptions,
) -> (Vec<InstallResult>, Result<()>)
pub fn uninstall( &self, options: &InstallOptions, ) -> (Vec<InstallResult>, Result<()>)
Removes the selected skills from the resolved targets. Skills this tool
did not install are left alone unless InstallOptions::force is set.
A run over the whole set also removes what this tool wrote and the binary no longer carries, so that a full uninstall leaves nothing of this tool’s behind.
The results are worth reporting even when the outcome is an error.
Source§impl Installer
impl Installer
Sourcepub fn usage(&self) -> String
pub fn usage(&self) -> String
The help text for the skill command. A tool that writes its own help can print it, so that the two agree.
Sourcepub fn usage_hint(&self) -> String
pub fn usage_hint(&self) -> String
One line naming the skill command, for a tool whose own help would otherwise never mention it.
eprintln!("{}", skills.usage_hint());Run "mytool skill" to install the 2 agent skills embedded in mytool.