Skip to content

Add Agent Skills support and CLI commands#441

Open
jasonkneen wants to merge 2 commits intolmstudio-ai:mainfrom
jasonkneen:feature-skills
Open

Add Agent Skills support and CLI commands#441
jasonkneen wants to merge 2 commits intolmstudio-ai:mainfrom
jasonkneen:feature-skills

Conversation

@jasonkneen
Copy link

Introduces Agent Skills (agent.md/SKILL.md) support to the CLI, including interactive skill scaffolding (lms init), skill discovery, validation, inspection, and directory management (lms skills command group). Integrates skills into chat sessions with system prompt injection, updates core types, parser, and CLI preferences, and registers new commands in the CLI. All changes follow the Agent Skills open specification and maintain compatibility with the broader skills ecosystem.

Introduces Agent Skills (agent.md/SKILL.md) support to the CLI, including interactive skill scaffolding (`lms init`), skill discovery, validation, inspection, and directory management (`lms skills` command group). Integrates skills into chat sessions with system prompt injection, updates core types, parser, and CLI preferences, and registers new commands in the CLI. All changes follow the Agent Skills open specification and maintain compatibility with the broader skills ecosystem.
Copilot AI review requested due to automatic review settings January 29, 2026 13:23
@github-actions
Copy link

github-actions bot commented Jan 29, 2026

All contributors have signed the CLA ✍️ ✅
Posted by the CLA Assistant Lite bot.

Copy link

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: f6ec96d082

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +250 to +253
...(loadedSkills ?? []).map(
(skill): SlashCommand => ({
name: skill.metadata.name,
description: skill.metadata.description,

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Prevent skills from overriding built-in slash commands

Because skills are appended to the slash-command list after the built-ins, and SlashCommandHandler.setCommands stores commands in a Map by name (last registration wins), any skill whose name matches a built-in command will silently replace it. For example, a skill named model (valid by the current schema) will make /model unreachable, which breaks existing chat workflows for those users. Consider rejecting conflicting skill names or ensuring built-ins take precedence.

Useful? React with 👍 / 👎.

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This pull request introduces comprehensive Agent Skills support to the LM Studio CLI, implementing the Agent Skills open specification (agentskills.io). The changes enable users to create, discover, validate, and use reusable AI agent instructions through a structured SKILL.md format.

Changes:

  • Adds interactive skill scaffolding via lms init command with YAML frontmatter validation
  • Implements lms skills command group (ls, validate, inspect) for skill management and discovery
  • Integrates discovered skills into chat sessions through system prompt injection and dynamic slash commands

Reviewed changes

Copilot reviewed 18 out of 20 changed files in this pull request and generated 7 comments.

Show a summary per file
File Description
src/skills/types.ts Defines Zod schemas for skill metadata validation with naming constraints
src/skills/parser.ts Parses SKILL.md files with YAML frontmatter extraction
src/skills/discovery.ts Recursively discovers valid skills from configured directories
src/skills/promptBuilder.ts Builds XML-formatted skill metadata for system prompt injection
src/subcommands/init.ts Interactive skill scaffolding with template generation
src/subcommands/skills/validate.ts Validates skill directories against specification
src/subcommands/skills/list.ts Lists discovered skills with metadata
src/subcommands/skills/inspect.ts Shows detailed skill information and statistics
src/subcommands/skills/index.ts Registers skills command group
src/subcommands/chat/index.tsx Loads skills and injects into system prompt
src/subcommands/chat/react/Chat.tsx Refactors prediction logic for reuse by slash commands
src/subcommands/chat/react/slashCommands.ts Registers skills as dynamic slash commands
src/cliPref.ts Adds skillsDirectories configuration option
src/lmstudioPaths.ts Defines default skills directory path
src/index.ts Registers new Skills command group in CLI
.gitignore Excludes /docs/plans directory
patches/* Third-party dependency patches (informational)

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

);
}
logger.error("Description is required. Use --description <desc>");
throw process.exit(1);
Copy link

Copilot AI Jan 29, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The statement throw process.exit(1) is problematic because process.exit() never returns and doesn't throw an exception. It terminates the process immediately. Using throw here doesn't make sense and could confuse TypeScript's control flow analysis. Simply call process.exit(1) directly without throw.

Copilot uses AI. Check for mistakes.
Comment on lines +255 to +258
const currentPrompt = chatRef.current.getSystemPrompt();
const isAlreadyActive = currentPrompt.includes(
`<skill-active>${skill.metadata.name}</skill-active>`,
);
Copy link

Copilot AI Jan 29, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The check for whether a skill is already active uses a simple string search in the system prompt, which could produce false positives if the skill name appears elsewhere in the prompt (e.g., in regular text or as part of another skill name). Consider using a more precise check, such as a regex pattern that ensures the skill tag is a complete match or tracking active skills in a separate data structure.

Copilot uses AI. Check for mistakes.
Comment on lines +261 to +263
const skillContext =
`\n\n<skill-active>${skill.metadata.name}</skill-active>\n` +
`<skill-instructions>\n${skill.body}\n</skill-instructions>`;
Copy link

Copilot AI Jan 29, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The skill body is injected directly into XML tags without escaping. If the skill body contains XML-like characters (e.g., <, >, &), this could break the XML structure or cause parsing issues. Consider escaping the skill body content before embedding it in the <skill-instructions> tags, or wrapping it in a CDATA section if the content should be treated as literal text.

Copilot uses AI. Check for mistakes.
Comment on lines +250 to +252
...(loadedSkills ?? []).map(
(skill): SlashCommand => ({
name: skill.metadata.name,
Copy link

Copilot AI Jan 29, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's a built-in slash command named "skills" (line 190) that lists loaded skills. If a user creates a skill directory named "skills", it will also be registered as a slash command with the same name (line 252), causing a naming conflict. Consider either reserving certain command names (like "skills", "help", "exit", "model", "clear", "system-prompt", "stats", "download") or implementing a collision detection mechanism that warns users or prefixes skill commands to avoid conflicts.

Copilot uses AI. Check for mistakes.
Comment on lines +110 to +114
let frontmatter = `---\nname: ${name}\ndescription: ${description}\n`;
if (license !== undefined) {
frontmatter += `license: ${license}\n`;
}
frontmatter += `---`;
Copy link

Copilot AI Jan 29, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The YAML frontmatter is constructed by simple string concatenation without proper YAML escaping. If the description or license contains special YAML characters (e.g., :, ", ', #, newlines), the generated SKILL.md file will have invalid YAML and fail to parse. Consider using a YAML library to serialize the frontmatter (e.g., using the YAML library already imported for parsing) to ensure proper escaping.

Copilot uses AI. Check for mistakes.
);
}
logger.error("Skill name is required. Usage: lms init <name>");
throw process.exit(1);
Copy link

Copilot AI Jan 29, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The statement throw process.exit(1) is problematic because process.exit() never returns and doesn't throw an exception. It terminates the process immediately. Using throw here doesn't make sense and could confuse TypeScript's control flow analysis. Simply call process.exit(1) directly without throw.

Copilot uses AI. Check for mistakes.
import { addCreateClientOptions, createClient, type CreateClientArgs } from "../../createClient.js";
import { formatSizeBytes1000 } from "../../formatSizeBytes1000.js";
import { addLogLevelOptions, createLogger, type LogLevelArgs } from "../../logLevel.js";
import { defaultSkillsFolderPath } from "../../lmstudioPaths.js";
Copy link

Copilot AI Jan 29, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unused import defaultSkillsFolderPath.

Suggested change
import { defaultSkillsFolderPath } from "../../lmstudioPaths.js";

Copilot uses AI. Check for mistakes.
@jasonkneen
Copy link
Author

I have read the CLA Document and I hereby sign the CLA

@github-actions github-actions bot added the CLA signed Indicates if all contributors have signed the CLA label Jan 29, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

CLA signed Indicates if all contributors have signed the CLA

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants