feat(skills): support nested directories and lightweight injection#261
Open
Weaxs wants to merge 1 commit into
Open
feat(skills): support nested directories and lightweight injection#261Weaxs wants to merge 1 commit into
Weaxs wants to merge 1 commit into
Conversation
Two improvements that align pi-subagents' skill handling with the host
pi-coding-agent runtime:
1. Recursive skill discovery
collectFilesystemSkills now descends into subdirectories of registered
skill roots, treating any directory containing a SKILL.md as a skill
anchor (matches pi-coding-agent's loadSkillsFromDirInternal). Hidden
directories and node_modules are skipped. Layouts like
<root>/<group>/<name>/SKILL.md now resolve, in addition to the
existing <root>/SKILL.md and <root>/<name>/SKILL.md shapes.
2. Lightweight skill injection
New agent frontmatter field `skillInjection: full | light` (default
full, fully backwards compatible) controls how buildSkillInjection
shapes the system prompt:
- full: existing <skill name="...">full body</skill> per skill
- light: <available_skills> with name/description/location per skill,
plus instructions to load via the read tool when the task matches
Mirrors pi-coding-agent's formatSkillsForPrompt output so a model
that knows one knows the other. Helpful for agents that declare many
skills in `skills:` but want a small startup system prompt.
The new buildLightSkillInjection helper is exported alongside the
existing buildSkillInjection. The field is plumbed through
BuiltinAgentOverrideBase, BuiltinAgentOverrideConfig, AgentConfig,
KNOWN_FIELDS, the agent serializer, agent management input parsing,
and the subagent extension tool description.
Tests
- Add test/unit/skills-extensions.test.ts covering nested discovery
(two-level layout, recursion stop at first SKILL.md, node_modules
and dotfile exclusion, deep resolveSkills) and light injection
(no-body emission, missing-description fallback, empty-list, XML
escaping, full-mode regression guard)
- Full unit suite: 512/512 pass
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #183
Closes #262
Why
Two gaps surface when subagents and the host
pi-coding-agentshare the same skill directory.1. Nested skill discovery mismatch
pi-coding-agent'sloadSkillsFromDirInternalrecursively walks subdirectories looking forSKILL.md, treating any directory that contains one as a skill root. This lets users group skills hierarchically:pi-subagents'scollectFilesystemSkillsonly descends one level — it finds<root>/<name>/SKILL.mdbut not<root>/<group>/<name>/SKILL.md. The result is that a subagent's frontmatterskills: safe-bashresolves in the host but fails in the subagent runtime, even when both agents are pointed at the same skills tree.2. Heavyweight injection is the only option
When an agent declares
skills: a, b, cin frontmatter,buildSkillInjectionalways wraps each skill's fullSKILL.mdbody into the system prompt. For agents that bundle many skills, this can add 100KB+ to every cold start, even though most invocations only touch one or two skills.pi-coding-agent'sformatSkillsForPromptsolves the same problem on the host side by emitting a compact<available_skills>listing of name + description + path, and pointing the model at the read tool. Subagents have no equivalent escape hatch — they either inject everything or know nothing about declared skills.What changes
1. Recursive skill discovery (matches
pi-coding-agent)collectFilesystemSkillsnow recurses into subdirectories of each registered skill root, stopping at any directory that contains aSKILL.md(treating it as the skill anchor for that subtree). Hidden directories andnode_modulesare skipped to mirror the host scanner.Layouts that previously resolved are unchanged:
<root>/SKILL.md(single skill at the root)<root>/<name>.mdand<root>/<name>/SKILL.mdNew layouts now resolved:
<root>/<group>/<name>/SKILL.mdSKILL.md2. Optional lightweight skill injection
A new agent frontmatter field
skillInjection: full | light(defaultfull, fully backwards-compatible) controls howbuildSkillInjectionshapes the system prompt.Given an agent with
skills: safe-bash, tmux, here is the actual text appended to the subagent's system prompt in each mode.full(default, current behavior) — the entire SKILL.md body is inlined per skill:light— only name, description, and absolute path are emitted, plus instructions to load each file on demand via the read tool:The
lightshape mirrorspi-coding-agent'sformatSkillsForPromptoutput, so a model that knows how to use the host's<available_skills>listing already knows how to use this one.The new
buildLightSkillInjectionhelper is exported alongside the existingbuildSkillInjection. The field is plumbed throughBuiltinAgentOverrideBase,BuiltinAgentOverrideConfig,AgentConfig,KNOWN_FIELDS, the agent serializer, agent management input parsing, and thesubagentextension tool description.Example agent file
A user/project agent declaring many skills but opting into the lightweight listing — only the four "core" skills are injected in full; the rest stay as references the agent loads on demand:
Without
skillInjection: light, the sameskills:declaration would inline three full SKILL.md bodies into the system prompt at every cold start. Withlight, only the listing block (name + description + location) is appended, and the agent loads each SKILL.md via the read tool when actually needed.Tests
test/unit/skills-extensions.test.tsadds 9 cases covering:<root>/<group>/<name>/SKILL.md)SKILL.md(no nested SKILL.md leakage)node_modulesand dotfile directories are excludedresolveSkillsbuildLightSkillInjectionemits name/description/location, never the full bodybuildSkillInjection(default) still emits full body — regression guardFull test suite: 512 / 512 pass (
npm run test:unit).Backwards compatibility
<root>/<name>/SKILL.mdetc.) keep working unchanged.skillInjectiondefaults tofull, so agents without the field behave exactly as before.skillInjectionwhen value is non-default (light), keeping existing agent files byte-identical on round-trip.