Skills
Skills are user-created tools that extend Zubo's capabilities. Each skill is a single TypeScript file that the agent can invoke during conversations to perform actions, fetch data, or interact with external services. Skills are hot-loaded — you can add, edit, or remove them without restarting Zubo. User-installed skills run in sandboxed subprocesses for security, ensuring they cannot access Zubo's internal state or database.
Copy-Paste Task Cards
Secure API Access
Enable API auth and create a key before exposing ports beyond localhost.
zubo config set auth.enabled true
zubo auth create-key my-app
Set Local Model Fallback
Keep responses available during provider outages or API quota issues.
zubo config set failover '["openai","ollama"]'
zubo config set providers.ollama.model llama3.3
Common Errors
If auth.enabled is true, all /api/* calls require Authorization: Bearer <key>. Create a new key if needed.
curl -H "Authorization: Bearer YOUR_KEY" http://localhost:3000/api/dashboard/status
Use failover and switch temporarily to a responsive provider or smaller model. Check logs for repeated timeout patterns.
zubo model openai/gpt-4o-mini
zubo logs --follow
If local providers fail, ensure the runtime is running and a model is installed.
ollama serve
ollama pull llama3.3
Quick Start
The fastest way to create a skill is with the built-in scaffolding command:
zubo skills new
This interactive wizard walks you through choosing a name, writing a description, and defining parameters. It generates a ready-to-use handler.ts file in your skills directory.
Even easier — just tell Zubo what you need in plain language:
"Build a skill that checks the weather for a location"
Zubo will generate the entire skill file for you, including parameter definitions, error handling, and a working implementation. You can then refine it by chatting: "Add a units parameter that accepts celsius or fahrenheit."
Single-File Format
Every skill is a single TypeScript file with two exports: a skill configuration object and a default handler function. Here is a complete example:
export const skill = {
name: "weather",
description: "Get current weather for a location",
params: {
location: { type: "string", description: "City or coordinates", required: true },
units: { type: "string", description: "celsius or fahrenheit" }
}
};
export default async function (input: Record<string, unknown>): Promise<string> {
const location = input.location as string;
const units = (input.units as string) || "metric";
const res = await fetch(`https://api.example.com/weather?q=${location}&units=${units}`);
return JSON.stringify(await res.json());
}
The skill object tells the AI what this tool does and what parameters it accepts. The default export is the function that runs when the skill is invoked. This is all you need — no boilerplate, no build step, no configuration files.
Skill Config
The exported skill object defines metadata that Zubo uses to register the tool with the LLM:
| Field | Required | Description |
|---|---|---|
name | Yes | Unique identifier. Lowercase letters, numbers, and underscores only ([a-z0-9_]+). This is how the AI references your skill internally. |
description | Yes | A natural-language explanation shown to the AI. The LLM uses this to decide when to invoke your skill, so be specific and descriptive. |
params | No | An object mapping parameter names to their definitions (see Params Format below). If omitted, the skill accepts no parameters. |
Params Format
Each key in the params object is a parameter name. The value is an object with the following fields:
| Field | Type | Description |
|---|---|---|
type | string | The data type: "string", "number", or "boolean". Determines how the LLM formats the argument. |
description | string | Explains what this parameter does. Shown to the AI to help it provide the correct value. |
required | boolean | Whether the parameter must be provided. Defaults to false if omitted. |
The params are automatically converted to JSON Schema and passed to the LLM as part of the tool definition. You do not need to write JSON Schema yourself — Zubo handles the conversion.
Handler Function
The handler is the default export of your skill file. It is the function that executes when the AI decides to use your skill.
- Must be the default export — use
export default async function - Signature:
async (input: Record<string, unknown>) => Promise<string> - Must return a string — JSON is recommended for structured data. If the handler returns a non-string value, Zubo will automatically coerce it to JSON via
JSON.stringify(). - Access params via
input.paramName— cast to the appropriate type as needed (e.g.,input.location as string) - Available APIs:
fetch(), Bun APIs, built-in Node/Bun modules (fs,path,crypto, etc.) - Cannot import from Zubo internals — skills run in a sandboxed subprocess and have no access to Zubo's core modules, database, or session state
Accessing API Keys
Skills often need API keys or credentials to call external services. Zubo provides a secure way to pass secrets to skill handlers via environment variables:
export default async function (input: Record<string, unknown>): Promise<string> {
const apiKey = process.env.ZUBO_SECRET_WEATHER_API_KEY;
if (!apiKey) return JSON.stringify({ error: "Weather API key not configured" });
const location = input.location as string;
const res = await fetch(
`https://api.weatherapi.com/v1/current.json?key=${apiKey}&q=${location}`
);
return JSON.stringify(await res.json());
}
Secrets referenced in the handler code are automatically passed to the sandboxed subprocess as environment variables with the ZUBO_SECRET_ prefix. Only the secrets that your handler actually references are injected — other secrets are not exposed.
Set secrets via the dashboard or by telling Zubo in chat:
"Store my weather API key: abc123"
You can also use the CLI: zubo secrets set WEATHER_API_KEY abc123
Skill Directory Structure
All skills live under the Zubo workspace directory. Each skill gets its own folder containing a handler.ts file:
~/.zubo/workspace/skills/
├── weather/
│ └── handler.ts # Single-file skill
├── calculator/
│ ├── SKILL.md # Legacy format (still supported)
│ └── handler.ts
├── email_sender/
│ └── handler.ts
└── stock_price/
└── handler.ts
When Zubo starts (or when a skill is added/modified), it scans this directory and hot-loads all valid skills. You can manually place files here or use zubo skills new to scaffold them.
Sandbox Behavior
Security is a core concern when running user-authored code. Zubo enforces the following sandbox rules for user-installed skills:
- Isolated subprocesses — each skill invocation runs in its own Bun subprocess, fully isolated from the main Zubo process
- 30-second timeout — skills are killed after 30 seconds by default. Configure this via
sandbox.timeoutMsin your config - Minimal secrets — only secrets that the handler code explicitly references (via
process.env.ZUBO_SECRET_*) are passed to the subprocess - No internal access — sandboxed skills cannot access Zubo's database, session state, memory store, or any other internal APIs
- Built-in tools are NOT sandboxed — tools like
get_current_datetime,memory_write,memory_search, and other built-in tools run in the main process. Only user-created and registry-installed skills are sandboxed.
Community Skill Registry
The Zubo Skill Registry is a centralized community marketplace at zubo.bot/skills where you can discover, install, star, and publish skills. It features:
- GitHub Authentication — Sign in with your GitHub account to publish skills, star your favorites, and build a profile.
- Auto-Review Scanner — Every submitted skill is automatically scanned for security risks:
- Low risk (approved) — No suspicious patterns detected. Immediately available in the registry.
- Medium risk (flagged) — Network calls, filesystem writes, process spawning, etc. Shows an "Under Review" badge and requires manual review.
- High risk (rejected) — Dynamic code execution (
eval,Functionconstructor), base64 decoding, suspicious domains, sensitive environment variable access. Blocked from the registry.
- Stars & Install Tracking — Star skills you like. Install counts and star counts are tracked and displayed on each skill.
- User Profiles — Auto-created from GitHub. Shows your published skills, total stars, and total installs.
- Reporting — Report suspicious or broken skills anonymously (email required for follow-up).
- Trending & Popular — Browse trending (most installs in 7 days), popular (most starred), and newest skills.
You can also browse and install skills by chatting with Zubo: "Search for a skill that sends emails" or "Install the github_issues skill."
Publishing a Skill
To publish a skill to the community registry:
- Create your skill locally in
~/.zubo/workspace/skills/my-skill/handler.ts. - Authenticate by visiting
zubo.bot/skillsand signing in with GitHub. - Publish via the CLI:
zubo publish my-skill. - Or publish via the web form on the skills page at
zubo.bot/skills. - The auto-review scanner runs automatically. Clean code is approved immediately. Medium-risk code is flagged for manual review. High-risk code is rejected with detailed findings.
- Both the CLI and web form display the auto-review results after submission.
Registry CLI Commands
zubo search <query> # Search the registry
zubo install <name> # Install a skill from the registry
zubo publish <name> # Publish your local skill to the registry
The agent can also perform these actions via the skill_registry built-in tool, which supports search, install, and publish actions. All registry-installed skills are sandboxed by default, just like locally created skills.
Managing Skills
Use the CLI to manage your installed skills:
zubo skills list # List all installed skills
zubo skills new # Create a new skill interactively
zubo skills remove # Remove an installed skill
zubo install weather # Install a skill from the registry
zubo search "email" # Search the registry for skills
zubo publish my_skill # Publish your skill to the registry
Or manage skills conversationally by telling Zubo:
- "List my skills"
- "Remove the weather skill"
- "Build a skill that converts currencies"
- "Search for a skill that checks DNS records"
Built-in Tools Reference
Zubo ships with a comprehensive set of built-in tools that are always available. These are not sandboxed — they run in the main process and have full access to Zubo's internals.
| Tool | Description | Permission |
|---|---|---|
get_current_datetime | Get current date and time with timezone | auto |
memory_write | Save a fact or note to persistent memory | auto |
memory_search | Search memory using semantic and full-text search | auto |
kg_query | Query the knowledge graph for entities and relationships | auto |
kg_update | Add or update entities and relationships in the knowledge graph | auto |
manage_skills | Create, list, or remove user skills | auto |
secret_set | Store a secret (API key, token, etc.) | auto |
secret_list | List all stored secret names | auto |
secret_delete | Delete a stored secret | confirm |
cron_create | Create a scheduled task (cron job or one-shot reminder) | auto |
cron_list | List all scheduled tasks and reminders | auto |
cron_delete | Delete a scheduled task or reminder | auto |
reminder_set | Set a one-time reminder that fires after a delay and auto-deletes | auto |
config_update | Read or update Zubo's own configuration at runtime | auto |
connect_service | Connect an external integration (GitHub, Google, etc.) | auto |
delegate | Delegate a task to a sub-agent | auto |
delegate_task | Delegate a task to an ad-hoc sub-agent | auto |
manage_agents | Create, list, or remove sub-agents | auto |
diagnose | Check recent errors and system health | auto |
manage_workflows | Create and manage multi-step workflows | auto |
manage_teams | Manage agent teams and coordination | auto |
manage_triggers | Manage proactive triggers and alerts | auto |
run_workflow | Execute a defined workflow | auto |
skill_registry | Search and install skills from the registry | auto |
shell | Execute shell commands on the host system | confirm |
file_read | Read the contents of a file | auto |
file_write | Write or append content to a file | confirm |
code_interpreter | Execute Python, JavaScript, or TypeScript code | confirm |
web_search | Search the web via DuckDuckGo | auto |
url_fetch | Fetch and extract content from a web page | auto |
http_request | Make a generic HTTP request (GET, POST, etc.) | auto |
image_generate | Generate images using AI (DALL-E, etc.) | auto |
webhook_manage | Create, list, or remove incoming webhooks | auto |
oauth_manage | Manage OAuth provider connections | auto |
todos | Add, list, complete, update, and remove tasks with priorities, due dates, and tags | auto |
notes | Save, search (FTS5), list, update, delete, and pin notes with tags | auto |
preferences | Set, get, list, and remove user preferences (injected into system prompt) | auto |
topics | Create, switch, list, and archive conversation topics | auto |
follow_ups | Schedule, list, and cancel proactive follow-up messages | auto |
Tools marked confirm require user confirmation before execution. Tools marked auto run immediately without prompting.
Personal Features
Built-in tools for personal productivity — todos, notes, preferences, conversation topics, and scheduled follow-ups. These run in the main process (not sandboxed) and store data in your local SQLite database.
todos
A full task manager built into your agent. Add tasks, set priorities and due dates, tag them for organization, and check them off when done.
| Action | Description |
|---|---|
add | Create a new task with an optional priority (low, medium, high), due date, and tags. |
list | List tasks, optionally filtered by status, priority, tag, or due date. |
complete | Mark a task as done by ID. |
update | Update a task's text, priority, due date, or tags. |
remove | Delete a task by ID. |
Key parameters: text (string), priority ("low" | "medium" | "high"), due (date string), tags (comma-separated), id (task ID for update/complete/remove).
Example:
"Add a todo: finish the API docs, high priority, due Friday, tag: work"
"Show my todos tagged work"
"Complete todo #3"
notes
Save, search, and organize notes with full-text search (FTS5). Pin important notes so they surface first.
| Action | Description |
|---|---|
save | Create a new note with optional title, tags, and pinned status. |
search | Full-text search across all notes using FTS5. |
list | List all notes, optionally filtered by tag or pinned status. |
update | Update a note's content, title, tags, or pinned status. |
delete | Delete a note by ID. |
pin | Toggle the pinned status of a note. |
Key parameters: title (string), content (string), tags (comma-separated), pinned (boolean), query (search string), id (note ID).
Example:
"Save a note: meeting with design team moved to Thursday 2pm, tag: meetings"
"Search my notes for API design"
"Pin note #5"
preferences
Store user preferences as category/key/value pairs. Preferences are automatically injected into the system prompt so the agent always knows your settings.
| Action | Description |
|---|---|
set | Set a preference value for a given category and key. |
get | Retrieve a specific preference by category and key. |
list | List all preferences, optionally filtered by category. |
remove | Delete a preference by category and key. |
Key parameters: category (string, e.g. "display", "communication", "coding"), key (string), value (string).
Example:
"Set my preference: coding/language = TypeScript"
"What are my communication preferences?"
"Remove preference coding/editor"
topics
Organize conversations into named topics. Each topic scopes a separate session, so context stays focused and you can switch between projects easily.
| Action | Description |
|---|---|
create | Create a new topic with a name and optional description. |
switch | Switch the active conversation to a different topic. |
list | List all topics, including archived ones. |
archive | Archive a topic to keep things tidy without deleting history. |
Key parameters: name (string), description (string), id (topic ID for switch/archive).
Example:
"Create a topic called 'Website Redesign'"
"Switch to the API project topic"
"List my topics"
"Archive the old marketing topic"
follow_ups
Schedule proactive follow-up messages that Zubo sends to you at a specified time. Uses one-shot cron jobs under the hood — the job fires once and auto-deletes.
| Action | Description |
|---|---|
schedule | Schedule a follow-up message for a specific time or delay. |
list | List all pending follow-ups. |
cancel | Cancel a scheduled follow-up by ID. |
Key parameters: message (string — what Zubo should say), time (date/time string or natural language like "in 2 hours", "tomorrow at 9am"), id (follow-up ID for cancel).
Example:
"Follow up with me tomorrow at 10am about the deployment"
"List my pending follow-ups"
"Cancel follow-up #2"
Legacy Format (SKILL.md)
Earlier versions of Zubo used a Markdown-based skill definition file called SKILL.md. This format is still fully supported for backward compatibility:
# weather
Get current weather for a location.
## Input Schema
```json
{
"type": "object",
"properties": {
"location": { "type": "string", "description": "City or coordinates" }
},
"required": ["location"]
}
```
If both a SKILL.md file and an exported skill configuration object exist in the same skill directory, the SKILL.md definition takes precedence. For new skills, the inline export const skill format is recommended as it keeps everything in a single file.
Best Practices
- Keep skills focused — one tool, one purpose. A skill that does too many things makes it harder for the AI to know when to use it.
- Write clear descriptions — the AI relies on your description to decide when to invoke the skill. Be specific: "Get current weather for a city" is better than "Weather stuff."
- Return JSON strings for structured data — this makes it easy for the AI to parse and present results to the user.
- Handle errors gracefully — return error messages as JSON strings instead of throwing exceptions. For example:
return JSON.stringify({ error: "API key not configured" }) - Use
process.envfor API keys — never hardcode secrets in your skill files. UseZUBO_SECRET_*environment variables. - Test by chatting — the easiest way to test a skill is to ask Zubo to use it: "Use the weather skill for London"
- Keep response sizes reasonable — extremely large responses consume context window tokens. Summarize or paginate when dealing with large datasets.