xfetch plugins are standalone executables that communicate with the core via stdin/stdout using a JSON protocol. This document describes the protocol, the required binary naming conventions, and best practices for creating your own plugin.
The shared protocol crate is maintained in xfetch-cli/api.
Official plugins and third-party plugins are expected to use this crate instead of reimplementing protocol parsing manually.
- How It Works
- Binary Naming
- Discovery
- Protocol
- Request Format
- Response Format
- Example Plugin
- Testing
- Contribution Guidelines
- xfetch discovers the plugin binary (see Discovery).
- xfetch spawns the plugin process.
- xfetch writes a JSON request to the plugin's stdin.
- The plugin processes the request and writes a JSON response to stdout.
- xfetch reads the response and uses it for rendering.
- Errors are written to stderr; the plugin exits with a non-zero status on failure.
The plugin binary must follow this naming convention so xfetch can discover it:
xfetch-plugin-<name>| Platform | Example |
|---|---|
| Linux / macOS | xfetch-plugin-animate-logo |
| Windows | xfetch-plugin-animate-logo.exe |
The Cargo.toml name field should follow the same convention:
[package]
name = "xfetch-plugin-<name>"
version = "0.1.0"
edition = "2024"
When running a plugin, xfetch searches for the binary in the following order:
- Explicit path from the configuration (if provided).
PATHenvironment variable, looking forxfetch-plugin-<name>.- User plugin directory:
~/.config/xfetch/plugins/. - Workspace target directory of the plugins repository:
target/release/. - Legacy development path:
./plugins/<name>/target/release/.
The plugin protocol uses JSON over stdin/stdout. The plugin reads exactly one JSON object from stdin and writes exactly one JSON object to stdout.
{
"version": 1,
"kind": "<plugin_kind>",
"lines": ["line 1", "line 2"],
"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"
}
}
| Field | Type | Description |
|---|---|---|
version |
u32 |
Protocol version (currently 1). |
kind |
string |
Plugin type. Currently only "logo_animation" is supported. |
lines |
array[string] |
Logo ASCII art, one line per element. |
frames |
array[array[string]] or null |
Optional pre-loaded frame sets for frame-based animation. |
args |
object |
Plugin-specific arguments from the user config. |
{
"frames": [
{
"delay_ms": 83,
"lines": ["colored line 1", "colored line 2"]
}
]
}
| Field | Type | Description |
|---|---|---|
frames |
array[Frame] |
Array of animation frames (required). Must not be empty. |
| Field | Type | Description |
|---|---|---|
delay_ms |
u64 |
Milliseconds to display this frame before advancing. |
lines |
array[string] |
Frame content (may include ANSI escape codes for color). |
The animate-logo plugin is the reference
implementation. Its source is at plugins/animate-logo/src/main.rs.
Minimal plugin skeleton in Rust using the shared crate:
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);
}
}
Test your plugin manually by writing a request file and piping it:
echo '{"version":1,"kind":"logo_animation","lines":["hello"],"args":{"fps":12}}' \
| ./target/release/xfetch-plugin-my-plugin
You can also install it locally and test it with xfetch:
xfetch plugin install my-plugin
xfetch
-
Create a new directory under
plugins/named after your plugin (e.g.,plugins/my-plugin/). -
Include a
Cargo.tomlwith the package name following thexfetch-plugin-<name>convention. -
Implement the plugin with a
src/main.rsorsrc/lib.rsthat handles the JSON protocol. -
Add a
README.mddocumenting the plugin's features, configuration options, and available styles. -
Add sample configs and assets if applicable (see
plugins/animate-logo/for reference).
- Write all code, comments, and documentation in English.
- Prefer the shared
xfetch-plugin-apicrate for protocol types and JSON IO. - Handle protocol and output errors explicitly — write errors to stderr and exit with non-zero.
- Do not use external display or terminal libraries; the core handles rendering.
- Keep the plugin focused on a single responsibility (e.g., logo animation).
- Minimize dependencies to keep build times fast.
- Fork the repository and create a feature branch.
- Implement your plugin following the guidelines above.
- Ensure
cargo build --releasesucceeds for your plugin. - Add an entry for your plugin in README.md.
- Submit a pull request with a clear description of your plugin.
- Maintainers will review the protocol compliance and code quality.
- Do not modify core xfetch source files unless the plugin protocol itself needs changes.
- Do not use network calls or file I/O in animation plugins (they should be pure transformations).
- Do not embed binaries or large assets in the plugin source.
- Do not introduce platform-specific code without fallbacks.