skill_create writes a SKILL.md (name, description, body) plus whatever bundled
files the model lists, and skill_edit changes one file of an existing skill
either by literal replacement, by appending, or by rewriting it. Both resolve
every path inside the skill directory first, so a bad argument is answered
before anything is written and before the human is asked anything; a rewritten
SKILL.md is re-parsed so a skill that lost its description (or its frontmatter)
says so instead of failing quietly. The store is reloaded after a write, which
makes a new skill visible to the very next skill_list.
Writing is a host policy, not a directory-access grant: the denylist wins
first, then a session grant, then [skills].allow_write, then the host's confirm
hook — asked once per skill directory, so one answer covers its files. A host
without a hook denies, which keeps the library default safe; the confirm can
also answer 'session' to cover the rest of the chat.
skill_list shows what was discovered (name, source root, directory, description)
together with the copies a name collision shadowed and the notes from repaired
frontmatter, so a third-party skill never disappears quietly. skill_read
addresses a skill by name and defaults to its SKILL.md, reusing read_file for
ranges, line numbers, and the continuation token; a bundled file is resolved
inside the skill directory only, symlinks included. skill_search greps every
visible skill (or one) and rebases each hit on its skill, which makes a match
attributable without absolute paths.
Reads need no grant: the host registers the roots as static read-only roots on
the workspace policy, so read_file/grep_files see skill directories too while
write access stays denied — skill writes will go through their own confirm.
Grep used to drop every file outside the primary workspace root, which also hid
hits under a granted directory; those now come back as absolute paths.