Skip to content

feat: respect disable-model-invocation in SKILL.md frontmatter - #40609

Closed
simonedstrom96 wants to merge 1 commit into
anomalyco:devfrom
simonedstrom96:skill-model-invocation
Closed

simonedstrom96 wants to merge 1 commit into
anomalyco:devfrom
simonedstrom96:skill-model-invocation

Conversation

@simonedstrom96

@simonedstrom96 simonedstrom96 commented Aug 5, 2026 •

Copy link
Copy Markdown

Issue for this PR

Closes #34498

Type of change

  • Bug fix
  • New feature
  • Refactor / code improvement
  • Documentation

What does this PR do?

Skills are always advertised to the model today, so a big manual-only skill costs context on every request even if you only ever run it yourself.
This adds support for disable-model-invocation: true in SKILL.md frontmatter, which Claude Code and Cursor already use for the same purpose.

A skill with the flag is still discovered and still runs as /skill-name, but it is left out of <available_skills> and the skill tool refuses to load it.
That is the whole behavior change: discovery is untouched, only the two model-facing paths filter on the flag.

There are two skill implementations in the tree right now, so the flag is handled in both:

  • v1: parsed in packages/opencode/src/skill/index.ts, filtered out of Skill.available() (which feeds the system prompt), rejected in packages/opencode/src/tool/skill.ts.
  • v2: parsed in packages/core/src/skill.ts, filtered out of SkillV2.available() (which feeds skill guidance), rejected in packages/core/src/tool/skill.ts.

Manual invocation keeps working because commands are built from skill.all() / skill.list(), which still contain the flagged skills.

The flag is surfaced as an optional disableModelInvocation field on the skill payload, so the generated SDK files and packages/sdk/openapi.json are regenerated (bun run generate in packages/client, then ./script/generate.ts), and I added an entry to specs/v2/schema-changelog.md since that file asks for generated SDK schema changes to be recorded.
Docs for the new field are in packages/web/src/content/docs/skills.mdx.

How did you verify your code works?

Tests added:

  • packages/opencode/test/skill/skill.test.ts: a flagged skill shows up in all() but not in available().
  • packages/opencode/test/tool/skill.test.ts: the v1 skill tool fails on a flagged skill.
  • packages/core/test/skill.test.ts and test/skill/guidance.test.ts: flagged skill is listed but excluded from available() and from rendered guidance.
  • packages/core/test/tool-skill.test.ts: the v2 skill tool returns "Unable to load skill".

bun test passes in packages/core (the two failing Snapshot / Git trees tests also fail on a clean dev checkout) and for the skill tests in packages/opencode.
bun typecheck is clean.

To reproduce by hand, create .opencode/skill/manual-only/SKILL.md:

---
name: manual-only
description: Manual only skill.
disable-model-invocation: true
---

# Manual Only

opencode debug skill lists it with "disableModelInvocation": true, it is gone from the skills section of the system prompt, and /manual-only still inserts the skill content.

Screenshots / recordings

Not a UI change.

Checklist

  • I have tested my changes locally
  • I have not included unrelated changes in this PR

@github-actions github-actions Bot added the needs:compliance This means the issue will auto-close after 2 hours. label Aug 5, 2026
Skills that set `disable-model-invocation: true` stay installed and
manually invocable, but are no longer advertised to the model or
loadable through the `skill` tool.

- Add optional `disableModelInvocation` to the v1 and `SkillV2.Info`
  HTTP payloads and generated SDK skill lists, parsed from
  `disable-model-invocation` in `SKILL.md` frontmatter.
- Omit flagged skills from the system prompt / skill guidance and reject
  them from the model-facing `skill` tool; they stay listed so users can
  still invoke them as slash commands.
- No durable data or migration is affected; the field is optional and
  unset for existing skills.

Closes anomalyco#34498
@simonedstrom96
simonedstrom96 force-pushed the skill-model-invocation branch from 57180c1 to 94cdf50 Compare August 5, 2026 08:57
@github-actions github-actions Bot removed the needs:compliance This means the issue will auto-close after 2 hours. label Aug 5, 2026
@github-actions

github-actions Bot commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

Thanks for updating your PR! It now meets our contributing guidelines. 👍

@niksy

niksy commented Aug 5, 2026

Copy link
Copy Markdown

Does this cover OpenCode v2 approach by extending support? I don’t see relevant part regarding metadata.

@rhoxe

rhoxe commented Aug 18, 2026

Copy link
Copy Markdown

Does this also cover user-invocable frontmatter?

@harshcurious

Copy link
Copy Markdown

@simonedstrom96 are you still working on this?

@Geker

Geker commented Sep 12, 2026

Copy link
Copy Markdown

I implemented the same change locally and verified it end-to-end. Two notes that might be useful:

Filtering the list isn't enough on its own. Skill.available() is the only model-facing list — its sole caller is session/system.ts — so filtering there hides the skill from <available_skills> while leaving slash commands working, since command discovery uses Skill.all(). But both skill tools still resolve by name from the full registry: v1 tool/skill.ts calls .require(params.name), v2 core/src/tool/skill.ts calls skills.list().find(...). A model that knows the name can load it regardless. Your PR patches both tool files, which is the right call — worth keeping.

Measured on a config with 122 skills, 19 of them flagged: debug skill (the all() path) still lists all 122, the model-visible list drops to 103, and the difference is exactly those 19 — no collateral filtering.

Plugins that register their own skill tool bypass this entirely. I hit it locally: with a plugin overriding the tool, the refusal never fired even after patching both native tools. The tell was the tool output wording not matching anything in the opencode source. Probably out of scope for this PR, but it may be worth a line in skills.mdx, since plugin-provided skill tools are fairly common.

@thdxr

thdxr commented Oct 10, 2026

Copy link
Copy Markdown
Member

This branch has been upgraded to V2. We're no longer taking PRs for V1. If you think this is still relevant, please port it over and open it for V2.

— from 𝕺𝖕𝖊𝖓𝕮𝖔𝖉𝖊

@thdxr thdxr closed this Oct 10, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[FEATURE]:Respect disable-model-invocation: true in SKILL.md frontmatter

6 participants