Skip to content

Advertise the string-array schema for directory skill scripts and complete SKILL.md discovery #165

Description

@shibayan

What happens

directorySkillsSource (packages/core/src/node/directory-skills-source.ts) departs from the reference file-skill sources in three places that change what the model is told and which skills load at all.

1. Scripts are advertised without a parameter schema

A discovered script is pushed with name and run only (directory-skills-source.ts:215-222); SkillScript.parametersSchema stays undefined, so the skill body renders a self-closing <script name="scripts/convert.py"/> (packages/core/src/skills/skill.ts:351-357). The model learns nothing about the argument shape.

Both references advertise a fixed schema for every file-based script:

  • .NET AgentFileSkillScript.ParametersSchema returns {"type":"array","items":{"type":"string"}} (dotnet/src/Microsoft.Agents.AI/Skills/File/AgentFileSkillScript.cs:46,68), rendered as a <parameters_schema> child in the skill body.
  • Python FileSkillScript.parameters_schema returns the same object, "so that the LLM knows to pass positional CLI arguments as a JSON array of strings" (python/packages/core/agent_framework/_skills.py:505-512).

The runtime side already exists here: SkillScriptArguments accepts readonly string[] and the runner receives it. Only the advertisement is missing, so the model has to guess between an object and an array.

2. SKILL.md discovery stops one level short

skillDirectories returns the directory itself when it holds a SKILL.md, otherwise its immediate children that do (directory-skills-source.ts:160-178), and the paths doc promises exactly that ("a parent whose immediate children are skill directories", :43-51).

Both references search two levels below a root before giving up. .NET uses MaxSkillDirectorySearchDepth = 2 and checks for SKILL.md before the depth guard (AgentFileSkillsSource.cs:34,164-197); Python uses MAX_SEARCH_DEPTH = 2 with the same shape (_skills.py:1649,3576-3610). A root, its children and its grandchildren are all candidates, and a SKILL.md stops descent. The common layout skills/<category>/<skill>/SKILL.md loads there and is silently empty here.

searchDepth (already 2) is a different knob: it bounds the resource and script walk inside one skill, matching the references' per-skill search depth. It does not affect where SKILL.md is looked for.

3. A frontmatter name that differs from the directory name is accepted

loadSkill takes frontmatter.name as the skill's name and never compares it with the directory (directory-skills-source.ts:186-187). Both references reject the skill: Python logs "frontmatter name '%s' that does not match the directory name '%s'; skipping" (_skills.py:3532-3540), .NET logs LogNameDirectoryMismatch at Error and skips (AgentFileSkillsSource.cs:773). The references treat the directory name as the skill's name. Accepting a mismatch lets two directories advertise one name, and the resourceFilter / scriptFilter callbacks, which are keyed by skill name, then see a name that exists nowhere on disk.

Required implementation

  • Give every discovered script parametersSchema: { type: 'array', items: { type: 'string' } }.
  • Discover SKILL.md at the root, its children and its grandchildren, stopping descent at the first SKILL.md found; keep the symlink refusal at every level. Update the paths doc to match.
  • Report a skill whose frontmatter name differs from its directory name through ctx.reportSkillError and skip it.

Acceptance criteria

  • A reproduction-first test asserts the rendered skill body contains a <parameters_schema> element for a discovered script, failing before the change.
  • A test with root/<category>/<skill>/SKILL.md loads the skill; one with a fourth level does not; a SKILL.md at a higher level hides the ones beneath it.
  • A name/directory mismatch is reported and skipped, not loaded.
  • The existing symlink and containment tests still hold at the added level.
  • CHANGELOG.md records the discovery change; pnpm check passes.

Explicitly out of scope

Script execution itself, which stays application-supplied through scriptRunner, and the .py-only default script extension list.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    coreUsage: [Issues, PRs], Target: packages/coreskillsUsage: [Issues, PRs], Target: Agent Skills

    Type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions