Skip to content

Creating Custom Skills

A custom skill is a directory with two files: a manifest and an executor.

Minimal Example

~/my-skills/
  joke/
    skill.json
    index.js

skill.json

json
{
  "name": "joke",
  "description": "Tell a random programming joke",
  "tools": [
    {
      "name": "get_joke",
      "description": "Returns a random programming joke",
      "parameters": {
        "type": "object",
        "properties": {},
        "required": []
      }
    }
  ]
}

index.js

js
const jokes = [
  'Why do programmers prefer dark mode? Because light attracts bugs.',
  'A SQL query walks into a bar, walks up to two tables and asks: Can I join you?',
]

export async function get_joke() {
  return jokes[Math.floor(Math.random() * jokes.length)]
}

Point Airupt at your skills directory:

yaml
skills:
  custom_skills_dir: ~/my-skills

Restart Airupt and test:

Tell me a joke.

Tool Parameters

Tools can accept parameters:

skill.json

json
{
  "name": "weather",
  "description": "Get current weather for a city",
  "tools": [
    {
      "name": "get_weather",
      "description": "Fetch current weather for a given city",
      "parameters": {
        "type": "object",
        "properties": {
          "city": {
            "type": "string",
            "description": "City name, e.g. Berlin"
          }
        },
        "required": ["city"]
      }
    }
  ]
}

index.js

js
export async function get_weather({ city }) {
  const res = await fetch(`https://wttr.in/${encodeURIComponent(city)}?format=3`)
  return res.text()
}

TypeScript Skills

TypeScript is supported. Name your file index.ts and Airupt will compile it on load:

ts
export async function get_weather({ city }: { city: string }): Promise<string> {
  const res = await fetch(`https://wttr.in/${encodeURIComponent(city)}?format=3`)
  return res.text()
}

Skill Lifecycle

  • Skills are loaded once at startup
  • To reload after editing: airupt skills reload (no full restart needed)
  • Errors in a skill are caught and reported; other skills continue running

Best Practices

  • Keep each tool focused on one action
  • Return plain strings or JSON-serialisable objects
  • Use description fields thoroughly — the LLM uses them to decide when to call your tool
  • Avoid side effects in skill.json; all logic goes in index.js

Released under the MIT License.