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

The agent13 of 15

Skills

About 4 minutes to read

On this page

A skill is a Markdown file. Not code, not a plugin — a document she reads when the job matches it.

Two tools in the skills toolset: skill_list and skill_view. Both are read risk and both are offered in a conversation as well as in work mode.


The three that ship

Read live from the repo:

Namerequires_toolsetsWhat it is
research(none)Plan sub-questions, search several angles, prefer authoritative sources, synthesise, and produce a .md report with citations
operating-websitesbrowserDrive a real website end-to-end — log in, search, navigate, fill forms
discord-serverdiscordReading a conversation back, remembering people, and what she cannot do in a guild

research.md is about 10,500 characters. A skill is allowed to be long, because it is loaded only when it is needed.


Where they live

skills_dir() resolves in this order:

  1. KOTOBA_SKILLS_DIR, if set
  2. <repo>/soul/skills — from a clone
  3. <package>/data/soul/skills — the read-only copy inside the wheel

From a wheel, position 2 becomes ~/.kotoba/soul/skills, so a skill you drop there wins over the packaged one.


The frontmatter

Optional YAML at the top of the file:

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…
KeyEffect
nameThe name skill_view takes, and the title in the list. Defaults to the filename stem.
descriptionThe one line she sees when scanning the list. Falls back to the first paragraph after the first # heading.
requires_toolsetsA list (or a single string). The skill is eligible only when every named toolset is active. Empty or absent means always eligible.
when_to_useRead by humans. Nothing in the code parses it.

No frontmatter at all still works: the title comes from the first # heading and the description from the first non-heading line under it.

Broken YAML degrades to no frontmatter rather than failing.

requires_toolsets is also accepted nested under metadata.hermes.requires_toolsets, for compatibility with that convention.

Measured:

eligible with no toolsets active:      ['research']
eligible with browser + discord:       ['discord-server', 'operating-websites', 'research']

How she picks one

Two different mechanisms, one per mode.

In a conversation, the system prompt lists name — description for every skill, with no toolset filtering, under a rule about balance:

"for everyday things you already know (chat, a quick search, simple math, a basic page) just ACT — don't call skill_view as 'preparation' and don't loop on it … BUT if a skill matches, load it ONCE with skill_view(name) before you start. Load at most one, once, then act."

In work mode, the job is given a filtered list — only skills whose required toolsets are actually active — under an explicit instruction:

"SKILLS (playbooks you can load) — this list is your STEP ZERO. Scan it; if one matches the job, load that ONE with skill_view(name) first and follow what it says. If none matches, there is no step zero: act straight away. Never a second skill, never a re-read."

Why "step zero" is named at all

This block and work mode's BIAS TO ACTION rule contradicted each other for months. One asked for the skill; the other called a skill_view a forbidden "preparation" step. The prohibition won every run.

What went with it was everything a skill knows that nothing else does. A Spanish research request came back as an English report, because the language rule lives in the one page the job was told not to open.

So both rules now name each other:

"BIAS TO ACTION, from step zero onward … These two are one rule with an entry point: step zero is a single read that ends the moment it returns, and everything past it acts. Each covers a failure the other cannot — do not delete either one to satisfy the other."

The cost is one extra model call, plus the skill's own text as input, before the first real action.


skill_list

No arguments. Returns - name: description per skill. Unfiltered — it lists everything in the directory, regardless of which toolsets are active.

Returns nothing when the directory is empty, and she says "I don't have any skills loaded right now."


skill_view

Takes: name. Returns the file's full text, frontmatter included.

The name is slugified and jailed: lowercased, non-alphanumerics collapsed to -, then resolved against the skills directory and checked to be inside it. Measured — skill_view("Research") works, skill_view("../../etc/passwd") returns None.

A name that resolves to nothing is a plain failure and she says "I don't have a skill by that name yet."


Adding your own

Drop a .md file into the skills directory. There is nothing to register and nothing to restart — list_skills() reads the directory on every call.

Three things worth knowing before you write one:

  1. Give it frontmatter. The fallback title-and-first-paragraph convention works, but a description is what she scans, and it is the only thing standing between your skill and being ignored.
  2. Use requires_toolsets if it depends on tools. A browser playbook offered to a job with no browser is guidance she cannot follow.
  3. The filename is the address. skill_view slugifies whatever it is given and looks for <slug>.md, so name the file the way you want it called.

A note on language: research.md is currently the only skill in the tree that carries a rule about which language the deliverable is written in. Every non-research job would lose that rule if the language note were removed from the work brief, which is why it is stated in both places.