Skills
Skills are plugin packages that extend Open Genie with new actions. They're loaded dynamically at boot from the data/skills/ directory.
Structure
Each skill is a directory containing a manifest and action definitions:
data/skills/
├── weather/
│ ├── manifest.json # Skill metadata and configuration
│ └── actions.ts # Action handler implementations
└── media-grab/
├── manifest.json
└── actions.ts
Manifest Format
{
"name": "weather",
"version": "1.0.0",
"description": "Fetch weather forecasts and current conditions",
"actions": ["get_weather", "get_forecast"],
"cron": [
{
"schedule": "0 6 * * *",
"action": "get_forecast"
}
],
"config": {
"api_key": {
"type": "string",
"description": "Weather API key",
"required": true
},
"units": {
"type": "string",
"description": "Temperature units",
"default": "metric"
}
}
}
Manifest Fields
| Field | Type | Required | Description |
|---|---|---|---|
name | string | yes | Unique skill identifier |
version | string | yes | Semver version |
description | string | yes | Human-readable description |
actions | string[] | yes | List of action names this skill provides |
cron | array | no | Auto-scheduled jobs to create |
config | object | no | Configuration schema for the skill |
Action File
The actions.ts (or actions.js) file must export action definitions that match the action registry format:
// data/skills/weather/actions.ts
export const get_weather = {
name: "get_weather",
description: "Get current weather for a location",
parameters: {
type: "object",
properties: {
location: {
type: "string",
description: "City name or coordinates",
},
},
required: ["location"],
},
handler: async (params: { location: string }) => {
const response = await fetch(
`https://api.weather.example/current?q=${params.location}`
);
const data = await response.json();
return { output: JSON.stringify(data) };
},
};
export const get_forecast = {
name: "get_forecast",
description: "Get 5-day weather forecast",
parameters: {
type: "object",
properties: {
location: { type: "string", description: "City name" },
},
required: ["location"],
},
handler: async (params: { location: string }) => {
// ... fetch forecast
return { output: JSON.stringify(forecastData) };
},
};
Loading Process
At boot, loadSkills() (lib/skills/loader.ts):
- Scans each subdirectory in
data/skills/ - Reads and validates
manifest.json - Dynamically imports
actions.tsoractions.js - Registers each exported action in the global
actionRegistry - If the manifest defines
cronentries, schedules them
data/skills/weather/
→ Read manifest.json
→ Import actions.ts
→ Register get_weather, get_forecast
→ Schedule daily forecast job
Creating a Skill
-
Create a directory under
data/skills/:mkdir data/skills/my-skill -
Write a
manifest.json:{"name": "my-skill","version": "1.0.0","description": "What my skill does","actions": ["my_action"]} -
Write
actions.ts:export const my_action = {name: "my_action",description: "What this action does",parameters: {type: "object",properties: {input: { type: "string", description: "Input value" },},required: ["input"],},handler: async (params: { input: string }) => {return { output: `Result: ${params.input}` };},}; -
Restart the server — the skill will be loaded automatically.
REST API
GET /api/skills
Returns all loaded skills with their manifests.
Key Files
| File | Purpose |
|---|---|
lib/skills/loader.ts | Skill discovery, loading, and registration |
data/skills/*/manifest.json | Skill metadata |
data/skills/*/actions.ts | Action implementations |