Skill Manager¶
Maturity — Mature ⬤⬤⬤⬤⬤
Long-lived and actively maintained. Depend on it freely. Rated by repository health, not version.
In a nutshell
The Skill Manager is the part of OVOS that finds all your installed skills and starts them when the assistant boots. It also decides when each skill is allowed to run. Some skills need the internet or a screen before they can work. The Skill Manager keeps re-checking, so a skill you install later appears without a restart. See Skill Installer for how skills get added, or the Glossary for terms.
Module: ovos_core.skill_manager.SkillManager: ovos_core/skill_manager.py
The SkillManager is a core component of ovos-core. It is a daemon Thread that owns the full lifecycle of skill plugins: discovery, loading, connectivity-gating, and graceful shutdown.
In plain terms: when OVOS starts, the SkillManager finds every installed skill. It decides which ones are allowed to load right now. Some skills need the network or a screen first. The SkillManager starts the ready skills and re-scans periodically, so newly installed skills show up without a restart.
Technical Reference
-
SkillManager.run():ovos_core/skill_manager.py. This is the main loop. It re-scans for new skills every 30 s viaself._stop_event.wait(30). -
SkillManager.load_plugin_skills():ovos_core/skill_manager.py. It loads discovered skills viaPluginSkillLoader(fromovos_workshop.skill_launcher). It applies each skill'sRuntimeRequirements(network_before_load/internet_before_load) as the connectivity gate. A plugin skill that keeps failing to load backs off exponentially (starting at 30 s, doubling up to a 15-minute cap) instead of retrying on every scan, andloaded_newonly reports skills that actually finished loading. -
SkillManager._sync_skill_loading_state():ovos_core/skill_manager.py. It queries connectivity (viaovos.PHAL.internet_check/ GUI state) and emitsmycroft.network.connected/mycroft.internet.connected. The actual gating happens inload_plugin_skills(), and only whenskills.use_deferred_loadingis enabled.
Skill Discovery¶
Skills are Python packages. They register themselves via the opm.skill entry point group. The older ovos.plugin.skill group is still accepted as a deprecated alias. ovos-plugin-manager discovers them with find_skill_plugins(), which returns a {skill_id: SkillClass} dict.
Connectivity Gating¶
Note
RuntimeRequirements (network_before_load, internet_before_load, requires_gui, …) is
a deprecated mechanism. See Runtime Requirements for the
full picture. The gating described below is opt-in: it only applies when
skills.use_deferred_loading is set to true in config. With the default configuration,
every installed skill loads unconditionally at startup regardless of its declared
requirements.
When skills.use_deferred_loading is enabled, skills declare their runtime requirements in
RuntimeRequirements, and the skill manager defers loading a skill until those requirements
are met:
| Event | Action |
|---|---|
| Startup (offline) | Load skills with no network/internet requirement |
mycroft.network.connected |
Load skills requiring network |
mycroft.internet.connected |
Load skills requiring internet |
mycroft.gui.available |
Load skills requiring GUI |
Network/internet state is queried from PHAL at startup via ovos.PHAL.internet_check. It falls back to a direct HTTP check if PHAL is unavailable.
Loading a Skill¶
The loading process follows this flow:
flowchart LR
A["load_plugin_skills()"] --> B["find_skill_plugins()<br/>{skill_id: SkillClass}"]
B --> C["_get_plugin_skill_<br/>loader()<br/>build a<br/>PluginSkillLoader"]
C --> D["_load_plugin_skill()<br/>instantiate &<br/>start the skill"]
Diagram: skill loading runs left to right from load_plugin_skills, through find_skill_plugins and building a PluginSkillLoader, to _load_plugin_skill instantiating and starting the skill.
Each skill gets its own bus connection when websocket.shared_connection is false in config (see _get_internal_skill_bus()), providing isolation from "BusBricker" style attacks.
What a load failure looks like¶
A skill whose class raises during instantiation is logged loudly in skills.log and skipped.
It registers no intents and can never match. The signatures to grep for:
ERROR - Failed to load skill: <skill_id> (<exception>) # skill_launcher, traceback follows
ERROR - Skill <skill_id> failed to load
ERROR - Load of skill <skill_id> failed! # skill_manager, traceback follows
On the bus, a failed load emits mycroft.skills.loading_failure. Its true complement is
mycroft.skills.loaded (plural), fired on every successful load path. The singular
mycroft.skill.loaded below is an extra emission specific to the Skill Manager's
plugin-skill path, with no failure counterpart. "Installed but never matches" reports should check for these before
anything else. See the Troubleshooting
funnel.
Blacklisting¶
Skills listed in skills.blacklisted_skills in mycroft.conf are skipped at load time. The recommended approach is to uninstall unwanted skills rather than blacklist them.
Intent Training¶
Each successfully loaded skill is announced on the bus as mycroft.skills.loaded
(plural) with the skill id. This event fires on every load path. Plugin-skill
loads through the manager also emit a singular mycroft.skill.loaded. Both
are useful for tooling that waits for a specific skill to become available. After new skills are loaded, the manager requests pipeline
re-training:
The manager emits mycroft.skills.train and moves on. It waits for nothing. The
Padatious pipeline plugin does announce mycroft.skills.trained when its training round
completes (on both the trained and nothing-new-to-train paths), so tooling that needs a
deterministic "training done" signal can watch for that. The manager itself never does.
The manager itself never blocks on mycroft.skills.trained. The event exists (see
above) but nothing in core does a wait_for_response on it. An earlier
version blocked on wait_for_response(..., "mycroft.skills.trained", timeout=60), which
stalled boot for a full minute on every install without a deferred-training engine, since
no such engine was there to answer. A single responder could not speak for every loaded
pipeline anyway.
Settings File Watcher¶
When enabled, a FileWatcher monitors ~/.config/mycroft/skills/*/settings.json. Any change emits:
Bus Events Handled¶
| Event | Handler |
|---|---|
skillmanager.list |
send_skill_list |
skillmanager.activate |
activate_skill |
skillmanager.deactivate |
deactivate_skill |
skillmanager.keep |
deactivate_except |
mycroft.network.connected |
handle_network_connected * |
mycroft.internet.connected |
handle_internet_connected * |
mycroft.gui.available |
handle_gui_connected * |
mycroft.network.disconnected |
handle_network_disconnected * |
mycroft.internet.disconnected |
handle_internet_disconnected * |
mycroft.gui.unavailable |
handle_gui_disconnected * |
* The six connectivity events are subscribed only when deferred loading is on, which it
is not by default. On a stock install nothing is listening on them, so emitting one changes
nothing. The four skillmanager.* events above are always subscribed.
Read next: Intent Service · Skill Installer Related: ovos-core Overview · Anatomy of a Skill · Runtime Requirements