Skip to Content
SkillsAuthoring Custom Skills

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:

  1. 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_learned event). This is the fastest path — you get new skills as a side effect of running the product.
  2. 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>.md

Where <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>.xlsx with pre-defined sheets.
  • Coordinator URL for inter-agent calls: http://10.101.0.1:9000.
  • AGENT_ROLE_ID env 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:

  1. The system detects a skill_learned event from the chat stream (org_chat_service.py:1872).
  2. The agent writes a draft skill markdown to data/knowledge/<workspace_id>/skills/<slug>.md.
  3. The draft is marked status: draft and learned_by: agent.
  4. 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

Last updated on