huxley
Deploy

Add a Skill

Install a skill, configure it, see it work.

A skill is a Python package. Installing one means: add it to your dependencies, list it in your persona, restart the server. That's the whole loop.

The first-party skills

Huxley ships with seven skills. You don't install them separately — they're part of the workspace and uv sync brings them in:

SkillProvides
huxley-skill-audiobooksLibrary search, playback, bookmarks, resume
huxley-skill-newsWeather (Open-Meteo) + headlines (Google News RSS)
huxley-skill-radioHTTP/Icecast streaming via ffmpeg
huxley-skill-systemVolume, current time
huxley-skill-telegramVoice calls + text messages via MTProto
huxley-skill-timersOne-shot reminders, persistent across restart
huxley-skill-searchWeb search

To use one in a persona, list it under skills::

persona.yaml
skills:
  audiobooks:
    library: audiobooks         # relative to data/
    sounds_path: sounds
    sounds_enabled: true
  news:
    location: "Bogotá"
    latitude: 4.71
    longitude: -74.07
    country_code: "CO"
    language_code: "es"
  system: {}
  timers: {}

Each skill defines what config keys it accepts. Read the skill's README, or see the Cookbook for examples.

Installing a third-party skill

Third-party skills come from PyPI, GitHub, or any other Python package source. Install with uv add from the workspace root:

uv add huxley-skill-spotify              # from PyPI
uv add git+https://github.com/foo/huxley-skill-bar.git  # from git

uv add updates pyproject.toml and uv.lock. The skill becomes available to all personas in this workspace.

To use it, list it in persona.yaml:

skills:
  spotify:
    library_path: spotify/

Then restart the server. Huxley discovers skills at startup via Python entry points — the new skill registers itself automatically when it's pip-installed.

Secrets: Per-skill secret interpolation (${ENV_VAR} in persona.yaml) is on the roadmap but not yet shipped. For now, well-written skills read secrets via os.environ inside their code rather than accepting them as plain config. Check each skill's README to see how it handles credentials.

What does a skill's config look like?

That depends on the skill. There's no schema enforced by the framework — a skill takes whatever config dict the persona gives it.

Conventions you'll see across well-written skills:

  • Paths are relative to data/. library: audiobooks resolves to server/personas/<name>/data/audiobooks/.
  • Per-language overrides go in i18n.
    audiobooks:
      library: audiobooks
      i18n:
        en:
          on_complete_prompt: "The book is finished. Tell the user warmly."
        es:
          on_complete_prompt: "El libro terminó. Anúncialo con calidez."

Removing a skill

Remove it from the persona:

skills:
  audiobooks:
    ...
  # delete the lines for the skill you no longer want

Restart. The skill stops loading. Its data on disk (the SQLite rows it owns) stays — you can add it back later and resume.

To remove the package entirely:

uv remove huxley-skill-foo

Make sure no remaining persona references it; otherwise startup fails.

Conflicts and collisions

Each loaded skill must have a unique name. If two skills both call themselves news, the framework refuses to start. This is intentional — silent shadowing would be a debugging nightmare.

Tool names must also be unique across loaded skills. Two skills both exposing a play() tool will fail. Conventionally, skills prefix tools with their domain: play_book, play_radio, play_song.

Where to find skills

There's no central registry yet. Today, skills live:

  • In the Huxley monorepo (the seven first-party ones).
  • In Mario's personal Huxley extensions repo (eventually).
  • On PyPI under names starting with huxley-skill-.

If you write one and want it indexed, file an issue on the Huxley repo — we'll link it.

A real example

Here's a persona that loads four skills, each with its own configuration:

server/personas/example/persona.yaml
version: 1
name: example
voice: coral
language_code: es
transcription_language: es
timezone: America/Bogota
system_prompt: |
  Eres un asistente cálido. Responde corto. Nunca digas que no — ofrece
  alternativas.
constraints:
  - never_say_no
skills:
  audiobooks:
    library: audiobooks
    sounds_path: sounds
    sounds_enabled: true
    i18n:
      es:
        on_complete_prompt: "El libro terminó. Anúncialo con calidez."

  news:
    location: "Bogotá"
    latitude: 4.71
    longitude: -74.07
    country_code: "CO"
    language_code: "es"
    interests: [politica, local]
    max_items: 8
    sounds_path: sounds
    start_sound: news_start

  timers: {}

  system: {}

Run it:

HUXLEY_PERSONA=example uv run huxley

You now have an agent that plays audiobooks, reads news, sets timers, and tells the time — in Spanish, with a warm voice, never refusing requests.

Next

On this page