v.skills #
Module skills owns the bundled agent skills that ship with the V compiler and the rules for installing them into a project or a user-wide directory.
A skill is a directory under vlib/v/skills/<name>/ holding a SKILL.md with YAML front matter (name, description) plus optional extra files in references/ and scripts/. The bundles are read from the V source tree at run time rather than embedded into a binary, so a skill can be reviewed, edited and diffed in the repository, and adding one needs no rebuild.
Both v skills (cmd/tools/vskills) and the v_skills MCP tool (cmd/tools/vmcp) go through this module, so the catalog, the layout and the install rules cannot drift apart.
Constants #
const entry_file = 'SKILL.md'
entry_file is the file every skill directory must contain. It is both the entry point an agent reads first and the marker catalog looks for, so a directory without one is never offered as a skill.
const max_name_length = 64
max_name_length is the longest a skill name may be, per the Agent Skills spec.
const max_description_length = 1024
max_description_length is the longest a skill description may be. The description is the only part an agent sees before it decides to load the skill, so the spec puts a ceiling on it; a longer one would be truncated anyway.
const project_dir = '.agents/skills'
project_dir is the per-project install location, relative to a project root. It matches the layout opencode, Claude Code and friends already scan.
const global_dir = '.agents/skills'
global_dir is the install location relative to the user's home directory, so a skill installed with --global applies to every project.
fn bundled_root #
fn bundled_root(vroot string) string
bundled_root returns <vroot>/vlib/v/skills.
fn catalog #
fn catalog(vroot string) []Skill
catalog returns every skill bundled with the compiler, sorted by name.
A directory qualifies when it contains SKILL.md; a directory without one is skipped rather than reported, so an in-progress bundle cannot break v skills list.
fn find #
fn find(vroot string, name string) ?Skill
find returns the bundled skill called name, or none for an invalid name.
fn human_size #
fn human_size(bytes int) string
human_size renders a byte count compactly, for file listings.
fn install #
fn install(skill Skill, dir string, opts InstallOptions) !InstallResult
install copies skill into dir.
The bundle is validated first, so a skill whose front matter does not satisfy the spec is refused here rather than copied somewhere an agent will read it. Its name must match the bundle and its destination must be an immediate child of the install directory. File paths must stay within that skill and its bundle and refer to regular files.
Without force, an already installed skill is skipped and reported as such rather than overwritten: an agent must not silently discard local edits to a checked-in skill. dry_run computes the same result without writing.
A symlink sitting where the skill would go is refused. os.is_dir and os.rmdir_all both follow their argument, so --force over a link would list and delete the target's contents rather than the link. That is how an install turns into an unrelated directory wipe.
fn installed #
fn installed(dir string) []string
installed returns the names installed in dir, sorted.
fn invalid_bundled #
fn invalid_bundled(vroot string) []string
invalid_bundled returns the bundled skills whose front matter does not satisfy the Agent Skills spec, as "name: reason" lines.
catalog skips what it cannot read, which is right for a listing but would leave a malformed bundle invisible. v skills list reports these instead, so a broken bundle shows up before someone tries to install it.
fn list_files #
fn list_files(directory string) []string
list_files returns every file below directory as paths relative to it, with SKILL.md first. The rest are sorted so a reinstall reports a stable order.
fn out_of_date #
fn out_of_date(vroot string, dir string) []string
out_of_date returns the bundled skills installed in dir whose installed copy no longer matches the bundled one. v skills list reports these so an agent can offer v skills add --force instead of acting on stale guidance.
fn parse_front_matter #
fn parse_front_matter(content string) ?map[string]string
parse_front_matter reads the name: and description: keys from a SKILL.md YAML front matter block.
The parser is deliberately narrow: it accepts the leading --- block, then reads the flat key: value keys a skill needs. A missing block or a missing key yields none rather than a guessed value, so v skills add never installs a bundle whose metadata it could not read.
fn relative_to #
fn relative_to(base string, path string) string
relative_to renders path relative to base with forward slashes, for readable tool output. The base itself reads as ., and a path outside base is returned unchanged.
fn remove #
fn remove(dir string, name string) !RemoveResult
remove deletes an installed skill directory. It reports removed: false when nothing was installed under that name. Names follow validate_name; symlinks and targets outside the immediate install directory are refused.
fn strip_bom #
fn strip_bom(content string) string
strip_bom removes a leading byte order mark from content.
fn target_dir #
fn target_dir(scope Scope, base string) string
target_dir returns the directory a skill installs into for scope. base is the project root for Scope.project_root and is ignored for Scope.home_dir.
fn validate_bundle #
fn validate_bundle(directory string) !string
validate_bundle checks one skill directory against the Agent Skills spec and returns its name, or an error naming the first rule it breaks.
The name is what an agent matches a task against and the directory is what v skills remove addresses, so the spec requires the two to agree; a bundle where they disagree is one an agent will load under a name the installer cannot find again.
Installing checks this too, so a bundle that fails is refused rather than copied somewhere an agent will read it.
fn validate_name #
fn validate_name(name string) !string
validate_name checks one skill name and returns it, or an error naming the rule it breaks.
enum Scope #
enum Scope {
// project_root installs under `<project root>/.agents/skills`, which is
// committed with the repository and shared with the whole team.
project_root
// home_dir installs under `<home>/.agents/skills`, which applies to every
// project on the machine for the current user.
home_dir
}
Scope selects where a skill is installed.
struct InstallOptions #
struct InstallOptions {
pub:
// force overwrites an already installed skill of the same name.
force bool
// dry_run reports what would happen without touching the filesystem.
dry_run bool
}
InstallOptions is what install needs beyond the skill and the target.
struct InstallResult #
struct InstallResult {
pub:
skill string
// path is the installed `SKILL.md`.
path string
// skipped is true when the skill was already installed and `force` was off.
skipped bool
// dry_run is true when nothing was written.
dry_run bool
pub mut:
// written lists the files copied, relative to the skill directory.
written []string
}
InstallResult reports what one install call did.
struct RemoveResult #
struct RemoveResult {
pub:
skill string
// path is the removed directory.
path string
removed bool
}
RemoveResult reports what one remove call did.
struct Skill #
struct Skill {
pub:
name string
description string
// directory is the path of the bundled skill directory.
directory string
// files are the skill's files relative to `directory`, `SKILL.md` first.
files []string
}
Skill describes one bundled skill directory.
- README
- Constants
- fn bundled_root
- fn catalog
- fn find
- fn human_size
- fn install
- fn installed
- fn invalid_bundled
- fn list_files
- fn out_of_date
- fn parse_front_matter
- fn relative_to
- fn remove
- fn strip_bom
- fn target_dir
- fn validate_bundle
- fn validate_name
- enum Scope
- struct InstallOptions
- struct InstallResult
- struct RemoveResult
- struct Skill