feat(coding-agent): support disable-model-invocation frontmatter for skills

When set to true, the skill is hidden from the system prompt, preventing
agentic invocation. Users can still invoke explicitly via /skill:name.

Also fixes pre-existing test bug where source expectation was wrong.

Fixes #927
This commit is contained in:
Mario Zechner 2026-01-24 03:34:40 +01:00
parent 3235926eed
commit 951fb953ed
8 changed files with 99 additions and 3 deletions

View file

@ -16,6 +16,7 @@ const ALLOWED_FRONTMATTER_FIELDS = new Set([
"compatibility",
"metadata",
"allowed-tools",
"disable-model-invocation",
]);
/** Max name length per spec */
@ -27,6 +28,7 @@ const MAX_DESCRIPTION_LENGTH = 1024;
export interface SkillFrontmatter {
name?: string;
description?: string;
"disable-model-invocation"?: boolean;
[key: string]: unknown;
}
@ -36,6 +38,7 @@ export interface Skill {
filePath: string;
baseDir: string;
source: string;
disableModelInvocation: boolean;
}
export interface LoadSkillsResult {
@ -231,6 +234,7 @@ function loadSkillFromFile(
filePath,
baseDir: skillDir,
source,
disableModelInvocation: frontmatter["disable-model-invocation"] === true,
},
diagnostics,
};
@ -245,9 +249,14 @@ function loadSkillFromFile(
* Format skills for inclusion in a system prompt.
* Uses XML format per Agent Skills standard.
* See: https://agentskills.io/integrate-skills
*
* Skills with disableModelInvocation=true are excluded from the prompt
* (they can only be invoked explicitly via /skill:name commands).
*/
export function formatSkillsForPrompt(skills: Skill[]): string {
if (skills.length === 0) {
const visibleSkills = skills.filter((s) => !s.disableModelInvocation);
if (visibleSkills.length === 0) {
return "";
}
@ -258,7 +267,7 @@ export function formatSkillsForPrompt(skills: Skill[]): string {
"<available_skills>",
];
for (const skill of skills) {
for (const skill of visibleSkills) {
lines.push(" <skill>");
lines.push(` <name>${escapeXml(skill.name)}</name>`);
lines.push(` <description>${escapeXml(skill.description)}</description>`);