A shell script which checks your $HOME for unwanted files and directories.
3,369
stars
574
commits
Haskell
primary language
May 10, 2026
updated
A shell script that checks your $HOME for unwanted files and directories.
When xdg-ninja encounters a file or directory it knows about, it will tell you whether it's possible to move it to the appropriate location, and how to do it.
The configurations are from the arch wiki page on XDG_BASE_DIR, antidot (thanks to Scr0nch for writing a conversion tool), and crowdsourced by other users.
Clone the repository, then run the ./xdg-ninja.sh script.
git clone https://github.com/b3nj5m1n/xdg-ninja
cd xdg-ninja
./xdg-ninja.sh
This will run every test in the default configuration.
Turn on flakes, then run the following command:
nix run github:b3nj5m1n/xdg-ninja
[!NOTE] Due to how
xdg-ninjais developed, releases are not cut, so Homebrew ships a stale version, therefore you have to install and upgradexdg-ninjafrom the git HEAD. ref: #204Homebrew will not upgrade
xdg-ninjawhen running a genericbrew upgrade, you must specifically upgradexdg-ninjafrom the git HEAD, see below
Install:
brew install xdg-ninja --HEAD
Upgrade:
brew upgrade xdg-ninja --fetch-HEAD
xdg-ninja is available in many other package managers.
The full list is available on the repology page.
Follow the instructions for your package manager to install xdg-ninja.
The configuration is done in the ./programs/ directory, which should be located in the same working directory as the xdg-ninja.sh script. This can be overridden with the XN_PROGRAMS_DIR environment variable.
You define a program, and then a list of files and directories which that program ruthlessly puts into your $HOME directory.
For each file/directory, you specify if it can be (re)moved.
If this is the case, you also specify instructions on how to accomplish this in Markdown.
Files in this directory can have any name, but using the name of the program is recommended.
For x86_64 Linux systems, you can download the xdgnj binary from the releases page.
Alternatively, you can build it from source using cabal or stack, use the nix flake or use the provided docker image.
To be clear, this is just a tool that will help you automatically generate the config files, you still only need your shell to run the tests
xdgnj add # Adds a new configuration
xdgnj prev programs/FILE.json # Preview the configuration for a program
xdgnj edit programs/FILE.json # Edit the configuration for a program
xdgnj run # Mostly the same as running the shell script
[!IMPORTANT] The binaries only run on x86_64 Linux systems.
curl -fsSL -o xdgnj https://github.com/b3nj5m1n/xdg-ninja/releases/latest/download/xdgnj
chmod +x xdgnj
You can use cabal build or stack build
nix run github:b3nj5m1n/xdg-ninja#xdgnj-bin ...
Use the provided dockerfile in ./haskell/build/.
We're going to use git as an example.
By default, it puts the file .gitconfig into $HOME.
Luckily, the XDG spec is supported by git, so we can simply move the file to $XDG_CONFIG_HOME/git/config.
We can use that last sentence as our instructions. In this case, there are no newlines, so escaping this string for use in json is trivial, however, this is how you should generally approach it:
echo "Luckily, the XDG spec is supported by git, so we can simply move the file to _$XDG_CONFIG_HOME/git/config_." | jq -aRs .
Let's see what the output of this command looks like for something a little more sophisticated.
Here's an example file:
cat example.md
Currently not fixable.
_(But you can probably just delete the dir)_
Here's what catting this file into jq produces:
cat example.md | jq -aRs .
"Currently not fixable.\n\n_(But you can probably just delete the dir)_\n"
Now, we can assemble our final json file:
{
"name": "git",
"files": [
{
"path": "$HOME/.gitconfig",
"movable": true,
"help": "Luckily, the XDG spec is supported by git, so we can simply move the file to _$XDG_CONFIG_HOME/git/config_.\n"
}
]
}
Saving this as git.json in the ./programs/ directory will result in the script picking it up and checking the file.
If you've created a configuration for a file that isn't in the official repository yet, make sure to create a pull request so that other people can benefit from it as well.
(top 30 of 199)
Haskell
57.7%
Shell
31.4%
Nix
7.7%
Makefile
2.3%
Dockerfile
1.1%
A shell script which checks your $HOME for unwanted files and directories.
3,369
stars
574
commits
Haskell
primary language
May 10, 2026
updated
A shell script that checks your $HOME for unwanted files and directories.
When xdg-ninja encounters a file or directory it knows about, it will tell you whether it's possible to move it to the appropriate location, and how to do it.
The configurations are from the arch wiki page on XDG_BASE_DIR, antidot (thanks to Scr0nch for writing a conversion tool), and crowdsourced by other users.
Clone the repository, then run the ./xdg-ninja.sh script.
git clone https://github.com/b3nj5m1n/xdg-ninja
cd xdg-ninja
./xdg-ninja.sh
This will run every test in the default configuration.
Turn on flakes, then run the following command:
nix run github:b3nj5m1n/xdg-ninja
[!NOTE] Due to how
xdg-ninjais developed, releases are not cut, so Homebrew ships a stale version, therefore you have to install and upgradexdg-ninjafrom the git HEAD. ref: #204Homebrew will not upgrade
xdg-ninjawhen running a genericbrew upgrade, you must specifically upgradexdg-ninjafrom the git HEAD, see below
Install:
brew install xdg-ninja --HEAD
Upgrade:
brew upgrade xdg-ninja --fetch-HEAD
xdg-ninja is available in many other package managers.
The full list is available on the repology page.
Follow the instructions for your package manager to install xdg-ninja.
The configuration is done in the ./programs/ directory, which should be located in the same working directory as the xdg-ninja.sh script. This can be overridden with the XN_PROGRAMS_DIR environment variable.
You define a program, and then a list of files and directories which that program ruthlessly puts into your $HOME directory.
For each file/directory, you specify if it can be (re)moved.
If this is the case, you also specify instructions on how to accomplish this in Markdown.
Files in this directory can have any name, but using the name of the program is recommended.
For x86_64 Linux systems, you can download the xdgnj binary from the releases page.
Alternatively, you can build it from source using cabal or stack, use the nix flake or use the provided docker image.
To be clear, this is just a tool that will help you automatically generate the config files, you still only need your shell to run the tests
xdgnj add # Adds a new configuration
xdgnj prev programs/FILE.json # Preview the configuration for a program
xdgnj edit programs/FILE.json # Edit the configuration for a program
xdgnj run # Mostly the same as running the shell script
[!IMPORTANT] The binaries only run on x86_64 Linux systems.
curl -fsSL -o xdgnj https://github.com/b3nj5m1n/xdg-ninja/releases/latest/download/xdgnj
chmod +x xdgnj
You can use cabal build or stack build
nix run github:b3nj5m1n/xdg-ninja#xdgnj-bin ...
Use the provided dockerfile in ./haskell/build/.
We're going to use git as an example.
By default, it puts the file .gitconfig into $HOME.
Luckily, the XDG spec is supported by git, so we can simply move the file to $XDG_CONFIG_HOME/git/config.
We can use that last sentence as our instructions. In this case, there are no newlines, so escaping this string for use in json is trivial, however, this is how you should generally approach it:
echo "Luckily, the XDG spec is supported by git, so we can simply move the file to _$XDG_CONFIG_HOME/git/config_." | jq -aRs .
Let's see what the output of this command looks like for something a little more sophisticated.
Here's an example file:
cat example.md
Currently not fixable.
_(But you can probably just delete the dir)_
Here's what catting this file into jq produces:
cat example.md | jq -aRs .
"Currently not fixable.\n\n_(But you can probably just delete the dir)_\n"
Now, we can assemble our final json file:
{
"name": "git",
"files": [
{
"path": "$HOME/.gitconfig",
"movable": true,
"help": "Luckily, the XDG spec is supported by git, so we can simply move the file to _$XDG_CONFIG_HOME/git/config_.\n"
}
]
}
Saving this as git.json in the ./programs/ directory will result in the script picking it up and checking the file.
If you've created a configuration for a file that isn't in the official repository yet, make sure to create a pull request so that other people can benefit from it as well.
(top 30 of 199)
Haskell
57.7%
Shell
31.4%
Nix
7.7%
Makefile
2.3%
Dockerfile
1.1%