xfetch has a plugin system that allows extending functionality through standalone executables. Plugins communicate with the core via a JSON protocol over stdin/stdout.
Architecture Overview
Plugins are standalone executables named xfetch-plugin-<name> (or xfetch-plugin-<name>.exe on Windows). They are spawned as child processes by the xfetch core.
xfetch core ---> stdin (JSON request) ---> plugin process
xfetch core <--- stdout (JSON response) <--- plugin process
xfetch core <--- stderr (error messages) <--- plugin process
Plugin Kinds
There are two types of plugins:
| Kind | Identifier | Purpose |
|---|---|---|
| Logo Animation | logo_animation | Animates ASCII logos with color effects |
| Info Provider | info_provider | Returns system or external information lines |
JSON Wire Protocol
Protocol Version: 1
Info Provider Protocol
Request:
{
"version": 1,
"kind": "info_provider",
"args": {
"username": "xscriptor",
"max_lines": 3
}
}
The args field contains plugin-specific arguments from the configuration. If no arguments are configured, args is null.
Response:
{
"lines": [
"\uf09b X (@xscriptor)",
"\uf005 114 stars",
"\uf441 33 repos"
]
}
Logo Animation Protocol
Request:
{
"version": 1,
"kind": "logo_animation",
"lines": [
" __ __",
" \\ \\/ /",
" \\ /"
],
"frames": [
["frame 1 line 1", "frame 1 line 2"],
["frame 2 line 1", "frame 2 line 2"]
],
"args": {
"fps": 12,
"duration_ms": 1200,
"loop": false,
"style": "sweep"
}
}
The lines field contains the current ASCII logo. The frames field contains optional pre-loaded frame sets. The args field contains animation parameters.
Response:
{
"frames": [
{
"delay_ms": 83,
"lines": ["\u001b[31mcolored line\u001b[0m", "\u001b[32mnext line\u001b[0m"]
}
]
}
Each frame has a delay_ms (how long to display the frame) and lines (the frame content, potentially with ANSI escape codes for colors).
Error Handling
Plugins must write error messages to stderr and exit with a non-zero status code on failure.
Plugin Discovery
When executing a plugin, xfetch searches for the binary in this order:
- Explicit path: If the plugin name contains a path separator, use it as a direct file path
- PATH: Search
$PATHforxfetch-plugin-<name> - User plugin directory:
~/.config/xfetch/plugins/(Linux/macOS) or%APPDATA%/xfetch/plugins/(Windows) - Workspace target directories: Various paths relative to the current working directory, including
./plugins/<name>/target/release/ - Development directories: Ancestor directory searches for development setups
Plugin Installation
# Install from a local directory
xfetch plugin install ./my-plugin
# Install from the official plugin repository
xfetch plugin install animate-logo
# Install from a custom git repository
xfetch plugin install my-plugin --repo https://github.com/user/plugins.git
Install Process
- If the plugin name is a local path, use it directly
- Otherwise, search locally in
./<name>/,./plugins/<name>/, or./plugins/plugins/<name>/ - If not found locally, clone the plugin repository (
https://github.com/xfetch-cli/plugins.gitby default) - Run
cargo build --releasein the plugin directory - Copy the built binary to
~/.config/xfetch/plugins/xfetch-plugin-<name>
Plugin Management
# List installed plugins
xfetch plugin list
# Remove a plugin
xfetch plugin remove animate-logo
Official Plugins
Each plugin has its own reference page with full configuration details, arguments, and output examples.
| Plugin | Kind | Description |
|---|---|---|
| animate-logo | logo_animation | Animated ASCII logos with color effects (sweep, wave, rainbow, sparkle, breathing, frame) |
| docker | info_provider | Docker container statistics (total, running, paused, stopped) |
| github-stats | info_provider | GitHub profile stats (stars, repos, PRs, issues, followers) |
| music-player | info_provider | Currently playing music from MPD and/or Spotify |
| weather | info_provider | Current weather via wttr.in (condition, temp, wind, humidity) |
| timezone | info_provider | Local time, date, timezone name, and UTC offset |
| user-info | info_provider | User account info (UID, GID, home, shell, groups) |
| display-resolution | info_provider | Monitor resolution and refresh rate (cross-platform) |
| theme-detection | info_provider | Desktop theme detection (GTK, KDE Plasma) |
| theme-manager | info_provider | Theme registry browser and installer |
| chocolatey | info_provider | Chocolatey package count (Windows) |
| temperature | info_provider | CPU/SoC temperature from kernel thermal zones |
WebAssembly Plugins
The following reference plugins are compiled to WebAssembly and run in a sandbox with capabilities declared in their manifest:
| Plugin | Language | Description |
|---|---|---|
wasm-crypto | Rust | Crypto spot prices through the allowlisted Coinbase API |
wasm-ip-geo | Python component | Public IP, location, network and timezone via ipapi.co |
wasm-pacman | Go | Repository and AUR package counts through allowlisted pacman |
wasm-proc | C | Load average, uptime and memory from a read-only /proc |
Wasm guests reuse the same JSON protocol and support host calls (HTTP,
processes, files, environment) plus per-guest limits. See the WebAssembly
guests reference in the xfetch repository (docs/WASM.md) for the manifest
schema and the xfetch wasm tooling.
Writing Custom Plugins
Binary Naming Convention
xfetch-plugin-<name> (Linux/macOS)
xfetch-plugin-<name>.exe (Windows)
Minimal Plugin Skeleton (Rust)
[package]
name = "xfetch-plugin-my-plugin"
version = "0.1.0"
edition = "2024"
[dependencies]
serde = { version = "1", features = ["derive"] }
serde_json = "1"
xfetch-plugin-api = { git = "https://github.com/xfetch-cli/api", package = "xfetch-plugin-api" }
use xfetch_plugin_api::{
read_info_plugin_args_or_default,
write_info_lines,
};
#[derive(Debug, Default, serde::Deserialize)]
struct PluginArgs {}
fn main() {
let _args = match read_info_plugin_args_or_default::<PluginArgs>() {
Ok(value) => value,
Err(err) => {
eprintln!("{}", err);
std::process::exit(1);
}
};
if let Err(err) = write_info_lines(vec!["Hello from plugin".to_string()]) {
eprintln!("{}", err);
std::process::exit(1);
}
}
Testing Plugins
# Test an info plugin
echo '{"version":1,"kind":"info_provider","args":null}' \
| ./target/release/xfetch-plugin-my-plugin
# Test a logo animation plugin
echo '{"version":1,"kind":"logo_animation","lines":["hello"],"args":{"fps":12}}' \
| ./target/release/xfetch-plugin-my-plugin
Plugin API Crate
The xfetch-plugin-api crate (source at github.com/xfetch-cli/api) provides all the types and helpers needed for plugin development:
- Protocol types:
AnimationFrame,EmptyArgs,InfoPluginRequest,InfoPluginResponse,LogoAnimationArgs,LogoAnimationRequest,LogoAnimationResponse,PluginKind - Entrypoint helpers:
read_logo_animation_request(),read_info_plugin_request(),read_info_plugin_args_or_default(),write_logo_animation_frames(),write_info_lines() - IO helpers:
read_json_from_stdin(),write_json_to_stdout() - Timeout helpers:
with_timeout()and theTimedOuterror — run plugin work under a time budget and respond gracefully when it elapses - Error types:
PluginApiErrorenum with variants for Io, Serialize, Deserialize, InvalidProtocolVersion, InvalidPluginKind, InvalidArgs, EmptyAnimationFrames
The optional timeout_secs config key applies to info_plugins, logo_animation, and config_providers as a safety net in seconds: the core kills the plugin process if it runs longer, while the plugin's own with_timeout budget is the primary control.
Guidelines
- Keep plugins focused on a single responsibility
- Write errors to stderr and exit with non-zero status
- Do not use display or terminal libraries (the core handles rendering)
- Minimize dependencies
- Use the shared
xfetch-plugin-apicrate for protocol types - Handle network or I/O failures gracefully