README
A Claude-style internal skill system for LM Studio. When the plugin is active, it automatically supplies skill context under the hood, so users do not need to paste skill instructions into the system prompt.
lms-plugin-skills discovers local skill directories, deterministically routes the current request to the smallest relevant skill set, and provides tools for reading skill instructions and supporting files.
A skill is a directory containing a SKILL.md file. Normal requests receive only routed candidates; explicit $skill-name activations expand the matching SKILL.md body before the model begins reasoning.
Forked from dirty-data/skills
$skill-name to expand that skill’s SKILL.md body into the prompt before the model starts reasoning.read_skill_file("skill-name") checks the matching skill directory directly before falling back to broader scans.run_command is disabled by default and guarded by explicit safety modes.~/.lmstudio/plugin-data/lms-skills/settings.json so it survives new chats.LMS_SKILLS_DEBUG=1.When Internal Skills Context is enabled, the plugin prepends hidden skills guidance before the current user message reaches the model.
The internal context is routed, not a broad skill dump. The plugin scores skill metadata against the current request and injects only the top routed candidates:
<skills_runtime_context> ... </skills_runtime_context> <routed_skills> <skill rank="1" confidence="high" score="86"> <n>skill-name</n> <description>...</description> <why>frontmatter:description_token=...</why> <environment>WSL</environment> <location>WSL:/home/user/.agents/skills/skill-name/SKILL.md</location> </skill> </routed_skills>
To avoid context overload, the plugin injects:
Manual system-prompt instructions are optional and should only be used for extra project-specific behavior beyond the plugin defaults.
Users can explicitly activate a skill in a prompt with $skill-name notation:
Use $example-skill to create the helper.
When the preprocessor sees $example-skill, it resolves that skill directly and expands the matching SKILL.md body into a <skill_invocation_packet> before the request reaches the model.
Explicit activations tell the model:
SKILL.md body has already been expanded into a <skill_invocation_packet> before model reasoning,run_command must not be used for exploration,$skill tokens should be searched with list_skills before proceeding.The notation accepts lowercase skill-like names beginning with a lowercase letter and containing lowercase letters, numbers, ., _, or -. Uppercase shell variables such as $HOME are ignored so command payloads are not misread as skill activations. Examples:
$docx $example-skill $my.custom_skill
Explicit activation works even when the regular internal context is disabled, because the user is directly asking for a skill by name. Unlike normal routed candidates, explicit activations expand the selected SKILL.md body immediately; normal routing still uses progressive disclosure and expects the model to call read_skill_file for routed candidates. For explicit activations, the preprocessor removes the $skill-name token from the model-facing task payload and wraps the remaining user text in <task_payload for_expanded_skills="..."> so the model applies the expanded skill before interpreting command-looking text.
| Tool | Purpose |
|---|---|
list_skills | List/search skills. mode: "route" applies the deterministic router used by prompt injection. |
read_skill_file | Read SKILL.md or another file inside a skill directory. Defaults to SKILL.md. |
list_skill_files | Explore files inside a skill directory. |
run_command | Execute shell commands only when explicitly enabled by the command safety setting. Disabled by default. |
The plugin can resolve skills in different filesystem/runtime environments:
| Mode | Behavior |
|---|---|
Windows | Resolve paths and read skills through the native Windows filesystem. |
WSL | Resolve Linux paths and read skills through WSL/bash semantics. |
Both | Scan Windows and WSL separately and return environment-labeled paths. |
Environment-labeled paths look like:
Windows:C:\Users\you\.lmstudio\skills\docx\SKILL.md WSL:/home/you/.agents/skills/docx/SKILL.md
A skill is any subdirectory inside a configured skills path that contains a SKILL.md file.
~/.lmstudio/skills/ ├── docx/ │ ├── SKILL.md # entry point, required │ ├── scripts/ │ │ └── helper.py │ └── templates/ │ └── base.docx ├── pptx/ │ ├── SKILL.md │ └── editing.md └── my-custom-skill/ ├── SKILL.md └── skill.json # optional metadata override
SKILL.md is the required entry point for a skill. Claude-style YAML frontmatter at the top of SKILL.md is used as high-level routing metadata for <routed_skills>.
--- name: example-skill description: Use this skill when the user needs a clear, complete example workflow. when_to_use: Trigger when the request matches the workflow this skill implements. tags: [examples, workflow] disable-model-invocation: false --- # Example Skill Detailed instructions go here. This body is loaded when the model calls `read_skill_file`.
Frontmatter behavior:
name overrides the directory name in high-level context.description is the primary trigger text shown to the model before it reads the full skill.when_to_use / when-to-use is appended to description in the high-level skill listing.tags are used for search/scoring.disable-model-invocation: true keeps the skill out of automatic routing context, while still allowing explicit $skill-name activation.read_skill_file results for SKILL.md, so the model receives the detailed instruction body after discovery metadata has already been consumed.This mirrors the progressive-disclosure pattern: lightweight frontmatter is used for routing, normal routed skills load their body through read_skill_file, and explicit $skill-name activations expand their matching body immediately.
Place skill.json in a skill directory to override display metadata when SKILL.md frontmatter is absent:
{ "name": "My Custom Skill", "description": "Use this skill when the user asks to do X, Y, or Z.", "tags": ["data analysis", "csv", "statistics"] }
Metadata priority is: SKILL.md frontmatter first, then skill.json, then the directory name plus the first paragraph of the markdown body.
The model should not scan a long skills catalog. The plugin routes before injection so the model sees only the smallest useful set of candidates.
Routing uses metadata only:
name directory basename frontmatter description frontmatter when_to_use / when-to-use tags environment/display path
The router scores exact name matches, tag matches, description/when-to-use token overlap, and path/name overlap. Skills with disable-model-invocation: true are excluded from automatic routing. For normal routed candidates, the full SKILL.md body is not injected; the model loads it with read_skill_file. For explicit $skill-name activation, the matching SKILL.md body is expanded immediately before the model starts reasoning.
Use list_skills with mode: "route" to inspect the same routing decision outside the prompt loop.
| Setting | Default | Description |
|---|---|---|
| Internal Skills Context | On | Automatically provides skill instructions and available skill context under the hood. No system prompt setup required. |
| Max Skills in Context | 15 | Upper bound for discovery work. Normal prompt injection is further capped by deterministic routing, currently up to 3 routed candidates. |
| Skills Runtime Environment | Host-dependent | Windows, WSL, or Both. Controls path resolution, skill reads, and command target behavior. |
| Skills Paths | Last saved/default | Semicolon-separated skill root directories. |
| Command Execution Safety | Disabled | Controls whether run_command can execute shell commands. |
| WSL Distro | Empty | Optional WSL distribution name. Empty uses the default WSL distro. |
| Windows Shell Path | Empty | Optional override for Windows command execution shell. |
| WSL Shell Path | Empty | Optional override for WSL command execution shell. |
| Legacy Shell Path | Empty | Backward-compatible Windows shell override. Prefer Windows Shell Path. |
~/.lmstudio/skills on first run.default — reset saved paths back to ~/.lmstudio/skills.Examples:
~/.lmstudio/skills ~/.agents/skills ~/.lmstudio/skills;~/.agents/skills
Settings are persisted to:
~/.lmstudio/plugin-data/lms-skills/settings.json
All plugin tools use Zod schemas before the implementation runs. These schemas reject malformed inputs early, including:
.. in skill-relative file paths,Schema validation is not the only safety layer. Runtime path checks, command safety policy, and request timeouts still run after schema validation.
run_command is intentionally disabled by default.
This prevents the model from issuing exploratory or destructive shell commands unless the user explicitly enables command execution in plugin settings.
| Mode | Behavior |
|---|---|
| Disabled | Blocks all model-issued shell commands. Recommended default. |
| Read-only | Allows simple inspection commands only. Blocks shell metacharacters, redirects, pipes, command chaining, variable expansion, and mutating arguments. |
| Guarded | Allows broader commands but still blocks dangerous patterns. |
Read-only mode allows inspection-style commands such as:
pwd, ls, cat, head, tail, grep, rg, find, stat, file, wc, sort, diff, env, which
The safety policy blocks destructive or high-risk patterns such as:
rm, rmdir, del, Remove-Item format, mkfs, dd, shred chmod, chown, chgrp mv, cp, mkdir, touch, tee redirection such as >, >>, << package managers such as npm install, pip install, apt, brew, choco network/download tools such as curl, wget, Invoke-WebRequest ssh, scp, rsync mutating git commands such as clone, pull, push, reset, clean, checkout kill/taskkill nested shells such as bash -c, sh -c, powershell -c encoded PowerShell
This is policy-level hardening, not a full OS sandbox. For untrusted workloads, use an external sandbox such as a container, VM, or locked-down WSL environment with read-only mounts, no network, and resource limits.
The plugin has layered timeout protection so model/tool requests cannot wait forever:
| Area | Default | Behavior |
|---|---|---|
| Prompt preprocessor scan | 3 seconds | If skill discovery is slow, the model still receives a compact skills reminder instead of hanging. |
read_skill_file | 30 seconds | Aborts file/path resolution and returns a structured timeout result. |
list_skill_files | 45 seconds | Aborts directory traversal and returns a structured timeout result. |
list_skills | 60 seconds | Aborts broad skill scans/searches and returns a structured timeout result. |
run_command | 30 seconds default + 15 seconds setup budget | Command execution has a timeout and is disabled unless explicitly enabled. |
Timeouts are enforced with AbortSignal, so WSL/runtime subprocesses are killed when possible. Timeout events are logged as tool_timeout or runtime_exec_abort.
The plugin emits concise structured logs prefixed with [lms-skills].
Default logs are human-readable route, context-injection, and tool summaries. Every prompt injection logs proof of what was inserted, including injection kind, selected/expanded skills, source paths, byte counts, short SHA-256 hashes, and compact previews:
[lms-skills] prompt activation tokens=$example-skill resolved=example-skill unresolved=- expanded=1 action=expanded_before_model id=prompt-... [lms-skills] route mode=explicit_activation_expanded action=expanded_skill(example-skill) before_model inject=1984ch id=prompt-... [lms-skills] context kind=explicit_expanded packet=skill_invocation_packet skills=example-skill inject=1984ch sha=85a3f72de1cd preview="<skill_invocation_packet ..." payload=67ch payloadSha=678f53749760 payload="..." id=prompt-... [lms-skills] context kind=routed packet=routed_skills skills=1:docs-writer:score=128:confidence=high:source=WSL:/... inject=1378ch sha=d0abcc393a31 payload="please update the README docs" id=prompt-... [lms-skills] read_skill_file start skill=example-skill file=- timeout=30000ms id=read_skill_file-... [lms-skills] read_skill_file done 42ms id=read_skill_file-...
The context line is the audit trail for model-facing context. Use it to verify whether the model received an explicit expanded skill packet, routed skill candidates, a compact reminder, or fallback context.
Set LMS_SKILLS_DEBUG=1 to switch back to full JSON event logs.
Enable verbose step/runtime tracing with:
LMS_SKILLS_DEBUG=1 npm run dev
Optional thresholds:
LMS_SKILLS_SLOW_STEP_MS=250 LMS_SKILLS_SLOW_RUNTIME_MS=500
The default path ~/.lmstudio/skills resolves to:
| Platform | Path |
|---|---|
| Windows | C:\Users\<you>\.lmstudio\skills |
| macOS | /Users/<you>/.lmstudio/skills |
| Linux | /home/<you>/.lmstudio/skills |
In WSL mode, ~ is resolved using the WSL/Linux home directory.
$skill-name tokens.SKILL.md body into a <skill_invocation_packet> before model reasoning.read_skill_file("skill-name") before doing covered work.SKILL.md; if needed, it calls list_skill_files and reads referenced supporting files.cd lms-plugin-skills bun install bun run dev
Equivalent npm commands usually work too:
npm install npm run build npm run dev
npm run build
No test suite is currently configured.
Apache 2.0