Self-contained Arch Linux dotfiles for WSL, adapted from Omarchy, managed with GNU Stow.
EyrWSL carries the full terminal baseline for Arch Linux running inside WSL, plus the WSL and Windows-specific pieces: Windows Terminal and clipboard integration. It keeps Omarchy's terminal tooling and general feel while dropping desktop-specific components that do not apply inside WSL.
Eyrie is the shared project habitat, reflected locally in ~/Projects/eyrie/. Eyr is its shortened family prefix, used by EyrAgents, EyrArcHy, and EyrWSL.
Derivation model for this repo family:
AI agent harness → EyrAgents
Omarchy + personal deviations → EyrArcHy
Omarchy + WSL deviations → EyrWSL
eyragents- AI agent harness: Claude Code, Codex, and OpenCode settings, shared guidance, and commit workfloweyrarchy- Personal Omarchy customizations: Bash overrides, Hyprland bindings, Neovim plugins, and Yazieyrwsl- Self-contained WSL Arch environment: terminal baseline plus Windows Terminal and clipboard integration
Local clones live side by side under ~/Projects/eyrie/.
- Shell: Bash
- Prompt: Starship
- Terminal Workspaces: Tmux, Herdr
- AI Tools: Claude Code, Codex, and OpenCode, installed and updated through mise
- Editor: Neovim (LazyVim)
- Version Control: Git, GitHub CLI, LazyGit
- File Manager: Yazi, eza, zoxide
- Search and Preview: fd, fzf, bat, ripgrep
- System Monitor: btop
- System Info: fastfetch
- Dotfile Management: GNU Stow
- Terminal: Windows Terminal
- Theme: Gruvbox
Each top-level directory is a GNU Stow package that symlinks into $HOME, except windows-terminal/, which is deployed separately:
bash/ Shell config (.bashrc, .inputrc, .config/bash/)
btop/ System monitor config (btop.conf, themes/gruvbox.theme)
editorconfig/ Editor formatting rules (.editorconfig)
fastfetch/ System info config (config.jsonc)
git/ Git config (config, ignore)
mise/ AI tool wrappers (.local/bin/claude, codex, opencode) that install and run each tool through mise, plus the paranoid-mode fragment (.config/mise/conf.d/eyrwsl.toml)
nvim/ Self-contained Neovim config (bootstrap, lock, LazyVim config and plugins)
starship/ Prompt config (starship.toml)
tmux/ Tmux config (tmux.conf)
yazi/ File manager config (yazi.toml)
windows-terminal/ Windows Terminal settings.json, deployed explicitly, not stowed
Key ownership rules:
nvim/owns the full Neovim config, including the LazyVim bootstrap and lockfile pluslua/config/options.luawith the built-in WSL clipboard integrationnvim/includes the vault plugin specs (obsidian.lua,render-markdown.lua); the vault is expected at~/Projects/vault(override withOBSIDIAN_VAULT)- Bash supports additive machine overlays through
~/.config/bash-overlays/*; the directory is optional and reserved for untracked machine-local additions mise/owns the~/.local/binwrappers for Claude Code, Codex, and OpenCode, the filesomarchy-mise-installwrites on Omarchy minus its release-cooldown override, and the~/.config/mise/conf.d/eyrwsl.tomlfragment that turns on mise's paranoid mode; each wrapper installs its tool through mise on first run, and mise's other files (~/.config/mise/config.toml,~/.local/share/mise) are host state the wrappers create- The AI tools run as EyrAgents configures them; Omarchy's launch aliases are not carried
- Interactive Bash exports
OPENCODE_DISABLE_EXTERNAL_SKILLS=1andOPENCODE_ENABLE_EXA=1; EyrAgents owns OpenCode runtime configuration - EyrAgents owns shared OpenCode runtime and TUI configuration; its
systemtheme selection inherits the Windows Terminal ANSI palette windows-terminal/stays Windows-side and intentionally tracks the full paste-readysettings.json; setup deploys it through idempotent, backup-firstmake wt-push
EyrWSL targets current Windows 11 or a supported Windows 10 release with WSL2. Open PowerShell as Administrator, install Windows Terminal and the Nerd Font, and confirm Arch is listed online:
winget install --id Microsoft.WindowsTerminal --exact --accept-package-agreements --accept-source-agreements
winget install --id DEVCOM.JetBrainsMonoNerdFont --exact --accept-package-agreements --accept-source-agreements
wsl --list --onlineWindows Terminal uses the Windows-installed Nerd Font directly. WSL does not need a separate Linux font package for tmux, nvim, yazi, starship, or fastfetch icons to render correctly.
On a fresh Windows host, this command enables WSL and installs Arch instead of the default Ubuntu distribution:
wsl --install -d archlinuxRestart Windows if prompted. If WSL is already enabled, update it before installing Arch with the same distro-specific command:
wsl --update
wsl --install -d archlinuxAfter installation, require WSL2, make Arch the default distribution, and inspect the result:
wsl --update
wsl --set-default-version 2
wsl --set-version archlinux 2
wsl --set-default archlinux
wsl --status
wsl --list --verbosewsl --list --verbose must report archlinux at version 2 before continuing.
Launch Arch. The first shell runs as root. Set a root recovery password before creating the daily user, then update the system and install the bootstrap tools:
passwd
pacman -Syu
pacman -S --needed git neovim openssh sudo
useradd -m -G wheel -s /bin/bash <username>
passwd <username>
EDITOR=nvim visudoUncomment this line in visudo:
%wheel ALL=(ALL:ALL) ALL
Open nvim /etc/wsl.conf, then set the default user and keep Windows interop enabled:
[user]
default = <username>
[interop]
enabled = trueExit the root shell, then terminate only Arch from PowerShell so /etc/wsl.conf is applied without stopping unrelated distributions:
wsl --terminate archlinux
wsl -d archlinuxConfirm the new shell opens as <username>, and run sudo -v before continuing.
Edit /etc/locale.gen, uncomment en_US.UTF-8 UTF-8, then generate the locale:
sudo nvim /etc/locale.gen
sudo locale-gen
sudo nvim /etc/locale.confSet the following value in /etc/locale.conf, then start a fresh WSL shell:
LANG=en_US.UTF-8Install the baseline packages required by these dotfiles:
sudo pacman -S --needed 7zip bash-completion bat btop curl diffutils eza fastfetch fd file findutils \
fzf gcc git github-cli gum inetutils inotify-tools jq lazygit less lua make man-db man-pages mise \
neovim openssh procps-ng python ripgrep rsync shellcheck starship \
stow sudo tmux tree-sitter-cli unzip util-linux which yazi zoxideAll baseline packages come from official Arch repositories. mise installs and updates the AI terminal tools through the stowed wrappers (section 10), as it does on Omarchy. The local documentation baseline uses man-db and man-pages, and 7-Zip enables Yazi archive previews and extraction. Windows interoperability handles host integration, so this terminal baseline does not add the desktop-oriented xdg-utils. This repo intentionally depends on no AUR packages and installs no AUR helper.
Verify the exact baseline; successful closure prints no output. Resolve every reported package before continuing:
pacman -T 7zip bash-completion bat btop curl diffutils eza fastfetch fd file findutils \
fzf gcc git github-cli gum inetutils inotify-tools jq lazygit less lua make man-db man-pages mise \
neovim openssh procps-ng python ripgrep rsync shellcheck starship \
stow sudo tmux tree-sitter-cli unzip util-linux which yazi zoxideFor Yazi image, video, PDF, SVG, and extended archive previews, optionally install the official media helpers:
sudo pacman -S --needed ffmpeg imagemagick poppler resvgThese helpers are optional and are not required by make verify.
Herdr is not packaged in the official Arch repositories; recheck the exact package name first:
pacman -Si herdrProceed with its canonical user-level installer only while the probe reports package not found:
curl -fsSL https://herdr.dev/install.sh | shPackage ownership is Pacman-first. Before future reinstalls, recheck the official repositories; if herdr becomes packaged, replace the standalone installation with the official package.
Claude Code, Codex, and OpenCode are not installed in this step. The mise/ package stows one wrapper per tool into ~/.local/bin, and each wrapper installs its tool through mise the first time it runs (section 10). A host that installed Codex and OpenCode from Pacman or Claude Code with its native installer removes those first, or make clean reports the leftover launcher at the owned path: sudo pacman -Rns openai-codex opencode, then rm -f ~/.local/bin/claude and rm -rf ~/.local/share/claude as the Claude Code uninstall documents (settings and credentials under ~/.claude stay). Authentication and subscriptions are separate from installation; complete interactive sign-in only after the shell configuration is stowed.
Create the parent directory and clone EyrWSL. EyrAgents is optional and recommended when this host will run Claude Code, Codex, or OpenCode with the shared agent harness:
mkdir -p ~/Projects/eyrie
git clone https://github.com/peregrinus879/eyrwsl.git ~/Projects/eyrie/eyrwsl
git clone https://github.com/peregrinus879/eyragents.git ~/Projects/eyrie/eyragentsSkip the EyrAgents clone for an EyrWSL-only installation. EyrWSL can be cloned elsewhere; adjust the commands below to match its location. EyrAgents deploys itself: run make stow in its clone after this repository is stowed.
The nvim/ package includes the complete LazyVim bootstrap, static configuration, and generated plugin lockfile. Setup requires no separate Neovim configuration clone.
Tracked Git config intentionally excludes [user] identity. Create a local untracked file before using Git:
mkdir -p ~/.config/gitCreate ~/.config/git/config.local with your local identity:
[user]
name = Your Name
email = your-email@example.comChecklist before stowing:
- Required packages are installed
- EyrWSL was cloned locally
~/.config/git/config.localexists with your local Git identity- Any existing conflicting files were reviewed and moved or merged
Run the guarded preparation from the repository root:
cd ~/Projects/eyrie/eyrwsl
make cleanPreparation derives the owned paths from the package files and checks every one before changing anything. It removes only folded directory links left by a folding deployment and dangling links left by a moved or deleted clone; live leaf links stay for Stow to manage. A regular file, a foreign or unrecognized link, or a special file at an owned path aborts the entire run without partial removal. Compare and move or merge the reported conflict, then rerun make clean.
A fresh Arch user normally has a regular ~/.bashrc from /etc/skel, so expect the first preparation run to report it. Compare any needed local content, move or merge it deliberately, and rerun make clean; the script never replaces it automatically.
Link every package (the Makefile owns the package list):
cd ~/Projects/eyrie/eyrwsl
make stowStow runs without directory folding, so ~/.config/bash, ~/.config/nvim, and the other managed parents stay real directories that tools may write into; Stow reports any conflicting regular file without changing it. When EyrAgents is cloned, run make stow in its clone next so its packages are linked. Start a new terminal session, or run source ~/.bashrc, for the shell config to take effect.
cd ~/Projects/eyrie/eyrwsl
make unstowPreview what stow would do without making changes:
cd ~/Projects/eyrie/eyrwsl
make dry-runTo update symlinks after the repo content changes (same clone path):
cd ~/Projects/eyrie/eyrwsl
make restowTo migrate from a different clone path, unstow from the old location first:
make -C /old/clone/path unstow
cd ~/Projects/eyrie/eyrwsl
make stowIf the old clone is no longer available, make clean (section 8) removes its dangling links; then run make stow.
Open Neovim once to install the revisions recorded in the tracked lockfile:
nvimRun :LazyHealth, confirm Gruvbox loads, then exit and open Neovim again to verify the lock is stable. If the vault is synced to a different path, export OBSIDIAN_VAULT before launching Neovim; otherwise the vault workflow expects ~/Projects/vault. Vault synchronization and the vault's normalize.py are user-owned data, not installed by this repo.
Start each AI terminal tool once from a fresh shell and complete its own authentication flow. The stowed wrapper in ~/.local/bin runs mise use -g for its tool, which installs the current release under ~/.local/share/mise and records the latest pin in ~/.config/mise/config.toml, then starts the tool; interactive shells afterwards resolve the tool through mise activate and skip the wrapper:
claude
codex
opencodeThese wrappers keep mise's default 24-hour release cooldown (minimum_release_age), so a release installs the day after it ships, and the mup alias (mise up) keeps it too; Omarchy opts its AI tool wrappers and updater out of that cooldown while its other mise-managed tools wait it out, and here nothing opts out. Paranoid mode is on through the stowed ~/.config/mise/conf.d/eyrwsl.toml: global configs stay implicitly trusted, and a project-level mise.toml or .tool-versions is refused until mise trust accepts it. mise ls lists the installed versions.
Authentication failures do not indicate a dotfile deployment failure; resolve account access with the tool provider before testing tdw or hdw.
Launch Windows Terminal once so its settings file exists, then complete the required automated deployment step from WSL:
cd ~/Projects/eyrie/eyrwsl
make wt-push
make wt-diffmake wt-push resolves the active Windows account through PowerShell and validates both JSON files. If they already match, it exits without writing anything. If they differ, it creates a timestamped settings.json.backup-<timestamp> beside the deployed file and atomically replaces the deployment with the tracked file. The following make wt-diff confirms there is no normalized drift after Windows Terminal's key-order rewrites.
To roll back, copy the reported backup over the deployed settings.json. Delete obsolete backups manually after confirming the replacement is stable. Set WT_SETTINGS only when Windows Terminal uses a nonstandard settings path.
If automatic discovery is unavailable, open Windows Terminal settings JSON with Ctrl+Shift+, and replace its contents with the full tracked windows-terminal/settings.json. The deployed file normally lives at:
%LOCALAPPDATA%\Packages\Microsoft.WindowsTerminal_8wekyb3d8bbwe\LocalState\settings.json
After deployment, confirm the default profile resolves to archlinux and the font is JetBrainsMono Nerd Font at size 9. If Windows Terminal warns about a missing default profile, re-select it once in the settings UI. Settings UI saves can serialize the generated archlinux profile into the deployed file; run make wt-diff, then make wt-push to restore the tracked canonical form when it reports drift.
After stowing or changing owned packages:
- Run
make lintandmake checkafter any change; both are repository-only (ShellCheck; every owned Bash, Lua, TOML, JSON, JSONC, Git, tmux, btop, and Fastfetch config inrepomode; thetests/fixtures), and GitHub Actions runs them on every push tomainand every pull request, plusmake twinsagainst a fresh EyrArcHy clone. - Run
make verifyfrom the repo root on the WSL host after stowing or changing owned packages:twins, thenscripts/verify.shinfullmode (WSL2 kernel and Windows interop, the command baseline, the three AI tools installed by mise and resolving through it, every Git-visible Stow source resolving into this repo with its managed parents real directories, a GitHub no-reply Git identity that is never printed, and every owned config), then every fixture suite.
Complete these manual fresh-session checks:
- Confirm the core symlinks and local Git identity exist:
test -L ~/.bashrc && test -L ~/.config/starship.toml && test -L ~/.config/nvim/lua/config/options.lua && test -f ~/.config/git/config.local - Start a fresh shell and confirm Bash, Starship, and Tmux load without errors.
- Confirm
printenv OPENCODE_DISABLE_EXTERNAL_SKILLSandprintenv OPENCODE_ENABLE_EXAeach print1. - Start a fresh shell and confirm
alias claude c cx cy ic ix icxreports no alias for any of them: the AI tools run as EyrAgents configures them. - Confirm
type tdwshows the tmux workspace function; from a project directory,tdw cc,tdw cx, ortdw ocopens its session in one window with the agent focused (-ccontinues that agent's last conversation; baretdwre-attaches an existing session). Creating a session fails before changing tmux state when the selected agent is unavailable. - Confirm
type hdw,type hdl,type hdlm, andtype hslshow the Herdr workspace functions.hdsis intentionally unavailable because it requires Hunk. - Confirm
mise ls claude codex opencodelists an installed version of each tool, andcommand -v claude codex opencoderesolves every one under~/.local/share/mise(interactive shells, throughmise activate) or to its~/.local/binwrapper;make verifyfails when a tool is missing from mise or resolves elsewhere. - Confirm
mise settings get paranoidprintstrueandmise settings get minimum_release_agereports that the setting is not set, so the 24-hour default applies;make verifychecks paranoid mode in full mode. - Run
nvimonce and confirm plugins install successfully and Gruvbox loads. - In Neovim, confirm yanks reach the Windows clipboard and pastes from the Windows clipboard reach Neovim.
- If the vault is synced to this machine, open a vault note and confirm obsidian.nvim loads (
<leader>ooopens the note switcher). - In OpenCode, run
/themeand confirmsystemis selected so the TUI inherits Windows Terminal's Gruvbox ANSI palette. - Confirm Windows Terminal uses JetBrainsMono Nerd Font at size 9 and the Gruvbox color scheme after applying
windows-terminal/settings.json.
- WSL or Arch does not start: Confirm hardware virtualization is enabled in UEFI, run
wsl --updatefrom elevated PowerShell, and repeatwsl --statusandwsl --list --verbose. Do not continue untilarchlinuxlaunches under WSL2. - Preparation reports a conflict: Compare the reported path, move or merge any needed content, then rerun
make clean. The script never deletes regular files, foreign links, or special files; the only dangling links it removes name a package path of this repo. - Neovim clipboard not working: Confirm
clip.exeandpowershell.exeare accessible from WSL (which clip.exe). If Windows interop is disabled, check[interop]in/etc/wsl.conf. - Obsidian image paste unavailable:
:Obsidian paste_imgexpectswl-clipboardorxclip, which this WSL baseline does not install. Save the image through Windows or the vault's own workflow, then link or embed it from the note. - mise refuses a project config: Paranoid mode is on. Review the file, then
mise trustit; an edit to a trusted file prompts again. - An AI tool wrapper fails or hangs on first run: The wrapper resolves and downloads the release through mise, which needs network access; rerun it, or run
mise use -g <tool>directly to see mise's own error.mise doctorreports activation and PATH problems. - OpenCode does not match Windows Terminal: Select
systemwith/theme. When EyrAgents is installed, confirm~/.config/opencode/tui.jsonresolves into itsopencodepackage.
A repo-root Makefile keeps the package list in one place and wraps the routine commands. stow, restow, clean, verify, and wt-push run on the WSL machine; lint, check, twins, and refs run anywhere:
make stow/make unstow/make dry-run/make restow- the stow command sets over the package list, without directory foldingmake lint- ShellCheck over the bash package,scripts/, andtests/;.shellcheckrcdisables the upstream-derived warnings so new issues stand outmake check- repository-only checks:scripts/verify.shinrepomode over every owned config, then every fixture suite (runs in CI)make twins- twin-file sync against the EyrArcHy clone (SIBLING, default~/Projects/eyrie/eyrarchy); a missing sibling is reported as a skipped checkmake verify-twins, thenscripts/verify.shinfullmode (host, command baseline, mise-managed AI tools, deployment with real managed parents, no-reply identity, and every owned config), then every fixture suitemake test- fake-home deployment, ownership, verifier, Windows Terminal, and reference-clone fixtures; the loop stops on the first failing suitemake clean- WSL-only guarded stow preparation (scripts/prepare-stow.sh); leftover folded links and dangling clone links only, aborts before removing anything otherwisemake refs- clone, fast-forward, and prune the reference clones under~/Projects/quarryto the family'sreferences.txtfiles, repointing moved GitHub remotes (/omasyncstep 1)make wt-diff- diff the tracked Windows Terminal settings against the deployed Windows-side file (normalized withjq, since Windows Terminal rewrites key order)make wt-push- WSL-only, validate both settings files, back up a changed deployment, and atomically deploy the tracked file
.github/workflows/test.yml runs make lint, make check, and make twins on every push to main and every pull request.
Updates run in two steps, as Omarchy's updater does in one: sudo pacman -Syu updates the system, mise itself included (the packaged mise cannot self-update and says so when asked), and never touches the mise-managed tools; mup then brings Claude Code, Codex, and OpenCode current, the mise up call Omarchy runs after its package step, here without Omarchy's cooldown override, so a release counts once it is a day old. Under mise, Claude Code's native auto-updater is not in play; the tools change version only through mise.
nvim/.config/nvim/lazy-lock.json is generated but tracked. Update it only through an intentional Lazy sync, review the pinned revision changes, verify a clean headless bootstrap, and commit the lockfile with the plugin-spec change that required it.
Periodically, review the local reference repos and official docs for upstream changes to owned packages, sync with /omasync or a manual comparison, and confirm every intentional difference is still documented in DEVIATIONS.md. Unresolved decisions, deferred work, active limitations, and dated evidence live in docs/maintenance.md.
Upstream comparison runs through the /omasync skill; make refs keeps the reference clones listed in references.txt current. Upstream URLs and official docs live in DEVIATIONS.md (Reference Sources).
Adapted from Omarchy. See DEVIATIONS.md for intentional differences and boundary definitions.