Part of #56, and the first two boxes of #64.
There is no skills code in the tree today, so this is the piece everything else stands on:
what a skill is, what it looks like on disk, and where the list lives.
Scope
What has to be decided here, before any UI exists
- A skill's name is how it is invoked, so the name is validated, not free text. Two
skills cannot share an invocation name, and a name that cannot be typed into a composer
is not a name. Decide the rule in one pure place and test it there.
- The file is the format. "Export and import as plain files" means a skill round-trips
through a file a person can read and edit in any editor — frontmatter plus body, the
shape MarkdownDocumentFile and SpaceFile already use in this codebase. Reuse the
reasoning; do not invent a second format.
- Import is the untrusted direction. A skill file can arrive from anywhere. It is
bounded on the way in — name length, body length, tool list size — and a malformed file
costs that skill, never the whole import.
- A skill's text is user content. It gets the same delimiter treatment as anything else
entering a prompt, and it must not be able to close its own delimiter and continue as
instruction. The neutralisation belongs with the model, so every later caller inherits it
rather than remembering it.
- Decoding old data must not break.
decodeIfPresent with defaults for every array
field, per the project rule — a skill written by an earlier build must still read.
Not in this issue
Layering onto the system prompt and tool scoping (#64's other boxes) come next, then the
composer and Settings surfaces. This one lands the model, the store and the file format
with tests, and nothing that draws.
Ground rules
Same as #56.
Part of #56, and the first two boxes of #64.
There is no skills code in the tree today, so this is the piece everything else stands on:
what a skill is, what it looks like on disk, and where the list lives.
Scope
What has to be decided here, before any UI exists
skills cannot share an invocation name, and a name that cannot be typed into a composer
is not a name. Decide the rule in one pure place and test it there.
through a file a person can read and edit in any editor — frontmatter plus body, the
shape
MarkdownDocumentFileandSpaceFilealready use in this codebase. Reuse thereasoning; do not invent a second format.
bounded on the way in — name length, body length, tool list size — and a malformed file
costs that skill, never the whole import.
entering a prompt, and it must not be able to close its own delimiter and continue as
instruction. The neutralisation belongs with the model, so every later caller inherits it
rather than remembering it.
decodeIfPresentwith defaults for every arrayfield, per the project rule — a skill written by an earlier build must still read.
Not in this issue
Layering onto the system prompt and tool scoping (#64's other boxes) come next, then the
composer and Settings surfaces. This one lands the model, the store and the file format
with tests, and nothing that draws.
Ground rules
Same as #56.