Authoring Custom Skills
A skill is a markdown file with YAML frontmatter and step-by-step instructions in the body. You can add your own in two ways:
- Let the agent learn it — send a request with no matching skill. If the agent succeeds via best-effort reasoning, the system persists a new skill markdown automatically (via the
skill_learnedevent). This is the fastest path — you get new skills as a side effect of running the product. - Write the markdown directly — drop a file in the workspace’s skill folder with the right frontmatter + body structure, and the agent picks it up on the next message.
This page covers option 2. For option 1, see How skills learn themselves below.
File location
Per-workspace skills live at:
data/knowledge/<workspace_id>/skills/<slug>.mdWhere <workspace_id> is your workspace’s UUID and <slug> is a kebab-case version of the skill name (e.g. post-to-x, download-bank-statements).
If you’re self-hosting the backend (cto-gui-libvirt-backend), write the file to that path directly. In cloud Direktor, use the Skills Editor in the workspace settings (coming soon — for now contact support to add a skill).
Frontmatter schema
Every skill starts with a YAML block. All fields:
---
slug: post-to-x # kebab-case, matches filename
type: skill # always "skill"
name: Post to X (Twitter) # human-readable title
category: marketing # optional but recommended
difficulty: medium # low / medium / high
tags: [social, x, twitter, ugc] # for search + ranking
tool_url: https://x.com # optional, linkable from the UI
status: complete # complete / draft / deprecated
learned_by: user # user / agent / cli
learned_at: 2026-04-20T12:00:00Z # ISO timestamp
summary: > # one-line description — shown in catalog
Post a message + optional media attachment to X, capture the post URL,
and log engagement metrics.
---The agent uses category, tags, and summary to rank skills when it’s looking for a match on a user’s request. difficulty affects how much time budget the agent allocates to the skill before escalating. status: draft hides the skill from catalog UI but leaves it invokable by name.
Body structure
After the frontmatter, write the instructions as markdown. Use the [step-id] Step N: Description heading convention to anchor each step so the agent can reference specific steps mid-skill:
## [prereqs] Step 1: Validate Prerequisites
Check that:
- The user has connected their X account (see [auth-x](#)).
- The message body is ≤ 280 chars.
- If media is attached, it's a supported format.
If any prereq fails, return to the user and ask before continuing.
## [compose] Step 2: Compose the Post
Draft the post. If the user gave you exact text, use it verbatim.
Otherwise, draft three variants and pick the strongest based on:
- Single clear point (no nested ideas)
- Specific (names, numbers, dates) not abstract
- Matches the caller's previous tone (look at last 3 posts)
## [publish] Step 3: Publish
Open https://x.com in Firefox. Sign in if needed.
Click the compose button. Paste the text. Attach media if present.
Click Post. Wait for the confirmation.
## [capture] Step 4: Capture Outcome
Screenshot the published post.
Copy the post URL from the address bar.
Log `{url, text, published_at}` to the skill's workbook.
## [handoff] Step 5: Return to User
Reply to the user with the post URL and the first engagement metric
snapshot. Recommend a 24h follow-up check.Conventions from the built-in skills
Looking at the 7 shipping skills, these patterns hold:
- Workbook path at the top if the skill persists structured data:
/opt/agent/workspace/<slug>.xlsxwith pre-defined sheets. - Coordinator URL for inter-agent calls:
http://10.101.0.1:9000. AGENT_ROLE_IDenv var to identify the invoking node in milestones + inter-node messages.- Firefox for live web interactions inside the agent’s desktop VM — never source facts from training data when the world is moving under the skill (prices, job postings, ad dashboards, X posts).
- Screenshots for audit — screenshots go to
/opt/agent/workspace/screenshots/<skill>/. - Hard stops + decision gates — any action that spends money, posts publicly, or binds a commitment requires explicit approval first.
Copy these conventions unless you have a good reason to break them.
Reference example
cto-gui-libvirt-backend/app/services/agent_skills/find-job.md is the canonical example. It covers:
- A full YAML frontmatter.
- Nine steps with anchors.
- Inbox-vs-apply mode detection.
- Explicit call-outs for “Never source from training data”.
- Daily-loop design (build-profile runs once, plan-the-day onwards run daily).
When in doubt, open find-job.md side-by-side and mirror its shape.
How skills learn themselves
If you send a node a request with no matching skill, the agent attempts the task using general reasoning. When it finishes:
- The system detects a
skill_learnedevent from the chat stream (org_chat_service.py:1872). - The agent writes a draft skill markdown to
data/knowledge/<workspace_id>/skills/<slug>.md. - The draft is marked
status: draftandlearned_by: agent. - You review the draft in the Skills Editor and promote it to
status: complete(or edit first).
This is how the catalog grows organically — high-demand tasks become skills without anyone sitting down to write markdown by hand.
Next
- Skills Catalog — the 7 skills that ship with Direktor.
- MCP Example Flows — how a skill looks when invoked from Claude Desktop.