Kotoba

Where to start

What Kotoba isWho she is and where she runs, in one page
InstallingOne package, two commands
First runA key, a model, her first words
The approval gateHow she asks before she acts
The two voice modesLocal voice, or the agent tunnel
The soul fileChange who she is
RoadmapWhat grows next, and what was cut on purpose

Or a section

↑↓ move openesc close124 pages
All pages

Contributing and reference5 of 12

Adding a skill

About 2 minutes to read

On this page

A skill is a Markdown file, not code. It is guidance the model pulls up when a task calls for it — a playbook rather than a capability. soul/skills/research.md is one; it tells her how to run a real research job, and it changes nothing about what tools she has.

Where they live

soul/skills/*.md

Three ship today, confirmed by asking the loader:

SkillRequires
researchnothing — always eligible
discord-serverthe discord toolset
operating-websitesthe browser toolset

The directory is resolved by api/src/kotoba/core/skill_docs.py in this order:

  1. KOTOBA_SKILLS_DIR, if set.
  2. soul/skills/ next to the clone.
  3. The copy packaged inside the wheel.

A skill name is slugified and jailed to that directory, so it can never escape it — skill_view with a ../ path returns nothing.

The frontmatter

markdown
---
name: operating-websites
description: Drive a real website end-to-end — log in, search, navigate, fill forms — with the browser tools.
when_to_use: any multi-step task inside a real website (Facebook, Gmail, a dashboard, a store)
requires_toolsets: [browser]
---

# Operating websites

The body. Everything below the frontmatter is what `skill_view` hands the model.

What each key does, read out of the loader:

KeyRead by the codeEffect
nameyesthe name she calls skill_view with, and the title in the prompt list
descriptionyesthe one line that appears in the system prompt beside the name
requires_toolsetsyesthe skill is offered only when every named toolset is active
when_to_usenonothing reads it; the three shipped skills carry it as prose for a human

requires_toolsets accepts a list or a single string. Absent, it means no requirement, and the skill is always eligible. Surface a browser playbook only when the browser is connected.

Frontmatter is optional. Without it, the loader falls back to the first # heading as the title and the first paragraph as the description, and the file stem as the name.

Malformed YAML degrades to no frontmatter rather than raising, so a broken skill file loses its metadata and keeps its body.

How it reaches her

The system prompt lists name — description for every skill. When one looks relevant she calls skill_view, which returns the whole body. Nothing is loaded until she asks, so a long playbook costs nothing on a turn that does not need it.

Writing one

  • The body is instructions, not documentation. Address her directly.
  • Say what to do first. The shipped skills open with the one rule that prevents most failures, and say so.
  • Name the tools it depends on, and declare them in requires_toolsets, or it will be offered on a turn where those tools do not exist.
  • Only research.md carries a language rule. If your skill needs one, write it; nothing inherits.

Testing it

api/tests/test_skill_frontmatter.py covers the loader — parsing, the no-frontmatter fallback, and the toolset filter. It writes its skills into a temporary directory via KOTOBA_SKILLS_DIR, which is the pattern to copy if you test a skill of your own.

bash
pytest api/tests/test_skill_frontmatter.py -q

Measured: 4 passed.