Files
Daniele c27da4e6ab
Nightly Build / build (push) Successful in 8m6s
feat(skills): rebuild the skill system for the multi-user model
Per blueprint/skill-project.md: the old single-namespace, hand-maintained
index is gone, replaced by a read-only, two-scope tree whose index is a
runtime function of its content.

- skills/ index generated at runtime (crates/skald-core/src/skills/:
  inventory, install, validate, watch), injected through the new
  <!-- SKILLS_LIST --> placeholder in AGENT.md (agents/common/skills.md);
  meta.json inject_skills flag removed. 11 chat/task agents carry the
  include, the 4 system agents do not.
- Two trees, both read-only in both directions: skills/shared/{id} (the
  group's) and skills/{username}/{id} (one member's own, on the stable
  userid). The root is closed too: UserFs::SkillMounts + RouteError (alias
  probe, plain-denied paths, no home fallback) and a per-user
  .skills-root/{userid} container mount with the two scope mounts nested
  inside, plus the fifth self-heal axis (skills_mounted).
- Agent verbs: skill_register/skill_delete (Config group, global scope
  behind the new skill.manage capability), fetch_repo for public repos,
  list_items(type="skills"); reads are plain read_file on the printed
  path. Seeded @fs_read skills/* allow.
- Freshness: a digest-gated watcher on the two trees emits
  SystemEvent::SkillsChanged, whose subscriber rebuilds the frozen prompt
  prefix via Skald::invalidate_prompt_prefix; in-process writers invalidate
  directly.
- The build ships no skills: the three bundled skills (ics2json,
  mcp-builder, skill-creator) and skills/index.md are removed, skills/ is
  instance data (gitignored, not packaged, no longer pruned by update.sh).
- Docs: skills.md, agents.md, shared-folders.md added; docs/index.md and
  agents/README.md updated.
2026-08-08 23:05:35 +01:00

70 lines
5.3 KiB
Markdown

# Skills
A skill is a **folder of instructions and resources** that you load on demand, when a task calls for it — a procedure written once and followed every time, instead of re-deriving the steps in each conversation. A skill adds no tools and starts no processes: it is knowledge you read, then apply with the tools you already have.
## Where skills live
Skills are installed in one of two read-only trees:
| Path | Whose | Who installs |
| --- | --- | --- |
| `skills/shared/<id>/` | the whole group — every member sees these | an admin |
| `skills/<username>/` | one member's own | that member |
Both trees are **read-only**, everywhere and for everyone. You cannot create or edit files under `skills/` — not with the file tools, not from the shell, not even with `sudo`. A skill is **installed**, never written in place; the only way in is `skill_register` (below). To modify a skill you copy it out, edit the copy, and register it again.
## Using a skill
Your prompt already carries the index of the skills you can see: one line each, with the full path of its `SKILL.md` and a short description of when to use it. When a task matches — even partially — **read the skill before doing the work**: `read_file skills/<scope>/<id>/SKILL.md`, then follow its instructions.
To run a skill's script, use `execute_cmd` with `workdir` set to the skill's folder (e.g. `workdir: "skills/shared/ics-import"`). A skill cannot write next to itself — the tree is read-only — so scripts must write their output, caches and state to your home (`~`) or `/tmp`, never into the skill folder.
To see exactly what is installed, with full descriptions and per-skill health: `list_items` with `type="skills"`.
## Creating a skill — the authoring contract
A skill folder looks like this:
```
<skill-name>/
├── SKILL.md # required
├── scripts/ or *.py, *.js in the root # optional executables
├── references/ # optional documents to read on demand
└── assets/ # optional templates, examples
```
`SKILL.md` opens with a YAML frontmatter block, then the instructions in Markdown:
```markdown
---
name: ics-import
description: Download an iCalendar (ICS) feed and turn it into JSON or a table. Use whenever the user gives you a calendar URL or asks to import, inspect or summarize events from an ICS link.
---
# ICS import
1. Run `python3 scripts/ics2json.py <url>` from this folder...
```
Rules — every one of them is **checked at installation**, and a folder that breaks one is refused with a message naming the problem:
- **`name`**: lowercase letters, digits and hyphens only, at most 64 characters. It becomes the installed folder's name — so the working copy may be called `draft-2`, but what gets installed is named after the frontmatter.
- **`description`**: required, at most 1000 characters. This is the *use condition* — the only thing the model sees when deciding whether the skill is relevant. Write it assertively: say exactly **when** to reach for the skill, and err on the side of triggering too easily rather than too rarely. Save the detail for the body.
- **Write it in English** — the instructions, the frontmatter, the comments. Everything the model reads is English.
- **Self-contained**: no symbolic links; at most 500 files and 8 MiB in total. A skill holds instructions, scripts and reference documents — bulk data belongs in a home or a project.
- Create the folder **somewhere you can write** — your home, a project or a shared folder. **Not in `/tmp`** or anywhere else in the container-only filesystem: installation copies the folder from the host side and cannot see those paths.
- A script that sticks to the Python/Node standard library and the preinstalled command-line tools works always. One that needs PyPI or npm packages **must say so in the body** — those packages are installed by hand (`sudo pip install …`) and are lost when the container is recreated.
## Installing, updating, deleting
All three go through tools, and all three ask a human for approval first — the approval card shows the full `SKILL.md`, the file list and where the skill is going:
- **Install**: `skill_register(scope, path)``scope` is `"mine"` (your own tree) or `"global"` (everyone's; requires the admin capability). The folder is validated, copied in one atomic move, and appears in the prompt index immediately.
- **Update**: register again with the same `name` in the same scope — the old copy is replaced. That is the only way a skill changes.
- **Promote to the group**: register an already-installed skill with `scope: "global"`, e.g. `skill_register("global", "skills/maria/ics-import")`.
- **Delete**: `skill_delete(scope, id)` — the folder is removed, with no recycle bin. Use `list_items` with `type="skills"` to find ids.
## Getting a skill from a public repository
`fetch_repo(url, sub_path, destination)` downloads a subtree of a **public git repository** into a writable folder of yours — shallow, without the `.git` history. It installs nothing: if what you downloaded is a skill, review the files and then call `skill_register` on the destination folder. Every download leaves a `.source.json` ticket in the destination recording the URL, the sub-path and the exact commit it came from, so "where did this come from?" always has an answer.