Skip to main content

Crate agent_skill_installer

Crate agent_skill_installer 

Source
Expand description

§agent-skill-installer

crates.io docs.rs CI License: LGPL-3.0-or-later MSRV: 1.95

A Rust library that installs an Agent Skill shipped inside your program into the skill directories of the AI coding tools on the user’s machine, so those tools know what your program is for and how to use it. It finds the tools that are installed, plans the fewest copies that reach all of them, installs only after the user agreed, and later keeps its own unchanged copies current without asking. The crate decides what would happen and carries it out; your program asks the user in its own CLI, TUI or GUI.

§Features

  • Only where a tool is present. A tool counts as installed when its home directory exists, and a skill directory is only created inside such a home. The crate never creates a tool’s home.
  • The fewest copies that reach every tool. The planner compares every combination of directories and picks the one that leaves the fewest tools seeing the skill twice, then needs the fewest copies, and names any tool that will see it twice anyway. Symlinks are resolved, so two names for one directory are one target.
  • Consent first. The first installation into a directory happens only after the user agreed. A “no” is kept in a file your program chooses, and that directory is not asked about again.
  • Silent updates of its own work only. The skill’s frontmatter records which program installed it, the version and a digest over every file. A newer program updates an unchanged copy without asking, also inside a git working tree, which is reported with its root.
  • Never half-written. A new version is written beside the skills directory and renamed into place; on a failure the old one is put back.
  • Extensible. The built-in list covers six tools; a program can add its own tools and directories, and the planner treats them like the built-in ones.
  • Small. Depends on semver and sha2 only. No YAML parser, no network access, no terminal input or output.

§Toolchain support

The minimum supported Rust version (MSRV) is 1.95. The crate targets a rolling window of stable Rust minus four releases, reviewed with each minor release. MSRV increases are made only in minor releases and noted in the changelog. CI builds and tests the crate against exactly the version in rust-version on every change.

§Installation

cargo add agent-skill-installer

or in Cargo.toml:

[dependencies]
agent-skill-installer = "0.1"

§Quick start

use agent_skill_installer::{AgentSkill, Program, Skill};

let skill = Skill::new(
    "devserial",
    "Serial terminal with a SQLite-buffered capture. Use it to read, search and write serial ports.",
    "# devserial\n\nRead the capture with `devserial read PORT --tail 50`.\n",
)?
.with_field("license", "GPL-3.0-or-later")?
.with_file("references/cli.md", "# Commands\n")?;
let program = Program::new("devserial", env!("CARGO_PKG_VERSION"))?;
let agent = AgentSkill::new(skill, program, "/path/in/your/data/dir/skill-consent")?;

// At every start: keep earlier installations current. Asks nobody.
agent.update()?;

// Once, when a place exists nobody has decided on: ask in your own surface.
if let Some(offer) = agent.offer()? {
    for step in offer.steps() {
        println!("{} (read by {})", step.place.display(), step.tools.join(", "));
    }
    let agreed = true; // the user's answer
    if agreed {
        offer.accept()?;
    } else {
        offer.decline()?; // these places are not offered again
    }
}

A command line can show agent.status(), install with agent.install_everywhere(), which also goes where the user declined before, and take the skill out with agent.remove().

status writes nothing. It returns one step per place with what is there (Found) and what would happen (Action): install, update, nothing and why, or a problem to report. The Plan inside it lists the installed tools, the tools that will see the skill twice, and any tool that cannot be served, with the reason. A program that wants to decide place by place uses agent.installer(), whose assess and apply take an Approval of its own.

To add a tool the built-in list leaves out:

use agent_skill_installer::{Catalog, Location, SkillDir, Tool};

let mut catalog = Catalog::builtin();
catalog
    .add_dir(
        SkillDir::new("kiro", Location::home(".kiro/skills"))
            .requires(Location::home(".kiro")),
    )?
    .add_tool(Tool::new("kiro", "Kiro", Location::home(".kiro")).reads(["kiro"]))?;

AgentSkill::with_catalog(catalog) then uses it.

Consent is given for a place, not for a version. What assess decides for a place depends on what it finds there under the skill’s name:

FoundAction
Nothing, the place is plannedInstall once the user approves (Action::Install)
Nothing, the user declined the placeNothing (Why::Declined)
This program’s copy, unchanged, olderUpdate without asking (Action::Update)
This program’s copy, unchanged, same or newerNothing; an older program never downgrades
This program’s copy, changed by somebodyReported (Problem::Modified); replaced only when listed in Approval::overwrite
A skill of that name that is not this program’sReported (Problem::Foreign), never replaced

A copy counts as this program’s when its metadata block carries the record the crate wrote, in the shape the crate writes it:

metadata:
  installed-by: "devserial"
  installed-version: "0.3.0"
  content-digest: "sha256:…"

The digest covers every file of the skill directory with its own value blanked. Any edited, added or removed file makes the copy “changed”; .DS_Store and Thumbs.db do not. Copies of this program in places the plan no longer chooses, for example for a tool uninstalled since, are still updated and removed.

§Supported tools

The built-in catalog, from docs/tools.md. “Measured” means the behaviour was observed with the tool on a test machine; “documented” means it follows the vendor’s documentation only.

ToolInstalled when this existsSkill directories it readsEvidence
Claude Code~/.claude ($CLAUDE_CONFIG_DIR when set)~/.claude/skills ($CLAUDE_CONFIG_DIR/skills)measured
Codex~/.codex ($CODEX_HOME when set)~/.agents/skillsdocumented
Gemini CLI~/.gemini~/.gemini/skills, ~/.agents/skillsmeasured
GitHub Copilot~/.copilot ($COPILOT_HOME when set)~/.copilot/skills ($COPILOT_HOME/skills), ~/.agents/skills; in VS Code also ~/.claude/skillsdocumented
Cursor~/.cursor~/.cursor/skills, ~/.agents/skills, ~/.claude/skillsdocumented
OpenCode~/.config/opencode ($XDG_CONFIG_HOME/opencode when set)~/.config/opencode/skills ($XDG_CONFIG_HOME/opencode/skills), ~/.claude/skills, ~/.agents/skillsdocumented; XDG_CONFIG_HOME from secondary sources

Claude Code reads only ~/.claude/skills, and Gemini CLI does not read it, so a machine with both needs two copies. With Claude Code and Codex installed, Cursor, OpenCode and Copilot in VS Code read both copies when they are installed too; the plan names them in Plan::duplicated.

§Limits

  • Windows paths follow the tools’ documentation, which only ever says ~. The test suite runs on Windows in CI, but no tool’s behaviour was measured there, and the symlink cases are tested on Unix only.
  • Four of the six tools are documented, not measured: Codex, Copilot, Cursor and OpenCode.
  • Two programs writing the same skill at the same moment are not coordinated. Each write is atomic, so the place ends up with one complete version, but one of the two may report a failure.
  • An interrupted process can leave a .asi-* staging directory beside the skills directory. The next write of the same skill removes it once it is an hour old; a younger one may belong to a write in progress.
  • Package managers do not install skills. That would be installing without consent; the program asks at runtime instead.

§Contributing

See CONTRIBUTING.md for the workflow and the checks CI runs, and SECURITY.md for reporting a vulnerability.

§License

LGPL-3.0-or-later. See LICENSE.

Re-exports§

pub use semver;

Structs§

AgentSkill
A program’s skill on the user’s machine.
Approval
What the user agreed to.
Assessment
A plan and the step for every place it concerns.
Catalog
The tools and directories the planner knows.
Consent
The declined targets, and the file they are kept in.
Environment
A home directory and the environment variables that relocate tools.
Installer
A skill, the program it belongs to, and the machine to put it on.
Location
Where something lives: below the home directory, unless a variable moves it.
Offer
Places nobody has decided on, to be put to the user once.
Outcome
What happened in one place.
Plan
What the planner found and proposes.
Program
The program shipping the skill: its name goes into the record, and its version decides whether an installed copy is older.
Skill
A skill: its frontmatter, its instructions and any further files.
SkillDir
A directory some tool reads skills from.
Step
One place, what is there and what would happen.
Target
One directory the skill would be written to.
Tool
A tool that reads skills.
Unserved
A tool the planner found installed but cannot serve, and why.

Enums§

Action
What the installer would do in a place, and why.
CatalogError
Why an entry could not be added to a catalog.
Done
What happened.
Error
Why the machine could not be looked at or the answers not be kept.
Found
What is in a place under the skill’s name.
NameError
Why a skill name was refused.
PlanError
Why no plan could be made.
Problem
Why something needs the user’s attention.
SkillError
Why a skill was refused.
Why
Why nothing is done.

Constants§

SKILL_FILE
The file every skill has, at the top of its directory.

Functions§

plan
Work out where a skill should go.
validate_name
Check a skill name against the Agent Skills specification.