File format
SKILL.md is UTF-8 text. It starts with a line of three hyphens, then YAML frontmatter,
then a closing line of three hyphens. Everything after that is the body: Markdown instructions the
employee receives when it loads the skill. Nebo finds the file by name without regard to case.
---
name: expense-review
description: Check submitted expenses against the company policy and flag anything that needs a receipt or an approval. Use when someone asks to review expenses or a reimbursement request.
version: "1.2.0"
author: Example Co
license: MIT
tags: [finance, expenses]
triggers: [reimbursement, expense report]
platform: [macos, linux, windows]
capabilities: [network]
metadata:
allowed_domains: [api.example.com]
secrets:
- key: EXAMPLE_API_KEY
label: Example API key
hint: Create one at https://example.com/settings/keys
required: true
requires:
- name: receipt-capture
version: ">=1.0.0"
plugins:
- name: example-ledger
version: ">=2.0.0"
optional: true
---
# Expense review
Follow the policy in references/policy.md ... Frontmatter fields
Only name and description are required. A field with the wrong type fails
the whole file, so the skill does not load. Keys not listed here are ignored.
| Field | Type | Default | Meaning |
|---|---|---|---|
name | string | required | Identifier. [a-z0-9-], no leading, trailing or double hyphen, at most 64 bytes. Match the folder name. |
description | string | required | What the skill does and when to use it. At most 1,024 bytes. Shown in the employee’s skill list and scored by find_skills. |
version | string | "1.0.0" | Semantic version. Checked by other skills’ requires ranges. Quote it. |
triggers | list of strings | [] | Words and phrases that describe when the skill applies. Up to three appear in the skill list; all count in find_skills. They never load a skill by themselves. |
platform | list of strings | all | macos, linux, windows. The skill loads only where one matches. |
capabilities | list of strings | [] | network and storage widen the script sandbox. See Script sandbox. |
metadata | map | {} | Holds secrets and allowed_domains. See below. |
requires | list of {name, version} | [] | Other skills this skill builds on. name is the short skill name; version is a semver range, default "*". |
plugins | list of {name, version, optional} | [] | Plugins this skill uses. version defaults to "*", optional to false. |
dependencies | list of strings | [] | Short names of skills that must be present. |
priority | integer | 0 | Orders skills in management lists. It does not affect which skill an employee chooses. |
author | string | "" | Stored for display. |
license | string | "" | Stored for display. |
compatibility | string | "" | Free-text note, at most 500 bytes. |
tags | list of strings | [] | Stored for display. |
allowed-tools | string | "" | A space-separated string. Stored; not enforced. |
When a skill named in requires or dependencies is missing or outside the
range, or a required plugin is not installed, the skill still loads and is marked degraded. The
owner sees what is missing.
Secrets
Declare the credentials a skill's scripts need under metadata.secrets. The owner
provides the values, and Nebo stores them encrypted for that skill.
metadata:
secrets:
- key: EXAMPLE_API_KEY
label: Example API key
hint: Create one at https://example.com/settings/keys
required: true | Key | Meaning |
|---|---|
key | Environment variable name the script receives. Required. |
label | Name shown to the owner. |
hint | Where to get the value. |
required | When true, execute refuses to run the skill’s scripts until the value is set. Default false. |
An employee sets a value with configure_skill. You can reference a secret in the body
as ${secret.KEY}, though scripts should read the environment variable instead.
Template variables
Nebo replaces these variables in the body when an employee loads the skill. They are not replaced inside other files; scripts read environment variables instead.
| Variable | Value |
|---|---|
${NEBO_SKILL_DIR} | Absolute path of the skill’s folder. |
${NEBO_DATA_DIR} | A writable folder for this skill: <data>/appdata/skills/<name>. Created on first use. |
${NEBO_USER_NAME} | The owner’s name. |
${NEBO_OS} | The operating system. |
${NEBO_ARCH} | The processor architecture. |
${plugin.SLUG_BIN} | Path of a declared plugin’s binary. The slug is uppercased, with hyphens as underscores: example-ledger becomes ${plugin.EXAMPLE_LEDGER_BIN}. |
${secret.KEY} | The value of a declared secret. |
$ARGUMENTS | The args passed to use_skill. When the body has no $ARGUMENTS, the arguments are appended after it. |
Skill tools
Employees work with skills through these tools. use_skill is always available; the
others load when an employee needs them.
| Tool | Parameters | What it does |
|---|---|---|
use_skill | name, args | Loads a skill. Returns the expanded body, the folder path, and the first 20 file names. Re-enables a disabled skill. |
find_skills | query | Returns up to 10 skills ranked by name, trigger and description matches. |
read_skill_file | name, path | Reads a file in the skill’s folder. Without path, lists the files and their sizes. |
save_skill | name, content | Writes a complete SKILL.md to the user skills folder. Marketplace skills cannot be overwritten. |
delete_skill | name | Deletes a user skill. |
install_skill | code | Installs a marketplace skill from its SKIL- install code. |
configure_skill | name, key, value | Sets a secret. Without key, shows what the skill needs. |
rate_skill | name, rating, review | Rates a marketplace skill from 1 to 5. |
read_skill_reviews | name | Reads a marketplace skill’s reviews. |
execute | skill, script, args, timeout | Runs a script from a skill’s folder. See Script sandbox. |
Script sandbox
The execute tool runs one script from a skill's folder. It chooses the runtime from the
file.
| Script | Runtime |
|---|---|
.py | Python, through Nebo’s bundled uv when present, otherwise python3. |
.ts, .js | Nebo’s bundled Bun when present, otherwise node. |
bin/<file> or binary | Run directly. |
.sh | Not supported. |
Each run starts with an empty environment and these variables:
| Variable | Value |
|---|---|
SKILL_ARGS | The args object as JSON. |
Each secret key | The decrypted secret value. |
<PLUGIN>_BIN | Path of each declared plugin’s binary. Plugin folders are also added to PATH. |
import json, os
args = json.loads(os.environ.get("SKILL_ARGS", "{}"))
api_key = os.environ["EXAMPLE_API_KEY"]
print(json.dumps({"checked": len(args.get("items", []))})) The script runs in a temporary copy of the skill folder, with a default timeout of 30 seconds. On macOS and Linux it runs in an operating-system sandbox:
- It cannot read credential stores, browser and keychain data, or Nebo's own settings and database.
- It can write only to its working folder and
/tmp/nebo. Thestoragecapability adds Nebo's data folder. - It has no network access. The
networkcapability allows package registries and the domains listed inmetadata.allowed_domains.
Files and limits
| Item | Rule |
|---|---|
| Resources | Every file except SKILL.md, manifest.json, signatures.json and dotfiles. |
| Executables | Files under scripts/ or bin/, and a root file named binary. |
| Paths | Relative to the skill folder. Paths with .. or that escape the folder are rejected. |
| Disabled skill | A folder with SKILL.md.disabled instead of SKILL.md. |
| Skill list | Each entry is cut at 250 characters. When the whole list exceeds 8,000 characters, shared skills are shortened first; an employee’s own skills keep their full entries. |
Limits for files you publish to the marketplace are on the Publishing page.