Back to blog

Why I Built a Shell Config Manager

the .zshrc problem

so at some point my .zshrc crossed 400 lines and I realized I had no idea what half of it did anymore. there were PATH entries from tools I uninstalled months ago, aliases I set up for a job I left, environment variables pointing at directories that don't exist. the whole thing was held together by comments like # TODO: clean this up that I wrote in 2023.

I kept telling myself I'd organize it. maybe split it into files. maybe use some dotfile manager. but every dotfile manager I looked at wanted to own my entire home directory. I didn't need that. I just needed someone to look after my shell config specifically — the PATH, the aliases, the env vars — and do it without me having to think about it.

so I built wayu.

what wayu actually does

wayu manages your shell configuration through structured files instead of one giant rc file. it handles three things: PATH entries, aliases, and environment variables. each one gets its own managed section with validation, deduplication, and automatic backups before any change.

the basic usage looks like what you'd expect:

wayu path add /usr/local/go/bin
wayu alias set ll "ls -la"
wayu env set EDITOR nvim

every command validates the input before writing. path add checks that the directory actually exists. alias set warns you if you're shadowing a system binary. env set won't let you accidentally blow away your HOME. and before any write, wayu snapshots your current config so you can roll back if something goes wrong.

but the thing I actually use most is the TUI:

wayu tui

this launches a full terminal interface where you can browse everything, search, edit inline, toggle entries on and off without deleting them. I found that for day-to-day management the CLI is great for scripting and quick additions but the TUI is where I actually understand what my shell looks like. having both turned out to be the right call even though I originally thought the TUI was a nice-to-have.

the PATH ordering problem

the thing that pushed me to v3.0.0 was PATH ordering. your PATH is an ordered list and the order matters — if /usr/local/bin comes before /usr/bin, the local version of a tool wins. but most shell configs just append to PATH with export PATH="$PATH:/new/thing" and you end up with duplicates, stale entries, and an order that's basically random depending on which rc file loaded last.

v3.0.0 introduced an array-based PATH system. internally wayu stores your PATH as an ordered array of entries, each with metadata about when it was added and whether it's currently active. reordering is a first-class operation. deduplication happens automatically. and because it's structured data instead of string concatenation, wayu can actually tell you useful things about your PATH:

wayu path list
# shows each entry with its index, status, and when it was added
 
wayu path move /usr/local/go/bin 0
# moves go to the front of PATH

the implementation in odin ended up being pretty clean. the path entries live in a dynamic array and the serialization handles the ordering:

Path_Entry :: struct {
    value:    string,
    active:   bool,
    added_at: time.Time,
}

Config :: struct {
    paths:   [dynamic]Path_Entry,
    aliases: map[string]string,
    env:     map[string]string,
}

odin's explicit memory management meant I could be precise about allocations. the whole thing runs through valgrind clean — 272 tests, zero memory leaks. that's not something I set out to achieve as a goal, it's just what happens when the language makes you think about ownership from the start.

why both cli and tui

I went back and forth on this. building a TUI is significantly more work than just a CLI. but after using wayu with only the CLI for a few weeks I noticed a pattern: I'd run wayu path list, then wayu alias list, then wayu env list, scrolling through each one trying to get a picture of my overall config. the TUI collapses all of that into one view.

the CLI is still the primary interface for automation. I have it in my setup scripts, I use it in CI for validating configs. but the TUI is where I go when I'm thinking about my shell rather than just adding one thing to it.

what it feels like to use daily

honestly the best part is not thinking about it. I add a new tool, I run wayu path add, and I know it's validated, backed up, and in the right position. I don't open my .zshrc anymore. I don't scroll through hundreds of lines looking for where the aliases are. I don't wonder if that PATH entry is duplicated somewhere.

it's a small tool that solves a small problem. but it's a problem I had every single day and now I don't.