@luan.sh/pi-skills
Tool-based skill loading for Pi
Package details
Install @luan.sh/pi-skills from npm and Pi will load the resources declared by the package manifest.
$ pi install npm:@luan.sh/pi-skills- Package
@luan.sh/pi-skills- Version
0.3.8- Published
- Sep 12, 2026
- Downloads
- not available
- Author
- cfcluan
- License
- MIT
- Types
- extension
- Size
- 27.6 MB
- Dependencies
- 3 dependencies · 2 peers
Pi manifest JSON
{
"image": "https://pi.luan.sh/media/previews/pi-skills-024c32055582.png",
"video": "https://pi.luan.sh/media/previews/pi-skills-b834329672d2.mp4",
"extensions": [
"./src/extension.ts",
"./node_modules/@luan.sh/pi-libtui/src/extension.ts"
]
}Security note
Pi packages can execute code and influence agent behavior. Review the source before installing third-party packages.
README
@luan.sh/pi-skills
@luan.sh/pi-skills adds a skill tool to Pi. The tool loads a skill's SKILL.md
into the conversation by exact name, so the model can pull in detailed
instructions only when a task needs them. The package also lists available
skills in the developer prompt, autocompletes $skill references in the
editor, and shows loaded skills as compact rows in the transcript.
Preview

Install
pi install npm:@luan.sh/pi-skills
Code Mode is bundled. It requires a Rust toolchain (https://rustup.rs). The
code-mode-host binary builds itself on first use under Pi's agent directory
(native/code-mode-host/<version>/). Set PI_CODE_MODE_HOST_BINARY to use a
prebuilt binary.
Optional companions:
pi install npm:@luan.sh/pi-xsettingsadds the/xsettingseditor for the setting listed below; without it the default applies.pi install npm:@luan.sh/pi-developer-messagesrenders the skill catalogue into the developer prompt; without it the catalogue contribution is registered but nothing displays it.pi install npm:@luan.sh/pi-custom-editorrenders known$skillreferences in the editor as pills; without it they stay plain text while typing.
How skills are discovered
At session start the package builds a map of skill name to SKILL.md path from
three sources, first match per name wins:
- Skills Pi has already loaded. Pi exposes each as a command named
skill:<name>with sourceskill; the suffix becomes the tool name. .pi/skills/under the current directory, only when the project is trusted..agents/skills/in the current directory and every ancestor up to the first directory containing.git(or the filesystem root). Each.agents/skills/tree is walked recursively; a directory containingSKILL.mdis a skill. Hidden entries andnode_modulesare skipped; symlinked directories are followed once. OnlySKILL.mdfiles count here, not loose Markdown files.
The tool cannot load a skill by arbitrary path. If the name is not in the map,
the call fails with Unknown skill "<name>".
Load a skill
Call the tool with the exact name:
{ "name": "writing-for-agents" }
The tool reads the SKILL.md, removes YAML frontmatter, and sends the body to
Pi as a steering message with custom type @luan.sh/pi-skills/loaded:
<skill>
<name>writing-for-agents</name>
<path>/absolute/path/to/SKILL.md</path>
<!-- SKILL.md body without frontmatter -->
</skill>
That message reaches the model as user context and stays in the session
history, so later turns can follow it without loading the skill again. The
tool result itself is the one-line text Loaded skill "<name>". plus a
details object (version: 1) with name, requestedName, filePath,
directory, hasSupportingFiles, supportingFiles,
supportingFilesTruncated, frontmatterRemoved, sourceChars,
loadedChars, loadedTokens, and instructions.
Frontmatter
If SKILL.md starts with ---, everything up to and including the next line
beginning with --- is dropped. A file with an opening delimiter but no
closing one is sent unchanged.
Supporting files
The loader walks the skill directory recursively and records relative paths of
every file except SKILL.md and agents/openai.yaml. At most 256 paths are
recorded; supportingFilesTruncated is true when more exist. File contents
are never read or appended.
When at least one supporting file exists, the body ends with:
Skill directory: /absolute/path/to/skill
The line is omitted when SKILL.md is alone or when agents/openai.yaml is
the only companion. Use the directory to open scripts, assets, or references
yourself.
Code Mode
The skill tool registers a Code Mode adapter, so when Code Mode lifts it, the
model calls tools.skill({ name }) inside exec instead of calling skill
directly. Code Mode decides which of the two is active; this package only
supplies the adapter.
Prompt catalogue
When skills exist that Pi allows the model to invoke, the package contributes
a <skills_instructions> block (priority 50) listing each skill's name and
description, plus short rules: use the smallest set of skills, call
tools.skill with the exact name inside exec before acting, load only the
supporting files the task needs. Skill filesystem paths are never included.
Skills flagged disableModelInvocation are omitted.
Settings
Namespace @luan.sh/pi-skills. Edited via /xsettings when @luan.sh/pi-xsettings is
installed; otherwise the default applies.
| Key | Default | Meaning |
|---|---|---|
catalogVisibility |
when-active |
when-active: include the catalogue when skill or exec is an active tool. always: include it in every prompt. off: never include it. |
Transcript rendering
The steering message is hidden from the transcript. The tool call renders as a
single row: a lightbulb marker, Skill, the skill name, and an estimated token
count (123 tokens or 1.2k tokens). Expanding the row shows the loaded body
as Markdown; collapsing hides it again. Failures render as Skill failed with
the error text.
In submitted user messages, $name tokens that match a discovered skill are
drawn as pills labelled with the skill's display name. References inside inline
or fenced Markdown code stay literal. Pills are suppressed while an overlay is
open or a selection is active.
Editor autocomplete
Type $ at the start of the input or after whitespace to list discovered
skills; continue typing to filter by substring, case-insensitive. Accepting a
suggestion replaces the $query token with $name and keeps any text after
the cursor. Each item shows the skill description in muted text when the skill
has one.
Pill labels come from interface.display_name in the skill's
agents/openai.yaml when present (plain or quoted scalars only), otherwise the
skill name. A missing or unreadable file never prevents loading.
This package registers no keyboard actions.
Layout
| Responsibility | File |
|---|---|
| Pi registration and lifecycle | src/extension.ts |
| Skill discovery, frontmatter, supporting files, display names | src/skills.ts |
skill tool schema, execution, and result details |
src/tools/skill/definition.ts |
| Transcript row rendering | src/tools/skill/presentation.ts |
| Steering message wrapper | src/loaded-skill-context.ts |
| Developer prompt catalogue | src/prompt.ts |
| Code Mode adapter | src/code-mode-adapter.ts |
| Settings declaration | src/contributions/xsettings.ts |
| Optional editor pills | src/contributions/editor-highlights.ts |
$skill autocomplete |
src/ui/autocomplete.ts |
| Transcript pills for user messages | src/ui/transcript-skills.ts |
| Public exports | src/index.ts |
Troubleshooting
- Unknown skill: the name must match a
skill:<name>command or a discoveredSKILL.mdexactly, including case and punctuation. - No catalogue in the prompt: check
catalogVisibility, make sureskillorexecis active when it iswhen-active, and install@luan.sh/pi-developer-messages. - Loaded text not visible: expand the skill row; the text is already in model context.
Develop
Source: https://github.com/luan/agents, directory
harnesses/pi/agent/packages/pi-skills. Run bun run typecheck and
bun test test in that directory.