Commands
The modern command line: the names you know, far better tools underneath.
The classic Unix commands have faster, friendlier rewrites. This setup installs them and points the familiar names straight at them, because these are the same commands agents run, quote, and debug with you. Learn the words once and you can supervise both your own shell and an agent's shell.
z name jump to a foldermkdir -p folder add a folderpwd / open . show / open hererg text search file contentscopy-context copy code context#Already wired up
Two of the commands you'll type most are remapped the moment your shell starts. You don't do anything — ls and cat already run the better tools:
ls | runs eza — colored, sorted, folders first |
ll | eza in long form with permissions, sizes, and a git status column |
lt | eza as a two-deep tree, the quick shape-of-a-repo view (ltt goes three deep) |
cat | runs bat — syntax highlighting and line numbers |
Need the plain version? Put a backslash in front: \ls or \cat runs the untouched original. You never lose the classic behavior.
#Moving on the command line
Before you reach for the mouse or the arrow keys, these editing shortcuts move and erase text far faster. They work on every command you type, in any shell.
| CtrlA / CtrlE | Jump to the start / end of the line |
| AltB / AltF | Move back / forward one word |
| CtrlW | Delete the word behind the cursor |
| CtrlU / CtrlK | Delete to the start / end of the line |
| CtrlY | Paste back whatever you just deleted |
| CtrlR | Search your history — start typing, pick, Enter |
| CtrlP / CtrlN | Previous / next command — the home-row ↑/↓, so your hands stay put |
Two more that save real typing: as you type, the shell suggests the rest of a past command in grey — press CtrlE to accept it without leaving the home row. (It's the same "jump to end of line" key; since the line ends with the suggestion, that fills it in. The → arrow works too, but reaching for it is the habit worth dropping.) And !! repeats your last command (great as sudo !!), while !$ reuses its last argument.
Type a folder's name with no cd in front to jump straight into it. Run cd - then Tab to bounce between recent folders. And start any command with a space to keep it out of your history — handy for anything with a password or token in it.
The commands on this page are identical whether or not you're in tmux. Pane and Neovim-split navigation uses Alth/j/k/l, leaving the classic shell controls alone: CtrlL clears, CtrlK deletes to the end of the line, and CtrlR searches history.
#Folders and files
These are the folder moves beginners ask for first, and the commands experienced developers still type all day. . means "this folder"; .. means "the parent folder."
pwd | Print the current folder path |
open . | Open the current folder in Finder on macOS. In WSL, use explorer.exe . |
cd .. | Move up one folder |
cd - | Jump back to the previous folder |
mkdir -p notes/ideas | Add a folder, including parent folders if needed |
touch todo.md | Add an empty file or update its timestamp |
mv old new | Rename or move a file/folder |
cp -R src dest | Copy a folder recursively |
rm -i file | Remove a file, asking before it does it |
rm is permanent from the shell. Beginners should use rm -i until they understand exactly what will disappear, and avoid copied commands like rm -rf unless they can explain every character.
#The daily drivers
Command names are easier to remember when they connect to the older Unix word they replace. Some names have official explanations; others are best treated as mnemonics, not gospel.
eza | Think "exa, continued." The official project describes it as a modern ls replacement and lists features beyond exa, but does not publish a clean expansion for the name. |
bat | A cat clone "with wings": same job as cat, upgraded with highlighting, Git markers, and paging. |
rg / ripgrep | "Rip" as in rip through a project fast; rg is the two-letter command you type instead of long grep forms. |
fd | A short, friendly find. Remember it as "find, but direct." |
zoxide | The z command is the memory hook: jump to frequently used folders by typing a few letters. |
fzf | Fuzzy finder. It finds the thing even when you only remember a fuzzy part of the name. |
yazi | A visual file browser (the y command). Think "the folder landscape, with previews" — a TUI over the same fd/rg/fzf tools. |
atuin | A shell-history vault. Use the practical memory hook: "a tuned-in history." |
delta | Delta means change; this makes Git changes easier to read. |
dust | A friendlier du that shows what is taking space. Think "disk usage, sorted tree." |
duf | Disk usage/free utility: the readable replacement for df. |
btop | A "better top": live CPU, memory, and process view of the whole machine. |
tokei | Japanese for clock/time; here it quickly counts code by language. |
hyperfine | A benchmarking tool for fine-grained timing comparisons. |
starship | The prompt: the control panel you see before every command. |
direnv | Directory environment: load project-specific env vars when you enter a folder. |
mise | Short for "mise-en-place": put project tools and versions in their right place. |
eza — listing files
Everything ls did, plus git status, icons, and a real tree view. The name is best remembered as the maintained, modern successor to exa.
$ ll
$ lt
# a two-deep tree of the current folder (eza --tree --level=2); ltt goes three deep
bat — reading files
A cat that highlights code, numbers lines, and marks git changes in the gutter. Piped into something else it quietly behaves like plain cat, so it never breaks a pipeline.
$ bat README.md
$ bat -p script.sh
# -p = plain, no line numbers or borders
ripgrep — searching inside files
Search every file in a project for text, fast. Groundwork configures rg with smart case: lowercase searches match any casing, while a capital letter makes the search case-sensitive. By default it respects .gitignore and skips hidden files; use rga when you deliberately want hidden and ignored files too.
$ rg test # smart-case: finds test, Test, TEST
$ rg Test # capital letter makes it case-sensitive
$ rg -t md heading # only markdown files
$ rga api_key # include hidden + ignored files, still skip .git
For fuzzy content search, use rgf. It runs ripgrep, opens the matches in fzf, previews with bat, and opens the selected line in your editor.
$ rgf button
$ rgf # live search as you type
fd — finding files by name
A simpler, quicker find. Type part of a name; it searches from here down, ignoring junk folders.
$ fd config
$ fd -e png # files ending in .png
zoxide — jumping to folders
It remembers the folders you visit. After you've been somewhere once, z plus a few letters teleports you back from anywhere — no long paths.
$ z groundwork
$ z know # jumps to ~/code/my-knowledge
fzf — fuzzy-finding anything
A fuzzy picker wired into the shell. Groundwork points its file picker at rg --files, so fuzzy file selection respects the same ignore rules as ripgrep. Three keystrokes are worth memorizing:
| CtrlR | Search your command history — start typing, pick, Enter |
| CtrlT | Insert a file path into the line you're typing, with a bat preview; opens as a floating popup inside tmux |
| AltC | Fuzzy-pick a folder and cd into it |
yazi — browsing the folder landscape
When you want to see a directory rather than list it — skim a tree, preview files as you arrow through them, do a quick bulk rename or move — y opens Yazi, a visual file browser. It is a choice, not a replacement for cd, fd, and eza: those composable commands stay the fundamentals and the shared language with agents. Yazi is the visual layer on top for the moments you would otherwise leave the terminal for a GUI file manager. Quitting is a choice between two keys: q lands the shell in whatever folder you were browsing, so a visual detour ends where you looked; Q quits without changing directory, for when the browsing was just a peek and you want to stay where you started.
$ y
# arrow keys or hjkl to move, Enter to open in the editor, O to pick an opener
# q quits into the folder you were browsing; Q quits back where you started
Groundwork manages ~/.config/yazi/yazi.toml so the preview pane gets most of the width — the panes are for navigating, the preview is for reading. The preview is still a glance, not a reader: press Enter to open the file in the editor, or O and choose Read with bat for a highlighted, scrollable view that drops you back into Yazi when you quit it.
Image, PDF, and video previews render inline in Ghostty, which speaks the Kitty graphics protocol. Yazi already uses the fd, rg, fzf, and bat that Groundwork installs; for richer previews of more formats you can optionally add poppler (PDF) and ffmpeg (video thumbnails).
largest: find space, then clean by owner
largest wraps dust in a command that is easier to remember. It shows the biggest entries first, stays on one filesystem, and follows the scan with a cleanup guide. Start in the narrowest useful folder: scanning a project is cheaper and easier to reason about than scanning the whole drive.
$ largest # current folder, top 50
$ largest ~ # home folder
$ largest --files / # files on the startup filesystem
$ largest --folders -n 25 /Users
$ largest --help
On macOS, a root scan can only see protected locations when the terminal app has Full Disk Access in System Settings. On Linux, rerun with sudo only when the permission errors hide a location you actually need to inspect. Neither changes what is safe to delete.
| Apps | Use the vendor uninstaller when one exists. For a Homebrew app use brew uninstall --cask NAME; use AppCleaner for a plain dragged-in app. Treat --zap as an explicit deeper cleanup because it can remove shared preferences and caches. |
| Project output | Folders such as node_modules, .venv, target, dist, build, and coverage are often reproducible. Confirm with the repo before removing them, then let its package/build tool recreate them. |
| Package caches | Use the owner, such as brew cleanup or pnpm store prune. Shared caches are not corruption; removing them trades space now for downloads later. |
| Containers | Run groundwork-doctor (or docker system df) first for a read-only picture of usage and leftovers. Clean stopped containers, images, build cache, and volumes deliberately — a repo's own clean commands first, then Docker Desktop or the matching Docker prune command. |
| Xcode | Remove runtimes in Xcode Settings, delete unavailable simulators with xcrun simctl delete unavailable, and remember that DerivedData can be rebuilt. |
| Archives and media | Review old .dmg, .pkg, .zip, video, and audio files. Trash replaceable downloads; move irreplaceable originals to deliberate archive storage. |
| Photos and cloud files | Delete through Photos or the sync provider so its database, cloud state, and recently-deleted recovery stay consistent. |
| Logs, caches, and system data | Prefer the owning app's retention controls or macOS System Settings → General → Storage. Do not bulk-delete ~/Library, /var, APFS snapshots, or protected OS data just because it is large. |
A large file may be the only copy of something important. Inspect it, identify the owner, check backups, and use Trash when practical so recovery remains possible.
copy-context — sharing exact files with another AI tool
When a chat app or outside reviewer cannot see your repo, copy a small, explicit bundle instead of pasting random snippets. copy-context takes files or folders, expands folders through rg --files when available, and puts path-labeled contents on the clipboard. It works the same way inside tmux because it is just a shell command.
$ copy-context AGENTS.md src tests
$ copy-context --stdout docs/workflow.html
$ copy-context --paths-only src
Default folder expansion respects ignore rules, so dependency and build folders stay out of the prompt. Use --stdout before pasting when the files are sensitive or large, and never copy secrets into a chat tool.
#Also installed
You won't reach for these every hour, but they're there when you need them.
atuin | Records every command with its context, fully searchable — a much smarter history behind CtrlR |
delta | Makes git diff readable — syntax-highlighted, line-by-line |
dust | Shows what's eating disk space, as a tree (a friendlier du) |
duf | Free disk space in clean tables (a friendlier df) |
btop | Live CPU, memory, and process monitor (a friendlier top) — your first stop when the machine feels slow |
tokei | Counts lines of code in a project, by language |
hyperfine | Benchmarks a command properly, over many runs |
starship | The prompt itself — your OS and current folder on every line, so copied terminal text carries its context |
direnv | Loads per-folder settings from a .envrc when you enter (after you allow it) |
mise | Gives each project the right tool versions (Node, Python…) automatically |
#Practice
See git status while you list
Inside any Git repo, run ll and look at the status column on the left. Change a file, run it again, watch the marker appear. Then map the layout with lt.
Find, then search, then read
Locate a file by name with fd readme. Search the project's contents with rg "install". Open one of the hits with bat <file> and notice the highlighting. That find → search → read loop is most of a workday.
Teleport between folders
Visit two or three deep folders normally with cd. Now, from anywhere, jump straight back with z and a few letters of one. The more you use it, the better its guesses get.
Stop retyping commands
Press CtrlR and type a few letters of something you ran earlier. Pick it, Enter. Then try CtrlT mid-command to drop in a file path without typing it.
Edit a line without arrow keys
Type a long command but don't run it. Jump to the front with CtrlA, to the end with CtrlE, rub out the last word with CtrlW, then wipe the whole line with CtrlU. A week of this and your hands stop leaving the home row.
Don't try to absorb the whole table today. Pick rg and z this week — they pay off immediately — and let the rest arrive as you need them. Every tool answers --help.
#Under the hood: the zsh setup
The shell you're typing into is zsh, and a few small pieces make it feel modern. You don't configure any of this by hand — it's all in ~/.zshrc and managed by the setup — but here's what's running and why.
Plugins, loaded by antidote
antidote is the plugin manager. It reads a plain list of plugins from ~/.zsh_plugins.txt and loads them fast (it pre-compiles them so your shell still starts instantly). Three plugins are on that list:
| zsh-autosuggestions | The grey "ghost" text that predicts the rest of a command from your history. Press CtrlE to accept the whole thing (home row — no reaching for the arrows), or AltF to take just the next word. |
| fast-syntax-highlighting | Colors your command line as you type — valid commands go one color, unknown ones another, quotes and paths are highlighted. A typo in a command name is visible before you hit Enter. |
| ez-compinit | Plumbing: it makes zsh's tab-completion system initialize correctly and quickly. You never interact with it directly — it just keeps Tab completion fast and unbroken. |
Want another? Add its user/repo to ~/.zsh_plugins.txt (via chezmoi edit so it sticks), then exec zsh to reload. antidote clones and compiles it on the next start.
The init lines — what each tool hooks into your shell
Several tools need a line in ~/.zshrc to wire themselves in. This is where the prompt, directory-jumping, history, and version-switching come from:
starship | Draws the prompt — your OS badge and current folder, plus how long the last command took. The folder repeats on every line so pasted terminal output tells an AI tool where commands ran; git status answers branch questions and mise current answers which tool versions are active |
zoxide | The z command. Type zi instead for an interactive fuzzy picker of your visited folders |
atuin | The smarter CtrlR history (searchable by folder, exit code, time). It leaves your ↑ arrow alone for plain recall, and Groundwork keeps Atuin AI's bare ? binding off until you opt in |
fzf | The fuzzy finder behind CtrlR / CtrlT / AltC |
mise | Switches tool versions (Node, Python, Go…) automatically per project — see below |
direnv | Loads a folder's .envrc when you cd in (after you approve it once) |
History that's actually useful
zsh is tuned to keep a long, shared, de-duplicated history: 100,000 lines, written across all your open tabs at once, with exact duplicates collapsed. Two habits this unlocks: start a command with a space and it's kept out of history entirely (for anything with a secret in it), and CtrlR (atuin) searches the whole thing instantly.
Recent Atuin versions include an English-to-shell assistant. Groundwork disables the bare ? binding by default so an empty prompt does not open a setup screen by surprise. To try it deliberately, run atuin setup, create the local marker with mkdir -p ~/.config/groundwork && touch ~/.config/groundwork/enable-atuin-ai, then restart the shell with exec zsh. You can still run the assistant directly with atuin ai inline.
Per-project tool versions — mise
The experienced-developer payoff. Instead of juggling nvm, pyenv, and friends, mise reads a mise.toml (or .tool-versions) in a project and gives you exactly the runtimes it asks for — the moment you cd in.
$ cd ~/code/my-app
$ mise use node@22 python@3.13 # pins them in mise.toml + installs
$ node --version # the pinned version, here only
$ mise install # on a fresh clone: grab everything the toml asks for
Walk into a different project and the versions change to match it automatically. Globally, this setup pins Node LTS, pnpm, and Python (in ~/.config/mise/config.toml), so every machine agrees on the baseline. mise install installs missing declared versions; mise upgrade deliberately advances floating baselines such as Node LTS to the newest matching patch. Upgrades are live at your next prompt, even in terminals that are already open, because mise refreshes the shell's PATH before each prompt. Python projects should still use uv for dependencies and virtual environments; mise supplies the interpreter and will source an existing uv .venv when you enter the project.
Resource defaults — open files and parallel builds
Two lines in ~/.zshrc make sure commands actually use a modern machine. Both are derived from the machine they run on, so the same setup behaves correctly on a laptop, a Linux box, or inside a container.
| Open-file limit | macOS still caps each process at 256 open files by default — a limit from another era. File watchers, bundlers, language servers, and agent sessions blow through it and fail with a confusing too many open files. The shell raises its soft limit to 10,240 (never above the hard limit the system allows), and every command you or an agent launches from it inherits the room. |
make parallelism | Most modern build tools detect your CPU cores and parallelize on their own. Plain make does not — it builds one file at a time unless told otherwise. MAKEFLAGS is set to -j<cores>, counted on this machine at shell start, so compiling dependencies uses the hardware you paid for. |
What's deliberately not configured: hard CPU or memory caps. macOS can't enforce per-process memory limits anyway, and a cap that silently kills a build is a worse beginner experience than a slow one. When you genuinely need hard limits — a runaway workload, or bounding an agent's sandbox — run it in a container, which is where real limits live: docker run --cpus 2 --memory 2g …. To see what's using the machine right now, run btop.
#Going deeper
- The Missing Semester: shell tools & scripting — how these fit together.
- Command line & Unix — this guide's deeper page: core commands, scripting, and curated resources.
- ShellCheck & explainshell — lint a script / explain any command line, piece by piece.
- mise documentation — per-project tools, env, and tasks in depth.