Skip to content

Commit 2b48b3a

Browse files
committed
fix(skills): preserve disable-model-invocation prompt filtering
1 parent a02ceb3 commit 2b48b3a

5 files changed

Lines changed: 18 additions & 18 deletions

File tree

src/agents/skills.buildworkspaceskillsnapshot.test.ts

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -104,10 +104,8 @@ describe("buildWorkspaceSkillSnapshot", () => {
104104

105105
expect(snapshot.prompt).toContain("visible-skill");
106106
expect(snapshot.prompt).not.toContain("hidden-skill");
107-
expect(snapshot.skills.map((skill) => skill.name).toSorted()).toEqual([
108-
"hidden-skill",
109-
"visible-skill",
110-
]);
107+
expect(snapshot.skills.map((skill) => skill.name)).toContain("hidden-skill");
108+
expect(snapshot.skills.map((skill) => skill.name)).toContain("visible-skill");
111109
});
112110

113111
it("keeps prompt output aligned with buildWorkspaceSkillsPrompt", async () => {

src/agents/skills.loadworkspaceskillentries.test.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -149,12 +149,12 @@ describe("loadWorkspaceSkillEntries", () => {
149149
expect(entries.map((entry) => entry.skill.name)).toContain("fallback-name");
150150
});
151151

152-
it("keeps disable-model-invocation skills visible in exposure metadata for newly loaded entries", async () => {
152+
it("marks disable-model-invocation skills as hidden in exposure metadata for newly loaded entries", async () => {
153153
const workspaceDir = await createTempWorkspaceDir();
154154
await writeSkill({
155155
dir: path.join(workspaceDir, "skills", "hidden-skill"),
156156
name: "hidden-skill",
157-
description: "Visible prompt entry",
157+
description: "Hidden prompt entry",
158158
frontmatterExtra: "disable-model-invocation: true",
159159
});
160160

@@ -166,7 +166,7 @@ describe("loadWorkspaceSkillEntries", () => {
166166
const hiddenEntry = entries.find((entry) => entry.skill.name === "hidden-skill");
167167

168168
expect(hiddenEntry?.invocation?.disableModelInvocation).toBe(true);
169-
expect(hiddenEntry?.exposure?.includeInAvailableSkillsPrompt).toBe(true);
169+
expect(hiddenEntry?.exposure?.includeInAvailableSkillsPrompt).toBe(false);
170170
});
171171

172172
it("inherits agents.defaults.skills when an agent omits skills", async () => {

src/agents/skills.test.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -333,22 +333,22 @@ describe("buildWorkspaceSkillsPrompt", () => {
333333
expect(prompt).toContain(path.join(skillDir, "SKILL.md"));
334334
});
335335

336-
it("keeps runtime-eligible skills visible in available_skills when disable-model-invocation is set", async () => {
336+
it("omits disable-model-invocation skills from available_skills for freshly loaded entries", async () => {
337337
const workspaceDir = await makeWorkspace();
338338
const skillDir = path.join(workspaceDir, "skills", "hidden-skill");
339339

340340
await writeSkill({
341341
dir: skillDir,
342342
name: "hidden-skill",
343-
description: "Still visible in the prompt",
343+
description: "Hidden from the prompt",
344344
frontmatterExtra: "disable-model-invocation: true",
345345
});
346346

347347
const prompt = buildWorkspaceSkillsPrompt(workspaceDir, resolveTestSkillDirs(workspaceDir));
348348

349-
expect(prompt).toContain("hidden-skill");
350-
expect(prompt).toContain("Still visible in the prompt");
351-
expect(prompt).toContain(path.join(skillDir, "SKILL.md"));
349+
expect(prompt).not.toContain("hidden-skill");
350+
expect(prompt).not.toContain("Hidden from the prompt");
351+
expect(prompt).not.toContain(path.join(skillDir, "SKILL.md"));
352352
});
353353
});
354354

src/agents/skills/skill-contract.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -36,9 +36,10 @@ function escapeXml(str: string): string {
3636
}
3737

3838
/**
39-
* Keep this formatter byte-for-byte aligned with the upstream Agent Skills XML
40-
* layout so we can avoid importing the full pi-coding-agent package root on the
41-
* cold skills path.
39+
* Keep this formatter's XML layout byte-for-byte aligned with the upstream
40+
* Agent Skills formatter so we can avoid importing the full pi-coding-agent
41+
* package root on the cold skills path. Visibility policy is applied upstream
42+
* before calling this helper.
4243
*/
4344
export function formatSkillsForPrompt(skills: Skill[]): string {
4445
if (skills.length === 0) {

src/agents/skills/workspace.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -487,9 +487,10 @@ function loadSkillEntries(
487487
invocation,
488488
exposure: {
489489
includeInRuntimeRegistry: true,
490-
// Prompt visibility now follows runtime inclusion for freshly loaded skills.
491-
// Legacy entries without exposure metadata still use the fallback below.
492-
includeInAvailableSkillsPrompt: true,
490+
// Freshly loaded entries preserve the documented disable-model-invocation
491+
// contract, while legacy entries without exposure metadata still use the
492+
// fallback in isSkillVisibleInAvailableSkillsPrompt().
493+
includeInAvailableSkillsPrompt: invocation.disableModelInvocation !== true,
493494
userInvocable: invocation.userInvocable !== false,
494495
},
495496
};

0 commit comments

Comments
 (0)