Cremind
Agent Skills

Build Your First Skill

A step-by-step walkthrough that takes you from an empty folder to a working Cremind skill the agent can call.

This walkthrough builds a small but complete skill from scratch. By the end you will have a SKILL.md, a working uv-run CLI, and the agent calling it — and you'll understand every piece well enough to build your own. We'll build a toy "notes" skill that lists and appends to a local notes file, because it needs no external service to demonstrate the full loop.

Cremind is version 0.0.1 and community-driven. The built-in skills under app/skills/builtin/ are the best reference for real-world skills — read gmail once you've finished here.

Before you start

You need a Cremind install with at least one profile, and uv available on your PATH (skill scripts run through it). Skills live per-profile at:

<CREMIND_SYSTEM_DIR>/<profile>/skills/

Anything you drop there is hot-reloaded by the per-profile SkillsWatcher — no restart required.

Step 1 — Create the directory

Create a folder named after your skill inside your profile's skills directory:

notes/
├── SKILL.md
└── scripts/
    └── __main__.py

The directory name should match the name you'll put in the frontmatter.

Step 2 — Write the SKILL.md

The frontmatter needs only name and description; the body is the instruction manual the agent reads. Keep the description to one precise sentence — it's how the agent decides the skill is relevant.

---
name: notes
description: List and append short notes stored in a local notes.md file. Use when the user wants to jot something down or read back recent notes.
---

# notes

**Purpose:** A tiny CLI that reads and appends to a local `notes.md`.
Runs via `uv` (PEP 723 inline metadata), so there is no setup.

## CLI Commands
Run `uv run scripts/__main__.py <subcommand>`. Output is JSON.

| Subcommand | Required | Optional |
|---|---|---|
| `list` | — | `--max-results` (10) |
| `add`  | `--text` | — |

## Examples
\`\`\`bash
uv run scripts/__main__.py list --max-results 5
uv run scripts/__main__.py add --text "Buy milk"
\`\`\`

Match what the body promises

The agent reproduces the subcommands and flags exactly as written in the body. If your __main__.py accepts --text, the body must say --text. Drift between the two is the most common reason a skill "doesn't work."

Step 3 — Write the CLI

scripts/__main__.py is a plain argparse CLI. The two things that make it a Cremind skill script are: it declares its dependencies inline with PEP 723 so uv can run it with no venv, and it prints JSON so the agent can parse the result.

# /// script
# requires-python = ">=3.11"
# dependencies = []
# ///
"""notes — list and append local notes."""

import argparse
import json
import sys
from pathlib import Path

NOTES = Path(__file__).resolve().parent / "notes.md"


def cmd_list(max_results: int) -> dict:
    lines = NOTES.read_text(encoding="utf-8").splitlines() if NOTES.exists() else []
    return {"notes": lines[-max_results:]}


def cmd_add(text: str) -> dict:
    with NOTES.open("a", encoding="utf-8") as fh:
        fh.write(text.rstrip() + "\n")
    return {"added": text}


def main() -> None:
    parser = argparse.ArgumentParser(prog="notes")
    sub = parser.add_subparsers(dest="command", required=True)

    p_list = sub.add_parser("list")
    p_list.add_argument("--max-results", type=int, default=10)

    p_add = sub.add_parser("add")
    p_add.add_argument("--text", required=True)

    args = parser.parse_args()
    if args.command == "list":
        result = cmd_list(args.max_results)
    else:
        result = cmd_add(args.text)

    json.dump(result, sys.stdout)
    sys.stdout.write("\n")


if __name__ == "__main__":
    main()

The # /// script ... /// header is the PEP 723 block. uv run reads it, provisions an ephemeral environment with those dependencies (none here), and runs the file. Adding a dependency later is as simple as listing it in that header.

Step 4 — Run it yourself

Before involving the agent, confirm the script works from a terminal:

uv run scripts/__main__.py add --text "First note"
uv run scripts/__main__.py list

You should see JSON output and a notes.md appear next to __main__.py. If uv provisions dependencies on the first run, that's expected.

Step 5 — Let the watcher pick it up

Because the skill lives in a profile's skills directory, the SkillsWatcher notices the new SKILL.md and the sync layer registers it with the agent's tool registry automatically. You can confirm the skill loaded from the Settings page in the web UI, where it appears in the skills list.

Step 6 — Ask the agent to use it

Open a conversation on that profile and ask in plain language:

Add a note: pick up dry cleaning. Then read me my latest notes.

The agent reads your description, recognizes the skill, and runs uv run scripts/__main__.py add --text "..." followed by list, parsing the JSON each step. That's the whole loop: your Markdown taught the agent the interface, and the JSON gave it structured results.

Where to take it next

You now have the core pattern. Real skills extend it in a few directions:

  • Configuration. Declare metadata.environment_variables so the Settings UI renders a config form, and read them from scripts/.env. See the SKILL.md Reference.
  • Secrets that stay local. Store tokens in scripts/.<name>.json, never committed. See Local Tokens & Secrets.
  • Reacting to the world. Add a scripts/event_listener.py, declare metadata.long_running_app, and drop markdown into events/<event_type>/. See Event Listeners.
  • Wiring events to conversations. Subscribe an event to a conversation and dry-run it with cremind skill-events simulate. See Event Subscriptions.

On this page