Naalya Handbook
Adding AI Tools

Adding a skill

Teach an agent a procedure with a SKILL.md playbook — which folder, what to put in it, and what not to.

The agent already has tools. What it lacks is when and in what order to use them for a job that is bigger than one call — "write an NCDC scheme of work", "orient on my class before answering", "put this PDF in chat files."

That is a skill: a markdown playbook on disk, mounted read-only at /skills/. The model sees each skill's name and description up front, and only reads the file when the task matches. It is not a tool. It does not run code. It does not replace a missing RPC method.

This page is the ticket "Bean should follow our scheme-of-work procedure" or "every agent should know how we handle PDFs."

Where skills live

apps/worker/src/naalya-agent/core/skills/
general/<skill-name>/SKILL.md   ← every agent
rover/<skill-name>/SKILL.md     ← supervisor only
angie/<skill-name>/SKILL.md     ← class teacher
bean/<skill-name>/SKILL.md      ← subject teacher
sarah/<skill-name>/SKILL.md     ← director of studies

general/ is a shared library, not a persona. Put a skill there only if every agent should load it (PDF, PPTX, image, shared-docs, write-docs). Persona folders are extra sources on top.

AgentLoads
Rover/skills/general/ then /skills/rover/
Angie (class_teacher)/skills/general/ then /skills/angie/
Bean (subject_teacher)/skills/general/ then /skills/bean/
Sarah (dos)/skills/general/ then /skills/sarah/

Order matters: last wins on a name collision, so a persona skill can shadow a general one of the same name.

There is no graph file to edit. The helpers below already point each agent at general/ + its persona folder. A new SKILL.md in the right folder is picked up on the next worker boot.


The helpers that mount skills

Files: apps/worker/src/naalya-agent/core/backends/skills.constants.ts and skills.backend.ts. You do not call these when adding a playbook. You need to know they exist so you do not invent a fifth folder that nothing loads.

HelperWhat it does
SKILLS_ROUTE'/skills/' — where CompositeBackend mounts the on-disk library.
GENERAL_SKILL_SOURCE'/skills/general/' — shared playbooks every agent loads first.
WIRE_ID_TO_SKILL_PERSONAroverrover, class_teacherangie, subject_teacherbean, dossarah. The only map between wire ids and folder names.
skillSourcesFor(persona)[GENERAL_SKILL_SOURCE, '/skills/<persona>/']. Trailing slashes required. Order matters: last wins on a name collision, so a persona skill can shadow a general one.
skillSourcesForWireId(wireId)skillSourcesFor(WIRE_ID_TO_SKILL_PERSONA[wireId]). What Rover and each role subagent actually pass to deepagents.
SKILLS_PERMISSIONSDeny write on /skills/**. Skills are author-owned; agents may discover and read them, never rewrite the source of truth.
resolveSkillsRoot()Directory that contains the persona folders. Prefers the webpack-copied tree beside the worker bundle (dist/apps/worker/skills/), then the git tree (apps/worker/src/naalya-agent/core/skills) for Studio / nest start:dev. Warns once if neither exists — agents would otherwise run with zero skills silently.
createSkillsBackend()Read-only FilesystemBackend on that root, virtualMode: true.
createSkillsOnlyComposite()CompositeBackend with StateBackend as the writable scratch root and the skill tree mounted at SKILLS_ROUTE. Skills without a sandbox still need this so the skill tree is never the writable root.

Do not invent a fifth persona folder without a row in WIRE_ID_TO_SKILL_PERSONA and a skillSourcesFor caller. Custom subagents do not inherit Rover's skills — each graph gets its own skills array. Role subagents already call skillSourcesForWireId('class_teacher' | …).

What is already there

FolderSkills
general/pdf, pptx, image, shared-docs, write-docs
rover/chat-files
angie/know-my-class, class-roster, class-gradebook
bean/scheme-of-work
sarah/(none yet — empty folder is fine)

Step 1: Pick the folder

Ask two questions:

  1. Does every agent need this?general/. PDF handling yes. "How Angie reads a roster" no.
  2. If not, which specialist owns the procedure? Angie must not carry a scheme-of-work skill: those create tools are Bean-only. A playbook that tells Angie to call create_scheme_of_work is a bug — she does not have the tool.

Do not invent a fifth persona folder without wiring it in WIRE_ID_TO_SKILL_PERSONA and skillSourcesFor — see the helpers above.


Step 2: Create the folder and frontmatter

Directory name equals YAML name. Deep Agents looks up skills that way.

apps/worker/src/naalya-agent/core/skills/angie/know-my-class/SKILL.md
---
name: know-my-class
description: >-
  Orient before answering class-teacher questions about streams, subjects, or
  who teaches what. Use when the teacher asks about their class, form, stream
  setup, subject coverage, or staff assigned to their classroom.
---

description is the model's trigger. Write when to open this file, not a slogan. If it is vague, the skill never loads — or it loads on every turn and wastes context.

You can add REFERENCE.md or extra files in the same folder (the PDF skill does). Keep the entry point as SKILL.md.


Step 3: Write a playbook, not a tool list

Skills should containSkills should not be
When this work appliesA restatement of the tool inventory
Standards / constraints (NCDC alignment, template-first)Vague "be helpful" prose
A decision tree (template vs ask user vs stop)Instructions for tools this persona does not have
What a good answer looks likeIdentity or targeting rules the server already enforces
Which tools to use as steps inside the procedure

Bean's scheme-of-work skill is the template: NCDC constraints, "template means a platform record, never an upload", the list_templatesask_usercreate_scheme_of_workupdate_* sequence, and when to stop.

Angie's know-my-class skill is the other shape: list_my_classrooms first, then subjects/staff, never invent UUIDs, ask when multiple streams match.

Keep tool descriptions short. Progressive disclosure lives here.


Step 4: Confirm it mounts

You should not need a worker code change. If the skill is missing in production but present locally, the webpack copy of core/skills/** is the usual cause.

terminal (API repo)
npx vitest run apps/worker/src/naalya-agent/core/tests/skills.backend.spec.ts

That spec locks the source paths: Rover → general + rover, Angie → general + angie, and so on. It does not parse your markdown — read the file as if you were the model.

Custom subagents do not inherit Rover's skills. Each graph gets its own skills array. Role subagents already call skillSourcesForWireId('class_teacher' | …).


Gotchas

Skills are author-owned. There is no tenant-editable skill store. Staff cannot upload a playbook. Do not build that here.

Do not teach /workspace/ from a skills-only backend. The files prompt is only attached when sandbox + thread-files are mounted. Skills themselves never need that.

A skill cannot grant a tool. If Bean's playbook needs list_curriculum_topics and that RPC tool does not exist, add the tool first. The skill will not conjure it.

Sarah's folder can stay empty. An empty persona source is valid; she still loads general/.

Where to go next

On this page