feat(skill-loader): honor user-invocable spec field + OmO hide_nested_by_default flag (#54)
* feat(skill-loader): read user-invocable and disable-model-invocation from SKILL.md
Extends the skill loader to parse the Claude Code spec fields
user-invocable and disable-model-invocation from SKILL.md frontmatter.
Both are optional booleans; absent values retain the spec defaults
(user-invocable: true, disable-model-invocation: false).
Adds depth and userInvocable to LoadedSkill so downstream registration
can honor the spec field and apply the OmO nesting policy on top.
flatName stays paired with depth > 0 for the flat slash-name shortcut.
No behavior change yet: the registration layer still treats every
skill the same. The next commit wires the filter.
* feat(skill-loader): honor user-invocable in slash picker with OmO nesting-inversion flag
Teaches the slash-command registration layer to respect the Claude
Code user-invocable frontmatter field. Skills with user-invocable:
false are omitted from the slash-command picker; skills with
user-invocable: true (or unset) remain visible per spec default.
Adds a per-workspace override: skills.hide_nested_by_default. When
true, nested skills whose frontmatter does not set user-invocable are
treated as if they had set user-invocable: false. Top-level skills
are never affected by the override. Explicit frontmatter values
always win over the inversion.
When a nested skill is visible, it registers under its flat short
name (/child) unless that name is already taken by a top-level skill
or a previously-registered nested sibling, in which case it falls
back to the path-prefixed unique name (/parent/child) and a warning
is logged.
Skills tool, UI Skills picker, and load_skills delegation remain
unaffected by visibility; hidden skills stay fully reachable by
agents.
* test(skill-loader): cover user-invocable parsing, inversion flag, and collisions
Ten new cases across two files:
skill-directory-loader.test.ts
- top-level and nested depth/flatName tagging
- user-invocable frontmatter parsing for true, false, and unset
skill-definition-record.test.ts
- spec-default behavior (nested visible when flag off, field unset)
- user-invocable: false hides a skill at any depth
- OmO hide_nested_by_default inversion flag hides nested skills
- opt-in via user-invocable: true survives the inversion flag
- top-level skills unaffected by the inversion flag
- nested-vs-nested flat-name collision falls back to prefixed path
- top-level wins over nested on flat-name collision regardless of
iteration order
* fix(skill-loader): only index SKILL.md directories as skills, per Anthropic spec (#55)
* fix(skill-loader): only index SKILL.md directories as skills, per Anthropic spec
Stops indexing bare .md files inside skills directories as skills. Per
the Anthropic Agent Skills spec (https://agentskills.io/specification),
a skill is a directory containing SKILL.md. Sibling .md files within a
skill's directory are supporting material and must be referenced via
@path from the parent SKILL.md, not indexed as independent skills.
Affects both discovery paths:
- skill-directory-loader.ts (serial async, used by every loadX wrapper
in loader.ts)
- async-loader.ts (concurrency-16 async, used by the blocking worker)
The {dirName}.md fallback — where a directory without SKILL.md can
still declare a skill via a file named after the directory — is
preserved. It's an OmO extension the spec does not cover, and removing
it would break existing skills that use this entrypoint pattern.
User-configured single-file skill sources via skills.sources in
opencode.json are unaffected: config-source-discovery.ts still honors
absolute .md paths because those are explicit, not directory walks.
* test(skill-loader): update fixtures to use SKILL.md directories
Six tests previously encoded the pre-spec behavior where plain .md
files inside a skills directory were indexed as skills. They now
either flip to assert the spec-correct behavior (discovery returns
zero skills for bare .md) or restructure fixtures to use proper
dir/SKILL.md layouts.
async-loader.test.ts:
- 'discovers direct .md files' now asserts direct .md files are
ignored per spec
- 'preserves nested skill path names for nested direct markdown
discovery' now asserts sibling .md files inside a nested directory
are ignored when there is no SKILL.md entrypoint
blocking.test.ts:
- Four tests restructured from flat dir/skill-N.md fixtures to
dir/skill-N/SKILL.md fixtures. Same assertions, spec-compliant
inputs. V
Vacbo committed
4b4ccaea469dad70c4105cdbddec26a1b1f89853
Parent: bc3e389
Committed by GitHub <noreply@github.com>
on 4/21/2026, 10:33:49 PM