diff --git a/.github/workflows/rust-tests.yml b/.github/workflows/rust-tests.yml
index 6cb4e49..f2e67e5 100644
--- a/.github/workflows/rust-tests.yml
+++ b/.github/workflows/rust-tests.yml
@@ -22,3 +22,24 @@ jobs:
- name: Run tests
run: cargo test --locked
+
+ windows-tests:
+ name: cargo test (windows)
+ runs-on: windows-latest
+ steps:
+ - name: Checkout repository
+ uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
+
+ - name: Install stable Rust toolchain
+ uses: dtolnay/rust-toolchain@18f0487477426dce244608110aa2660c2893deb7 # stable
+ with:
+ components: clippy
+
+ - name: Run updater tests on Windows
+ run: 'cargo test --locked --no-default-features --features update --bin xfetch update::'
+
+ - name: Run all tests on Windows
+ run: cargo test --locked
+
+ - name: Run clippy on Windows
+ run: cargo clippy --all-targets -- -D warnings
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 47610f3..c78e72f 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,112 @@
# Changelog
+## 2026-11-06 — v1.0.3
+
+### CLI
+
+- Added machine output: `--json`, `--get`, `--list-modules`, `modules` and a
+ stable JSON schema version.
+- Added runtime overrides: `--layout`, `--theme`, `--modules`/`--only`,
+ `--color`/`--no-color`, `--offline`, `--no-cache`, `--quiet`, `--verbose`,
+ `--template` and `--format`.
+- Added `doctor` with platform checks, `config validate|print|get|set`,
+ shell completions, a man page and a unified `addon` command.
+- Added JSON output to addon list/info, theme list and `update --check`.
+- Added stable exit codes for config, network, addon, update and not-found
+ failures.
+- Moved the CLI into the modular `src/cli/` tree, including platform-specific
+ doctor checks.
+- Added CLI integration tests covering output modes, overrides, diagnostics,
+ exit codes, completions and offline behavior.
+- Added `docs/CLI.md` with the complete command, output, JSON and exit-code
+ reference.
+- Added config backup and restore: `--backup`, `--restore` and
+ `config backup|restore`. Backups include the active theme and config-roulette
+ routes, and restores write a pre-restore safety backup.
+- Added `scripts/test-cli-sandbox.sh` for end-to-end CLI verification.
+- Added JSON results for addon install, rollback and remove.
+- Added `config unset`, `reset`, `edit`, `backups` and `prune`, array/index
+ edits and comment-preserving nested value replacement.
+- Extended backup/restore with gzip compression, passphrase encryption
+ (Argon2id + XChaCha20-Poly1305), `--with-addons`, logo/ASCII assets, root
+ remapping, restored-config validation, backup listing/pruning and
+ timestamp-based latest selection.
+- Extended templates with conditionals, loops and value filters.
+- Added remote `theme search|info|install|update` and `logo list|preview`.
+- Added `doctor --fix`, `schema`, `self-uninstall` and `update --rollback`.
+- Added help/output snapshots and Windows CI test/clippy coverage.
+- Added `paths`, `env` and `build-info` diagnostics commands.
+- Added `doctor --bench` and `doctor --image`.
+- Added `--log-level` and `--log-json` structured logging.
+- Added complete help descriptions for every command group and subcommand,
+ enforced by a test.
+
+### Command addons
+
+- Added the `command` addon family and `xfetch run` for plain-text CLI
+ commands, with `--render` using the core layout and a separate command
+ catalog repository.
+- Added command catalog search, install, update, rollback, list, info and
+ remove, plus JSON output for those actions.
+
+### SDK and dependencies
+
+- Adopted the xfetch SDK 0.3.1: `xfetch-api-common` 0.3.1 plus
+ `xfetch-plugin-api`, `xfetch-extension-api` and `xfetch-effect-api` 0.3.1.
+ The JSON wire protocol remains `1` and the WIT remains `0.1.0`.
+- The matching `xfetch-guest-api` and `xfetch-command-api` 0.3.1 crates are
+ published for addon authors; the core does not depend on them directly.
+
+### Packaging
+
+- Added crates.io metadata: `repository`, `homepage`, `documentation`,
+ `readme`, `keywords` and `categories`.
+- The crate description now starts with `Fast` instead of `A fast`.
+- Documented `winget install xfetch` as a Windows installation method, with
+ matching update and uninstall guidance.
+
+### Layouts
+
+- Added `layout: "right"`: the info column stays on the left and the ASCII
+ or image logo is rendered on the right. Image logos use the same width and
+ gap settings as the classic layout.
+- Fixed the `right` layout: ASCII and image logos sit flush right, the
+ configured gap always separates text from the logo, and the text column is
+ skipped when an oversized image leaves no room.
+
+### Addon management
+
+- Installs now write an `.install.json` receipt beside each native or wasm
+ addon. The receipt records the installed version, source, repository, path,
+ catalog reference, SDK version and artifact SHA-256.
+- Added `update` and `rollback` for plugins, effects and extensions:
+ `--check` reports available versions, `--all` updates every installed addon,
+ and `--force` rebuilds even when the catalog version is not newer. Native
+ addons are rebuilt from their recorded source; wasm addons keep their
+ artifact/build flow. The previous artifact and receipt are kept as `.bak`
+ for rollback.
+- Added `install --all` for plugins, effects and extensions to install every
+ catalog entry, skipping already installed addons unless `--force` is used.
+- Catalogs accept optional `repo`, `ref`, `sdk_version` and
+ `min_xfetch_version` fields for source resolution and compatibility checks.
+
+### Windows updater
+
+- `xfetch update` now works on Windows. Prebuilt installs download the
+ `.zip` release asset, verify `SHA256SUMS`, keep `xfetch.exe.bak` and replace
+ the running executable with `self-replace`. winget installs are delegated to
+ `winget upgrade --id Xfetch-cli.Xfetch`; source builds are rebuilt from their
+ checkout instead of being replaced with the crates.io release.
+- The PowerShell installers write `%LOCALAPPDATA%\xfetch\install.json`, so a
+ prebuilt install, a source build and a manually copied binary are no longer
+ confused even when they share a directory.
+
+### Release
+
+- Bumped the crate version to 1.0.3 and the installer examples to 1.0.3.
+ The published v1.0.2 tag and its assets remain unchanged.
+
+
## 2026-11-03 — v1.0.2
### Security
diff --git a/Cargo.lock b/Cargo.lock
index 24c81f2..6890219 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -17,6 +17,16 @@ version = "2.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa"
+[[package]]
+name = "aead"
+version = "0.6.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1973cfbc1a2daf9cf550e74e1f088c28e7f7d8c1e1418fb6c9dc5184b7e84c99"
+dependencies = [
+ "crypto-common 0.2.2",
+ "inout",
+]
+
[[package]]
name = "allocator-api2"
version = "0.2.21"
@@ -94,6 +104,18 @@ version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3bc62ac97cc33321f50863d514c3bc38a453947a8f9e781137e47c7401020aed"
+[[package]]
+name = "argon2"
+version = "0.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "134c52ddac6d63c576bef8168db10c83c49c26444ecbc68060fef078925a901c"
+dependencies = [
+ "base64ct",
+ "blake2",
+ "cpufeatures 0.3.1",
+ "password-hash",
+]
+
[[package]]
name = "async-trait"
version = "0.1.92"
@@ -123,12 +145,27 @@ version = "0.23.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac07cdecf99051d9a5238b80f35af32cdeba5b336e55d957b318b50137e18da5"
+[[package]]
+name = "base64ct"
+version = "1.8.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2af50177e190e07a26ab74f8b1efbfe2ef87da2116221318cb1c2e82baf7de06"
+
[[package]]
name = "bitflags"
version = "2.13.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3ded4057c258ba199e2d26386d3af3780957ecaee6c4ef4041c6b4b8b97c0b06"
+[[package]]
+name = "blake2"
+version = "0.11.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5b5d4d889834ee8ecfc0f8426ad30faf7cdcb10f741a8e6d7224d95325479f6f"
+dependencies = [
+ "digest 0.11.3",
+]
+
[[package]]
name = "block-buffer"
version = "0.10.4"
@@ -192,6 +229,12 @@ version = "1.0.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4e7648175b45a9a48536d676f68d918270699102aa8dab5496df06904c914600"
+[[package]]
+name = "cfg_aliases"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f079e83a288787bcd14a6aea84cee5c87a67c5a3e660c30f557a3d24761b3527"
+
[[package]]
name = "chacha20"
version = "0.10.2"
@@ -199,10 +242,34 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "65c35e4b699c7e15ccbe7ee35c005e4fc0a278d22238a2857e6ce2dadeda1b06"
dependencies = [
"cfg-if",
+ "cipher",
"cpufeatures 0.3.1",
"rand_core",
]
+[[package]]
+name = "chacha20poly1305"
+version = "0.11.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9b89e1c441e926b9c82a8d023f6e1b7ae0adcfaa7d621814e4d60789bac751cb"
+dependencies = [
+ "aead",
+ "chacha20",
+ "cipher",
+ "poly1305",
+]
+
+[[package]]
+name = "cipher"
+version = "0.5.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e8cf2a2c93cd704877c0858356ed03480ff301ee950b43f1cbe4573b088bfa6c"
+dependencies = [
+ "block-buffer 0.12.1",
+ "crypto-common 0.2.2",
+ "inout",
+]
+
[[package]]
name = "clap"
version = "4.6.7"
@@ -225,6 +292,15 @@ dependencies = [
"strsim",
]
+[[package]]
+name = "clap_complete"
+version = "4.6.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "037e2a1a92236d0aff7e845093f64661d6df4c02c9fcc61a60e9e1d736fa392f"
+dependencies = [
+ "clap",
+]
+
[[package]]
name = "clap_derive"
version = "4.6.7"
@@ -243,6 +319,22 @@ version = "1.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1c133bc6a41be0d194c306b5506d15e6feeea7b1d6604bd3f8310dfb2ca96486"
+[[package]]
+name = "clap_mangen"
+version = "0.2.33"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7e30ffc187e2e3aeafcd1c6e2aa416e29739454c0ccaa419226d5ecd181f2d78"
+dependencies = [
+ "clap",
+ "roff",
+]
+
+[[package]]
+name = "cmov"
+version = "0.5.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0c9ea0ac24bc397ab3c98583a3c9ba74fa56b09a4449bbe172b9b1ddb016027a"
+
[[package]]
name = "cobs"
version = "0.3.0"
@@ -554,7 +646,18 @@ version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ce6e4c961d6cd6c9a86db418387425e8bdeaf05b3c8bc1411e6dca4c252f1453"
dependencies = [
+ "getrandom 0.4.3",
"hybrid-array",
+ "rand_core",
+]
+
+[[package]]
+name = "ctutils"
+version = "0.4.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "261af4dc7a06ea8a527a8717d8cbba3417530d6ea14e1edb3de6b67806be474f"
+dependencies = [
+ "cmov",
]
[[package]]
@@ -598,6 +701,7 @@ dependencies = [
"block-buffer 0.12.1",
"const-oid",
"crypto-common 0.2.2",
+ "ctutils",
]
[[package]]
@@ -887,9 +991,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "300e883d756b2e4ec94e02791f39b04b522276138852cfc41d9fb7e904106099"
dependencies = [
"cfg-if",
+ "js-sys",
"libc",
"r-efi",
"rand_core",
+ "wasm-bindgen",
]
[[package]]
@@ -1124,6 +1230,15 @@ dependencies = [
"serde_core",
]
+[[package]]
+name = "inout"
+version = "0.2.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3b5cf4372d022092674a83b8febf5865f7a573cc0c5baec5e49c879b6a20978f"
+dependencies = [
+ "hybrid-array",
+]
+
[[package]]
name = "is_terminal_polyfill"
version = "1.70.2"
@@ -1155,6 +1270,16 @@ dependencies = [
"libc",
]
+[[package]]
+name = "js-sys"
+version = "0.3.107"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8dc09d2154098bbf4ef54eab6b117a3c24cbbffe94046b16ee6d6d4510584d3a"
+dependencies = [
+ "cfg-if",
+ "wasm-bindgen",
+]
+
[[package]]
name = "json_comments"
version = "0.2.2"
@@ -1296,6 +1421,18 @@ version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "743fb55ba31b18fb1ecef6bdc9aa2743314978ac084044301a7eee33fb99a20d"
+[[package]]
+name = "nix"
+version = "0.31.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cf20d2fde8ff38632c426f1165ed7436270b44f199fc55284c38276f9db47c3d"
+dependencies = [
+ "bitflags",
+ "cfg-if",
+ "cfg_aliases",
+ "libc",
+]
+
[[package]]
name = "ntapi"
version = "0.4.3"
@@ -1424,12 +1561,33 @@ dependencies = [
"windows-link",
]
+[[package]]
+name = "password-hash"
+version = "0.6.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "aab41826031698d6ffcd9cff78ef56ef998e39dc7e5067cdfebe373842d4723b"
+dependencies = [
+ "getrandom 0.4.3",
+ "phc",
+]
+
[[package]]
name = "percent-encoding"
version = "2.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220"
+[[package]]
+name = "phc"
+version = "0.6.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "44dc769b75f93afdddd8c7fa12d685292ddeff1e66f7f0f3a234cf1818afe892"
+dependencies = [
+ "base64ct",
+ "ctutils",
+ "getrandom 0.4.3",
+]
+
[[package]]
name = "pin-project-lite"
version = "0.2.17"
@@ -1455,6 +1613,16 @@ dependencies = [
"miniz_oxide 0.8.9",
]
+[[package]]
+name = "poly1305"
+version = "0.9.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6e2d0073b297041425c7c3df6eb4792d598a15323fe63346852b092eca02904c"
+dependencies = [
+ "cpufeatures 0.3.1",
+ "universal-hash",
+]
+
[[package]]
name = "postcard"
version = "1.1.3"
@@ -1640,6 +1808,12 @@ dependencies = [
"windows-sys 0.52.0",
]
+[[package]]
+name = "roff"
+version = "1.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "323c417e1d9665a65b263ec744ba09030cfb277e9daa0b018a4ab62e57bc8189"
+
[[package]]
name = "rustc-demangle"
version = "0.1.28"
@@ -1731,6 +1905,17 @@ version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
+[[package]]
+name = "self-replace"
+version = "1.5.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "609c5606d9d357b26e4ee1a7a01beac396628d0fa01ebbade2a09cefb88e65af"
+dependencies = [
+ "fastrand",
+ "tempfile",
+ "windows-sys 0.61.2",
+]
+
[[package]]
name = "semver"
version = "1.0.28"
@@ -2142,6 +2327,12 @@ dependencies = [
"once_cell",
]
+[[package]]
+name = "typed-path"
+version = "0.13.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9ce898823d8cd0f028662d2f349694b1af0a5cc9be83f1d213a00d3147445e1e"
+
[[package]]
name = "typenum"
version = "1.20.1"
@@ -2166,6 +2357,16 @@ version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ca718471876992059f251e33aafc334346ada08042b7661dd1fe313a9bced557"
+[[package]]
+name = "universal-hash"
+version = "0.6.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f4987bdc12753382e0bec4a65c50738ffaabc998b9cdd1f952fb5f39b0048a96"
+dependencies = [
+ "crypto-common 0.2.2",
+ "ctutils",
+]
+
[[package]]
name = "untrusted"
version = "0.9.0"
@@ -2267,6 +2468,50 @@ version = "0.11.1+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b"
+[[package]]
+name = "wasm-bindgen"
+version = "0.2.130"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a0181c14ddd6c7b8b0b0132a5f1ea2f32be0d893cd3ea97b6e3b28886b8a3d1c"
+dependencies = [
+ "cfg-if",
+ "rustversion",
+ "wasm-bindgen-macro",
+ "wasm-bindgen-shared",
+]
+
+[[package]]
+name = "wasm-bindgen-macro"
+version = "0.2.130"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6e931a02a39abdbf66966fc35902b5ed58344750a70885bed4ebb3d8eaf28ed9"
+dependencies = [
+ "quote",
+ "wasm-bindgen-macro-support",
+]
+
+[[package]]
+name = "wasm-bindgen-macro-support"
+version = "0.2.130"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2ca4b54280e455bd831e33a6b8e734baf432fe63fbc35c42ac14ce67a043a53a"
+dependencies = [
+ "bumpalo",
+ "proc-macro2",
+ "quote",
+ "syn 3.0.9",
+ "wasm-bindgen-shared",
+]
+
+[[package]]
+name = "wasm-bindgen-shared"
+version = "0.2.130"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3b1f9c3798725016c6ee5f2f57869ec0831e8bbc0ab0310a86e787e97f41389e"
+dependencies = [
+ "unicode-ident",
+]
+
[[package]]
name = "wasm-encoder"
version = "0.261.0"
@@ -2975,20 +3220,36 @@ dependencies = [
"rustix",
]
+[[package]]
+name = "xfetch-api-common"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d67a2db2341dd5b8d6ae933c35dcd75670b4379e206ac59ffc1f42c94021315d"
+dependencies = [
+ "serde",
+ "serde_json",
+]
+
[[package]]
name = "xfetch-cli"
-version = "1.0.2"
+version = "1.0.3"
dependencies = [
"anyhow",
+ "argon2",
"base64 0.23.1",
+ "chacha20poly1305",
"clap",
+ "clap_complete",
+ "clap_mangen",
"console",
"crossterm",
"dirs",
"flate2",
+ "getrandom 0.2.17",
"image",
"json_comments",
"libc",
+ "self-replace",
"semver",
"serde",
"serde_json",
@@ -3005,36 +3266,40 @@ dependencies = [
"xfetch-effect-api",
"xfetch-extension-api",
"xfetch-plugin-api",
+ "zip",
]
[[package]]
name = "xfetch-effect-api"
-version = "0.2.0"
+version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "69820c4df7636fc23ab84a3b6cb7d3c9b58b523fbc10e0e0d733c8bc5facd009"
+checksum = "858f9c4671b66205a1985683eacf119af7a2b87a60953d8965c1d839d7e49310"
dependencies = [
"serde",
"serde_json",
+ "xfetch-api-common",
]
[[package]]
name = "xfetch-extension-api"
-version = "0.2.0"
+version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8f0d4e7f50b3cc65e4ad5a6556354975e24429c430a8df06ff665709ed383cab"
+checksum = "58797f1ea7e1c922c7a1b66aa76210a7d96fea84a11d9ff40afcfcb4fddc2aed"
dependencies = [
"serde",
"serde_json",
+ "xfetch-api-common",
]
[[package]]
name = "xfetch-plugin-api"
-version = "0.2.0"
+version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "54bc785c4c4bb18d4041edff98baeff88a9ff5595b52212a3a01c7d56e74fb8d"
+checksum = "79be6250101bee33db48a366b52d00284aa63f78017931f9237bf239a632dd14"
dependencies = [
"serde",
"serde_json",
+ "xfetch-api-common",
]
[[package]]
@@ -3140,6 +3405,24 @@ dependencies = [
"syn 3.0.9",
]
+[[package]]
+name = "zip"
+version = "9.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "eaeb7f16d2605b49aef586996ded29e65642fab7259a3077e537351df6d80426"
+dependencies = [
+ "crc32fast",
+ "flate2",
+ "getrandom 0.4.3",
+ "indexmap",
+ "libc",
+ "memchr",
+ "nix",
+ "typed-path",
+ "windows-sys 0.61.2",
+ "zopfli",
+]
+
[[package]]
name = "zlib-rs"
version = "0.6.8"
@@ -3152,6 +3435,18 @@ version = "1.0.23"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "29666d0abbfad1e3dc4dcf6144730dd3a3ab225bbbdac83319345b1b44ccfc1b"
+[[package]]
+name = "zopfli"
+version = "0.8.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "aaf7fc5d30c28483d93805c4a5e12b05bbb52407fa67c5f8bd552374cd01fb11"
+dependencies = [
+ "bumpalo",
+ "crc32fast",
+ "log",
+ "simd-adler32",
+]
+
[[package]]
name = "zstd"
version = "0.13.4"
diff --git a/Cargo.toml b/Cargo.toml
index 02615f8..5a525bd 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -1,9 +1,15 @@
[package]
name = "xfetch-cli"
-version = "1.0.2"
+version = "1.0.3"
edition = "2024"
license = "MIT"
-description = "XFetch — a fast, customizable system information fetch tool for the terminal"
+description = "Fast, customizable system information fetch tool for the terminal"
+repository = "https://github.com/xfetch-cli/xfetch"
+homepage = "https://xfetch-cli.github.io/web/"
+documentation = "https://docs.rs/xfetch-cli"
+readme = "README.md"
+keywords = ["system-information", "fetch", "cli", "terminal", "neofetch"]
+categories = ["command-line-utilities"]
[[bin]]
name = "xfetch"
@@ -13,14 +19,14 @@ path = "src/main.rs"
# WASM plugin/effect/extension support and the self-updater are enabled by
# default; build with `--no-default-features` for a minimal binary.
default = ["wasm", "update"]
-wasm = ["dep:wasmtime", "dep:wasmtime-wasi", "dep:anyhow", "dep:base64", "dep:url"]
-update = ["dep:flate2", "dep:semver", "dep:tar"]
+wasm = ["dep:wasmtime", "dep:wasmtime-wasi", "dep:anyhow", "dep:url"]
+update = ["dep:tar", "dep:zip", "dep:self-replace"]
[dependencies]
anyhow = { version = "1", optional = true }
-base64 = { version = "0.23", optional = true }
-flate2 = { version = "1", optional = true }
-semver = { version = "1", optional = true }
+base64 = "0.23"
+flate2 = "1"
+semver = "1"
sha2 = "0.11"
tar = { version = "0.4", optional = true }
clap = { version = "4.6.7", features = ["derive"] }
@@ -49,14 +55,21 @@ wasmtime-wasi = { version = "50.0.1", optional = true, default-features = false,
"p1",
"p2",
] }
-xfetch-plugin-api = { version = "0.2.0", package = "xfetch-plugin-api" }
-xfetch-extension-api = { version = "0.2.0", package = "xfetch-extension-api" }
-xfetch-effect-api = { version = "0.2.0", package = "xfetch-effect-api" }
+xfetch-plugin-api = { version = "0.3.1", package = "xfetch-plugin-api" }
+xfetch-extension-api = { version = "0.3.1", package = "xfetch-extension-api" }
+xfetch-effect-api = { version = "0.3.1", package = "xfetch-effect-api" }
+clap_complete = "4"
+clap_mangen = "0.2"
+chacha20poly1305 = "0.11.0"
+argon2 = "0.6.0"
+getrandom = "0.2"
# Windows-only native probes: CPU/date/battery/GPU are read through the Win32
# API instead of PDH counters or PowerShell subprocesses (see
# src/info/platform/windows/). The dependency never enters other targets.
[target.'cfg(windows)'.dependencies]
+self-replace = { version = "1.5", optional = true }
+zip = { version = "9", default-features = false, features = ["deflate"], optional = true }
windows-sys = { version = "0.61", features = [
"Win32_Foundation",
"Win32_Graphics_Gdi",
diff --git a/README.md b/README.md
index 4a19391..1a468c2 100644
--- a/README.md
+++ b/README.md
@@ -114,6 +114,11 @@ irm https://raw.githubusercontent.com/xfetch-cli/xfetch/main/install-prebuilt.ps
irm https://raw.githubusercontent.com/xfetch-cli/xfetch/main/install.ps1 | iex
```
+**Windows (winget):**
+```powershell
+winget install xfetch
+```
+
**Cargo (crates.io):**
```bash
cargo install xfetch-cli
@@ -141,9 +146,23 @@ yay -S xfetch-git # build from source
- **Effects**: Installable intro animations that reveal the info with style (e.g. a "decrypt" effect) — opt-in via `"effects"` in the config.
- **Daemon mode**: Pin an animated fetch at the top of the terminal and keep using the shell below.
- **Live stats daemon**: Turn the pinned fetch into a live panel — re-probes cpu/memory/battery/... every few seconds (`daemon_live`), with hot reload of the config on the fly (`daemon_live_reload`).
-- **Themes, plugins & extensions**: Switch visual themes, extend info with plugins, and transform the config with extensions.
+- **Themes, plugins, extensions & commands**: Switch visual themes, extend info with plugins, transform the config with extensions, and add CLI commands.
- **WebAssembly guests**: Plugins, effects and extensions can be sandboxed `.wasm` artifacts written in Rust, Python, Go, C and more, with manifest-based capabilities. See [docs/WASM.md](docs/WASM.md).
+
What's new in 1.0.3
+
+- **Scriptable CLI**: JSON output, `--get`, runtime overrides, templates, a
+ stable schema, explicit exit codes, shell completions and a man page.
+- **Command addons**: install and run plain-text CLI addons with
+ `xfetch command` and `xfetch run`.
+- **Diagnostics and recovery**: `doctor`, `paths`, `env`, `build-info`,
+ config validation, encrypted backups/restores and `self-uninstall`.
+- **Addon lifecycle**: `.install.json` receipts, update, rollback and `--all`
+ installs for plugins, effects, extensions and commands.
+- **Right-side logo layout**: `layout: "right"` keeps the info on the left and
+ renders the logo on the right.
+- **Windows**: native self-update, winget detection and `winget install xfetch`.
+
Configuration
xfetch looks for a configuration file at:
@@ -188,6 +207,31 @@ Simply run `xfetch` in your terminal.
```bash
xfetch # render the fetch
+xfetch --json # machine-readable fetch
+xfetch --get os,cpu,memory # print specific values
+xfetch --modules os,kernel,cpu # override the rendered modules
+xfetch --layout right # runtime layout override
+xfetch --theme dracula # runtime theme override
+xfetch --format 'os={{os}}' # inline output template
+xfetch modules # list module keys
+xfetch doctor # diagnose config, terminal and addons
+xfetch doctor --fix # apply safe fixes
+xfetch doctor --bench --image # probe timings and logo preview
+xfetch paths # resolved config/addon/cache paths
+xfetch env # platform, environment and settings
+xfetch build-info # build profile and enabled features
+xfetch config validate # validate the config file
+xfetch config edit # edit and validate the config
+xfetch --backup # back up the current config
+xfetch --restore # restore the latest backup
+xfetch command install github # install a command addon
+xfetch run github -- owner/repo # run it
+xfetch theme search dark # search the remote theme registry
+xfetch logo preview arch # preview a catalog logo
+xfetch schema fetch # JSON Schema for machine output
+xfetch --log-json --log-level debug # structured logs on stderr
+xfetch completions zsh # shell completions
+xfetch man # man page
xfetch --daemon # pin an animated fetch at the top (daemon mode)
xfetch --daemon-stop # stop the daemon
xfetch --no-daemon-live # disable the live stats daemon (config: "daemon_live": true)
@@ -197,6 +241,8 @@ xfetch --config path/to/config.jsonc
xfetch --gen-config # generate a starter config
xfetch --clean-cache # clear the module cache
xfetch plugin install # install a plugin
+xfetch plugin install --all # install every catalog plugin
+xfetch plugin update --all # update every installed plugin
xfetch plugin search # search the plugin catalog
xfetch plugin available # list the plugin catalog
xfetch extension install # install an extension
@@ -216,6 +262,7 @@ xfetch update # update to the latest release
diff --git a/ROADMAP.md b/ROADMAP.md
index 3c39c0f..f4d1095 100644
--- a/ROADMAP.md
+++ b/ROADMAP.md
@@ -100,6 +100,8 @@
- [x] Create binary releases for Linux x86_64 (#103)
- [x] Create binary releases for macOS (Intel & ARM) (#104)
- [x] Create binary releases for Windows (#105)
+- [x] Publish Windows packages to winget (#441119)
+- [x] Add Windows self-update for winget, cargo and prebuilt installs (#201)
- [x] Setup AUR package for Arch Linux (#106)
- [x] Setup Homebrew tap for macOS (#107)
- [x] Setup PyPI or cargo registry for distribution (#108)
@@ -125,6 +127,7 @@
- [x] Setup code formatter (rustfmt) (#122)
- [x] Implement platform-specific tests for each OS (#123)
- [x] Add cross-platform testing suite (#124)
+- [x] Run updater tests on Windows CI (#201)
- [x] Setup code coverage reporting (#125)
## Phase 12 · Advanced Features
diff --git a/docs/CLI.md b/docs/CLI.md
new file mode 100644
index 0000000..c160785
--- /dev/null
+++ b/docs/CLI.md
@@ -0,0 +1,1047 @@
+
Command Line Interface
+
+
+ This page is the complete reference for the xfetch command line:
+ global options, output modes, runtime overrides, every subcommand, JSON schemas,
+ exit codes and platform notes. For installation, see
+ INSTALLATION.md; for configuration keys, see
+ CONFIGURATION.md.
+
+
+
Synopsis
+
+
xfetch [OPTIONS] [COMMAND]
+
+
+ With no command, xfetch renders the fetch. Global options may appear before or
+ after the subcommand. Unsupported flag combinations fail with exit code 2.
+
+ --get probes only the requested keys. A single key prints the raw
+ value; multiple keys print key=value lines, with embedded newlines
+ escaped as \n. With --json the output is an object:
+
+ Supported keys are the module keys from --list-modules, plus
+ version and name. Patterns such as
+ packages:pacman and plugin:<name> are accepted.
+ Unknown keys fail with exit code 7.
+
+ Templates replace {{key}} with the resolved value. Unknown keys
+ render as an empty string. Supported placeholders include every module key,
+ packages:<manager>, plugin:<name>,
+ palette, header, sep,
+ name and version. List values such as
+ gpu, disks, packages_breakdown and
+ plugin:<name> support loops and join.
+
+ --get cannot be combined with --template or
+ --format. --json cannot be combined with
+ --template or --format. --get,
+ --template and --format only apply to the default
+ fetch and cannot be combined with a subcommand or daemon mode. The global
+ --json flag is accepted by subcommands that support machine
+ output.
+
+ --theme is applied before the theme merge so the selected theme is
+ the one that participates. --color auto strips ANSI when stdout is
+ not a TTY; always keeps it, never and
+ --no-color remove it.
+
Install from a catalog name, local directory, repository, direct wasm file or URL.
+
update
update [NAME] [--all] [--check] [--force]
Rebuild native addons from the recorded source; keep .bak for rollback.
+
rollback
rollback <NAME>
Restore the previous artifact and receipt.
+
list
list [--verbose]
List installed addons. --verbose adds capabilities and limits.
+
info
info <NAME>
Show metadata, capabilities, limits and the config snippet.
+
search
search [QUERY] (alias available)
Search the catalog; omit the query to list everything.
+
remove
remove <NAME>
Remove the installed artifact and receipt.
+
+
+
+
+ --json is supported for every addon action: install
+ returns the installed name and path (or a list with --all),
+ rollback and remove return a status object,
+ list and info return metadata, search
+ returns the catalog and update --check returns version checks.
+
+ info --json prints the same fields flattened with
+ schema_version. install --json prints
+ {schema_version, status, kind, name, path, source} (or an
+ installed array with --all). rollback --json
+ and remove --json print {schema_version, status, kind, name}.
+ update --check --json prints:
+
+ Command addons expose extra CLI commands. They are installed and updated like
+ the other addon families, but they run on demand:
+
+
+
xfetch command install github
+xfetch command list
+xfetch command info github
+xfetch command update --all --check
+xfetch command update github
+xfetch command rollback github
+xfetch command remove github
+
+xfetch run github -- rust-lang/rust
+xfetch run github -- https://github.com/rust-lang/rust
+xfetch --json run github -- rust-lang/rust
+xfetch --layout right run github --render -- rust-lang/rust
+
+
+ The command receives positional arguments and returns plain text, one field
+ per line. The core prints the text as-is by default; --render
+ passes the lines through the configured layout and logo. --json
+ wraps the output as
+ {schema_version, command, args, lines}.
+
+
+
+ Official command addons live in
+ https://github.com/xfetch-cli/commands. The catalog is at
+ .../main/index.json (XFETCH_COMMAND_INDEX overrides
+ it); XFETCH_COMMAND_REPO overrides the install repository and
+ XFETCH_COMMAND_DEV_DIR adds a development directory.
+ --offline blocks catalog and remote installs.
+
+
+
+ Wasm command addons use the command world in
+ xfetch:runtime@0.1.0; the request is JSON and the response is
+ plain text. Native commands may read argv and/or the JSON request on stdin.
+ See the WASM guide and the API
+ command-sdk.md.
+
+
+
Theme and Logo Commands
+
+
Theme Registry
+
+
xfetch theme search dark
+xfetch theme info nord
+xfetch theme install nord
+xfetch theme install nord --force
+xfetch theme update nord
+xfetch theme update --all --check
+xfetch theme list
+xfetch theme list --json
+xfetch theme set dracula
+xfetch theme remove dracula
+xfetch theme export my-theme
+xfetch --theme dracula
+
+
+ theme search, theme info, theme install
+ and theme update use the registry at
+ https://raw.githubusercontent.com/xfetch-cli/themes/main/index.json.
+ Override it with XFETCH_THEME_INDEX. Installed themes write a
+ .<name>.xfetch-meta.json sidecar with the installed version
+ and source; theme list includes that metadata.
+ theme update --check exits 1 when an update is available.
+ --offline blocks registry access.
+
+
+
+ theme list --json prints a themes array with
+ {name, path, version, source, installed_unix}.
+ theme search --json and theme info --json return
+ registry metadata. theme install --json returns
+ {schema_version, status, name, version, path}.
+ theme update --json returns a per-theme
+ {name, installed, available, update_available, status} list.
+
+
+
Logo Catalog
+
+
xfetch logo list
+xfetch logo list linux
+xfetch logo list --json
+xfetch logo preview arch
+xfetch logo preview arch --json
+
+
+ Logos come from
+ https://raw.githubusercontent.com/xfetch-cli/logos/main
+ (XFETCH_LOGOS_URL overrides the base URL). logo list
+ filters by id, alias or file; logo preview prints the validated
+ ASCII art. --json returns
+ {schema_version, logos:[{id,file,aliases}]} for lists and
+ {schema_version, id, file, aliases, art} for a preview.
+ --offline blocks catalog access.
+
Install the prebuilt release in place (Unix and Windows).
+
--bin-dir <DIR>
Directory holding the binary to replace (with --prebuilt).
+
-y, --yes
Assume yes for prompts.
+
--rollback
Restore the previous binary from xfetch.bak/xfetch.exe.bak.
+
+
+
+
+ --json requires --check for update checks; it also
+ works with --rollback. Package-managed installs (Homebrew, cargo,
+ winget, AUR) are delegated to their package manager instead of replacing the
+ binary.
+
xfetch doctor
+xfetch doctor --json
+xfetch doctor --network
+xfetch doctor --json --network
+xfetch doctor --fix
+xfetch doctor --fix --json
+xfetch doctor --bench
+xfetch doctor --bench --json
+xfetch doctor --image
+
+
+ doctor checks the binary, config file, theme, layout, terminal
+ size, image protocol, logo width/gap geometry, addon directories, install
+ receipts and cache. --network additionally checks the catalogs and
+ the update API. It exits 1 when any check fails. --quiet hides
+ successful checks. --fix applies safe fixes: it generates a
+ missing config and removes an invalid cache file. --bench runs
+ the info collection benchmark and includes it in the report.
+ --image prints a preview of the configured logo/image when
+ stdout is a terminal. Applied fixes and benchmark lines are included in JSON
+ output.
+
+ The registry includes plain keys (os, cpu,
+ memory, ...), patterns (packages:<manager>,
+ plugin:<name>) and presentation keys
+ (palette, header, sep).
+
+
+
[
+ {
+ "key": "os",
+ "description": "Operating system name and architecture",
+ "platforms": "all"
+ }
+]
+
+
Config Commands
+
+
xfetch config validate
+xfetch config print
+xfetch config print --raw
+xfetch config print --json
+xfetch config get layout
+xfetch config get colors.cpu
+xfetch config set layout right
+xfetch config set modules.1 kernel
+xfetch config set colors.cpu red
+xfetch config unset colors.cpu
+xfetch config reset --yes
+xfetch config edit
+xfetch config backups
+xfetch config prune --keep 5
+xfetch --config ./config.jsonc config set theme dracula
+
+
+
+
Action
Description
+
+
+
validate
Parse JSONC strictly, validate the schema and check that the referenced theme exists.
+
print
Print the resolved config after defaults, theme, providers and CLI overrides. --raw prints the stored file instead.
+
get <KEY>
Read one dotted key path. Array indices such as modules.0 are supported.
+
set <KEY> <VALUE>
Set one value. Dotted paths and array indices are supported. Existing nested values are replaced in place, preserving surrounding comments; missing values fall back to a semantic rewrite. The result is validated before writing.
+
unset <KEY>
Remove an object key or array element. Top-level keys preserve the rest of the file; nested removals may rewrite it.
+
reset
Write the built-in defaults after a safety backup. Requires --yes in non-interactive runs.
+
edit
Open the config in $VISUAL or $EDITOR after a safety backup, then validate it.
+
backups
List saved backups with timestamps, file counts and sizes.
+
prune
Delete old regular backups, keeping the newest N. Safety backups are preserved. --dry-run only lists them.
+
+
+
+
+ config set --json, config unset --json,
+ config reset --json and config edit --json print
+ status objects. config get --json prints the raw JSON value.
+ config backups --json and config prune --json return
+ structured backup metadata.
+
+
+
Backup and Restore
+
+
+ xfetch --backup snapshots the config currently in use. The
+ backup adapts to the installed setup:
+
+
+
+
The main config file.
+
Active themes for the main config and for every route config, recursively.
+
For config-roulette (and any provider with a
+ routes argument): the routes file and every referenced route
+ config.
+
Nested provider routes, up to a safe depth.
+
Other provider arguments that look like local file paths and exist.
+
Referenced logo/ASCII assets.
+
With --with-addons: installed addon artifacts, sidecar
+ manifests and install receipts.
+
A resolved-config snapshot, missing files and parse warnings.
+ By default backups live in the xfetch config directory under
+ backups/ (usually ~/.config/xfetch/backups on
+ Linux). --output accepts a file or a directory. Without
+ --from, config restore uses the newest backup by
+ the bundle's own timestamp. config backups lists saved backups;
+ config prune --keep N deletes older regular backups and keeps
+ safety backups untouched.
+
+
+
+ Restore always writes a safety backup named pre-restore-*.json
+ before touching the files. Files are written atomically and parent
+ directories are created. --dry-run prints the target paths
+ without writing. Missing entries recorded at backup time are skipped. The
+ restored main config is validated and the result is reported in the
+ validation field.
+
+
+
+ With --config <PATH>, the main config file is restored to
+ that path while auxiliary files return to their original locations.
+ --root <DIR> remaps every file under the original config
+ directory into a new root, which is useful for restoring a complete setup
+ elsewhere.
+
+
+
+ --compress writes gzip-compressed backups. --encrypt
+ writes a passphrase-encrypted backup using Argon2id and
+ XChaCha20-Poly1305; the passphrase comes from
+ --passphrase-file or XFETCH_BACKUP_PASSPHRASE.
+ Restore detects plain JSON, gzip and encrypted containers automatically.
+
+
+
+ Runtime overrides such as --theme, --layout and
+ --modules are not persisted in the backup; the stored config and
+ its referenced files are captured. For config-roulette, the
+ resolved_config field is a snapshot of one invocation, while the
+ raw routes are preserved for a faithful restore.
+
+
+
+ Backups are JSON text (optionally compressed/encrypted) and may contain
+ sensitive configuration values. On Unix the backup file is written with mode
+ 0600. Store encrypted backups and passphrase files securely.
+
+ schema prints JSON Schema documents for the machine-readable
+ outputs. xfetch schema prints all schemas in one object;
+ xfetch schema <kind> prints one schema. The schemas are
+ descriptive contracts for the schema_version: 1 outputs.
+
+ self-uninstall removes the running binary and its update backup.
+ --purge also removes the xfetch config directory (including
+ themes, addons and backups), cache and data directories. Without
+ --yes, it asks for confirmation when stdin is a terminal and
+ refuses in non-interactive runs.
+
xfetch man
+xfetch man --output ~/.local/share/man/man1/xfetch.1
+
+
Exit Codes
+
+
+
+
Code
Meaning
+
+
+
0
Success.
+
1
Generic failure, doctor failures, or update --check finding a newer release.
+
2
Usage error: invalid flag, invalid combination or clap parse error.
+
3
Configuration error.
+
4
Network error or a request blocked by --offline.
+
5
Addon error.
+
6
Update error.
+
7
Not found: unknown module, config key or addon.
+
+
+
+
Environment Variables
+
+
+
+
Variable
Effect
+
+
+
XFETCH_PLUGIN_REPO
Default plugin install repository.
+
XFETCH_EFFECT_REPO
Default effect install repository.
+
XFETCH_EXTENSION_REPO
Default extension install repository.
+
XFETCH_PLUGIN_INDEX
Plugin catalog URL or local path.
+
XFETCH_EFFECT_INDEX
Effect catalog URL or local path.
+
XFETCH_EXTENSION_INDEX
Extension catalog URL or local path.
+
XFETCH_LOGOS_URL
Base URL for the logo catalog.
+
XFETCH_THEME_INDEX
Theme registry index URL or local path.
+
XFETCH_COMMAND_INDEX
Command addon catalog URL or local path.
+
XFETCH_COMMAND_REPO
Default command addon install repository.
+
XFETCH_COMMAND_DEV_DIR
Extra development directory for command lookup.
+
XFETCH_BACKUP_PASSPHRASE
Passphrase for encrypted config backups.
+
VISUAL, EDITOR
Editor used by xfetch config edit.
+
XFETCH_PLUGIN_DEV_DIR
Extra development directory for plugin lookup.
+
XPM_AUR_STATE
Override the xpm-aur state file used for package counts.
+
XDG_CONFIG_HOME
Config root (default ~/.config).
+
XDG_CACHE_HOME
Cache root (default ~/.cache).
+
XDG_STATE_HOME
State root (default ~/.local/state).
+
KITTY_WINDOW_ID
Enables the Kitty graphics protocol when set.
+
TERM_PROGRAM, LC_TERMINAL
Select iTerm-compatible inline images when supported.
+
+
+
+
Platform Notes
+
+
+
+
Platform
Notes
+
+
+
Linux
Config under ~/.config/xfetch. Package counts read local databases. Daemon mode uses a terminal scroll region.
+
macOS
Config under ~/Library/Application Support/xfetch with a symlink from the XDG path. Homebrew installs are updated through Homebrew.
+
Windows
Config under %APPDATA%\xfetch. Install with winget install xfetch or the PowerShell installers. The updater uses self-replace; winget-managed updates are delegated to winget upgrade. Daemon mode uses a worker process instead of fork.
+
+
+
+
+ Images use the Kitty graphics protocol when available, iTerm inline images in
+ compatible terminals, and half blocks otherwise. Text colors are controlled by
+ --color; images are not affected by --no-color.
+
Native addons run unsandboxed with the current user's privileges.
+
Wasm addons use the deny-by-default manifest policy with time, memory, output, host-call and capability limits.
+
--sha256 on install is the provenance pin; it overrides the manifest digest.
+
Catalog fetches are bounded and schema-checked.
+
Config backups contain the raw config, referenced theme/route files and, with --with-addons, addon artifacts. Store them accordingly. On Unix they are written with mode 0600.
+
Encrypted backups use Argon2id and XChaCha20-Poly1305. Pass the passphrase through --passphrase-file or XFETCH_BACKUP_PASSPHRASE; do not put it directly on the command line.
+
--offline blocks xfetch network calls, but native addons are trusted code and can still make their own network requests. Wasm addons remain governed by the manifest policy.
+
Never place secrets in config files, addon manifests or command lines; prefer environment variables supplied by your shell or secret manager.
+ paths prints the resolved config file, config directory, themes,
+ plugins, effects, extensions, backups, cache and data paths.
+ env adds build information, platform details, environment
+ variables (terminal, session, XDG), runtime settings and the same path map.
+ build-info prints the version, target OS/architecture, build
+ profile and enabled features (wasm, update, encryption). All three support
+ --json.
+
+
+
Sandbox Test
+
+
+ Build the release binary and run the end-to-end sandbox:
+
+ The script uses an isolated HOME and XDG directories, exercises
+ the output modes, overrides, templates, config editing, doctor (including
+ --fix), themes and logos, addons, wasm tooling, completions, man
+ page, schema, backup/restore (compression, encryption, assets, addons and
+ root remapping), update rollback, self-uninstall and a config-roulette setup
+ with a fake extension. It prints the sandbox path and a pass/fail summary.
+
+ This table is a summary. The complete reference, including output modes, JSON
+ schemas, runtime overrides and exit codes, is in
+ CLI.md.
+
+
Flag / Command
Description
@@ -43,14 +49,45 @@
xfetch --daemon-live-reload
Force hot reload in the live stats daemon (same as "daemon_live_reload": true).
xfetch --gen-config
Generate a starter config at the default location. See GEN_CONFIG.md.
xfetch --list-layouts
Print the layout names accepted by --layout.
+
xfetch --layout <name>
Override the layout for this run. With --gen-config, select the generated layout.
+
xfetch --theme <name>
Override the active theme for this run.
+
xfetch --modules <list>, --only <list>
Override the rendered module list with a comma-separated key list.
+
xfetch --json
Emit machine-readable JSON with schema_version: 1.
+
xfetch --get <key>
Print only the requested module keys. Repeat the flag or use commas.
+
xfetch --template <file>, --format <string>
Render the fetch with a template using {{key}} placeholders.
+
xfetch --color auto|always|never, --no-color
Control ANSI output. Images are not affected.
+
xfetch --offline
Disable public IP, catalogs, update checks and logo fetch.
+
xfetch --no-cache
Bypass the package and public-IP cache for this run.
+
xfetch --quiet, --verbose
Suppress informational output or print extra diagnostics.
+
xfetch --log-level <level>
Log level: off, error, warn, info, debug or trace.
+
xfetch --log-json
Emit logs as JSON lines on stderr.
+
xfetch --list-modules
Print the module keys accepted in modules.
+
xfetch doctor [--network] [--fix] [--bench] [--image]
Diagnose the binary, config, terminal, image protocol, addons and cache. --fix applies safe fixes; --bench adds probe timings; --image previews the logo in a TTY.
+
xfetch modules
List the module registry (same as --list-modules).
The --daemon CLI flag overrides the daemon config value.
+ The complete flag reference, including --no-daemon-live,
+ --daemon-live-stop and --daemon-live-reload, is in
+ CLI.md.
+ Installs and updates write an .install.json receipt next to the
+ extension and keep the previous artifact as .bak for rollback.
+
+
+
+ The same actions are available through the unified form
+ xfetch addon extension <action>. See
+ CLI.md for the complete command and JSON reference.
+
+ xfetch is available from the Windows Package Manager:
+
+
+
winget install xfetch
+
+
+ See Windows (winget) below for update and
+ uninstall commands.
+
+
What the Script Does
@@ -265,6 +278,36 @@ makepkg -si
+
Windows (winget)
+
+
+ xfetch is available from the Windows Package Manager.
+ No Rust toolchain or compilation is needed:
+
+
+
winget install xfetch
+
+
+ To update to a newer release:
+
+
+
winget upgrade xfetch
+
+
+ To uninstall:
+
+
+
winget uninstall xfetch
+
+
+ Note: xfetch detects winget-managed installs and delegates
+ xfetch update to
+ winget upgrade --id Xfetch-cli.Xfetch --exact. winget may lag
+ behind the GitHub release while its manifest is reviewed.
+
- Prebuilt installs (install-prebuilt.sh,
- typically ~/.local/bin): the matching release asset is
- downloaded, verified against the published SHA256SUMS,
- extracted and moved over the current executable with an atomic rename. A
- single xfetch.bak keeps the previous binary and is
+ Prebuilt installs (install-prebuilt.sh on
+ Unix, install-prebuilt.ps1 on Windows): the matching release
+ asset is downloaded, verified against the published
+ SHA256SUMS, extracted and moved over the current executable.
+ Unix uses an atomic rename; Windows uses a self-replace helper because a
+ running .exe cannot be deleted. A single xfetch.bak
+ (or xfetch.exe.bak) keeps the previous binary and is
overwritten on the next update.
- Package-manager installs and local builds: never replaced;
- the command prints the right command instead.
+ Source builds (install.ps1 or
+ cargo install --path): rebuilt from the source checkout when
+ it still exists; otherwise the command prints the rebuild command. They
+ are never replaced with the crates.io release.
+
+
+ winget installs (Windows): delegated to
+ winget upgrade --id Xfetch-cli.Xfetch --exact. xfetch never
+ overwrites a winget-managed file itself. winget may lag behind the GitHub
+ release while its manifest is reviewed; in that case the command reports
+ the pending manifest instead of pretending the update succeeded.
+
+
+ Manual copies and local builds: never replaced
+ automatically; the command prints the right command or the release URL.
+
+ The PowerShell installers write
+ %LOCALAPPDATA%\xfetch\install.json to record whether the binary
+ is a prebuilt install or a source build. This is what distinguishes a
+ script-installed binary from a manual copy in the same directory. Existing
+ installs without a receipt are treated conservatively as unknown.
+
+
xfetch update --prebuilt --bin-dir <dir> forces the
- in-place update of <dir>/xfetch (Unix only). On Windows
- the prebuilt path is not available yet; use
- cargo install xfetch-cli --force or your installer of choice.
+ in-place update of <dir>/xfetch (or
+ <dir>\xfetch.exe on Windows). It refuses to touch
+ binaries owned by cargo or winget. This is also the escape hatch for a
+ manually downloaded binary.
@@ -431,7 +498,12 @@ xfetch update --yes # skip the confirmation prompt
See the Uninstallation Guide for detailed instructions.
diff --git a/docs/PLUGINS.md b/docs/PLUGINS.md
index 0110aa8..5877030 100644
--- a/docs/PLUGINS.md
+++ b/docs/PLUGINS.md
@@ -26,11 +26,14 @@
End users should install official plugins by name from the remote repository:
The core downloads the plugin source from the official remote, builds it, and installs
- the resulting binary into the xfetch plugin directory.
+ the resulting binary into the xfetch plugin directory. install --all
+ installs every plugin advertised by the catalog and skips plugins that are already
+ installed; add --force to reinstall them.
@@ -48,11 +51,29 @@ XFETCH_PLUGIN_REPO=https://github.com/user/plugins.git xfetch plugin install my-
an extra development directory to that search path.
+
+ Every successful install writes an .install.json receipt next to
+ the plugin. It records the version, source, repository and artifact hash used
+ by update. Updates rebuild native plugins from the recorded
+ source and keep the previous binary as .bak for
+ rollback.
+
+
+
+ The same actions are available through the unified form
+ xfetch addon plugin <action>. See
+ CLI.md for the complete command and JSON reference.
+
+
Manage installed plugins
xfetch plugin list
xfetch plugin list --verbose
xfetch plugin info <name>
+xfetch plugin update <name>
+xfetch plugin update --all
+xfetch plugin update --all --check
+xfetch plugin rollback <name>
xfetch plugin search <query>
xfetch plugin available
xfetch plugin remove <name>
diff --git a/docs/THEMES.md b/docs/THEMES.md
index cbcaf9f..6f8566b 100644
--- a/docs/THEMES.md
+++ b/docs/THEMES.md
@@ -136,8 +136,32 @@ xfetch theme remove nord
# Export current visual config as a reusable theme file
xfetch theme export my-theme
+
+# Apply a theme to one run without editing the config
+xfetch --theme nord
+
+# Machine-readable theme list
+xfetch --json theme list
+
+# Remote registry: search, inspect, install and update
+xfetch theme search dark
+xfetch theme info nord
+xfetch theme install nord
+xfetch theme update --all --check
+xfetch theme update nord
+
+ All theme commands accept the global --json flag. Remote commands
+ use the registry at
+ https://raw.githubusercontent.com/xfetch-cli/themes/main/index.json
+ (override with XFETCH_THEME_INDEX) and are blocked by
+ --offline. Installed themes keep a
+ .<name>.xfetch-meta.json sidecar with version and source.
+ theme update --check exits 1 when an update is available. See
+ CLI.md for the JSON shapes and exit codes.
+
- Plugins, effects and extensions can be WebAssembly artifacts instead of native
- executables. The core detects them by their binary header, runs them inside a
+ Plugins, effects, extensions and command addons can be WebAssembly artifacts
+ instead of native executables. The core detects them by their binary header, runs them inside a
sandboxed wasmtime runtime and keeps the same JSON protocol version 1, so the
rest of the system (config, timeouts, list/remove commands) is unchanged.
@@ -23,13 +23,13 @@
Core module
wasm32-wasip1
-
JSON request on stdin, JSON response on stdout, host calls through the xfetch.host_call import
+
JSON request on stdin, JSON or plain-text response on stdout, host calls through the xfetch.host_call import
Rust, C, Zig, Go, AssemblyScript, ...
Component
Component model (wasm32)
-
Export run from the WIT world, typed host imports
+
Export run from the plugin/effect/extension/command WIT world, typed host imports