Move between folders the way you scroll an iPhone alarm clock — visually, not by remembering paths.
Crafted by Noar.Company · MIT Licensed · Open Source
cd was designed for a world where you remembered every path. We don't live there anymore. We have a hundred projects, three drives, two cloud-synced home folders, a WSL mount, and a brain that forgot most of them five minutes ago.
cdx replaces cd with a visual picker. Open it from any prompt, scroll your folders like a wheel, type a few letters to fuzzy-find, hit Enter — your shell drops you exactly where you wanted to be.
It's the alarm-clock spinner from your phone, applied to filesystem navigation. The folders you visit most bubble to the top automatically (frecency, the algorithm zoxide popularized). A live preview shows you what's inside before you commit. The whole thing is one binary, zero runtime dependency, drop and run.
✻ cdx · Noar.Company Ai Solutions A: › 2026 projetos › cdx
╭─ drives ──╮ ╭─ folders ──────────────╮ ╭─ preview ────────╮
│ ▮ A: │ │ .. │ │ App.tsx │
│❯ ▮ C: │ │ ❯ ✦ src/ │ │ PaneDrives.tsx │
│ ▮ D: │ │ ✦ dist/ │ │ PaneFolders.tsx │
│ ~ home │ │ node_modules/ │ │ PanePreview.tsx │
│ │ │ package.json │ │ Shimmer.tsx │
╰────────────╯ │ tsconfig.json │ │ styles.ts │
╰─────────────────────────╯ ╰──────────────────╯
↑↓ navigate → enter ← back ⏎ cd tab focus / search esc quit
The wordmark
✻ cdx · Noar.Company Ai Solutionsis animated — a warm gradient (coral → amber → gold → cream) shimmers through the letters left-to-right, like Claude Code'sPrestidigitating….
Three pain points, one tool:
cdis a memory test. Tab-completion only helps if you remember enough of the prefix. We forget. Visual navigation removes the requirement.zoxideis great until you don't know the name. Frecency works when you've been there before, but new projects mean blind navigation again.ranger/lf/brootare powerful but heavy. They're full file managers. We don't want to manage files. We want to get to a folder fast and let the shell do its thing.
cdx is the smallest possible thing that solves the actual problem: a focused picker, a wheel-spinner UX, frecency, fuzzy, preview, and that's it. One binary, no config required, install in 30 seconds.
- Wheel-picker navigation. Arrow keys are everything. Up/down spins the wheel; right enters; left backs out.
- Cross-platform. Single self-contained binary for Windows, Linux, macOS, WSL. No Node, no Bun, no nothing.
- Frecency ranking. Visited folders surface to top. Same algorithm zoxide uses —
frequency × decay(age). - Fuzzy search. Type any letter to filter; matches highlighted in gold. Subsequence matcher with word-start bonuses.
- Live preview. Selected folder's contents render in a side pane (lazy-loaded, debounced).
- Multi-pane layout. Drives · folders · preview, side-by-side. Tab switches focus.
- Animated wordmark. Yeah, a small thing, but it feels alive. Warm gradient shimmer.
- Shell-agnostic. bash, zsh, fish, PowerShell —
cdx init <shell>prints the wrapper.
Download from Releases and drop into a folder on your PATH.
| Platform | File |
|---|---|
| Windows x64 | cdx.exe |
| Linux x64 | cdx |
| macOS | (planned) |
Requires Bun ≥ 1.3.
git clone https://github.com/Felipevieiraemp/cdx.git
cd cdx
bun install
bun run build # produces dist/cdx.exe and dist/cdxA child process can't change its parent shell's working directory. The standard trick (used by zoxide, fzf, lf) is a shell wrapper: cdx writes the chosen path; the wrapper calls cd. Run cdx init <shell> to print the wrapper for your shell:
echo 'eval "$(cdx init bash)"' >> ~/.bashrc
# zsh users: replace bash with zsh and the file with ~/.zshrccdx init fish | source
# Or persist:
cdx init fish > ~/.config/fish/conf.d/cdx.fishcdx init powershell | Out-String | Invoke-Expression
# To persist, add the same line to your $PROFILEAfter setup, just type cdx and the picker opens. Hit Enter on a folder and your shell jumps in.
cdx # open the picker at the current directory
cdx <query> # open with a query pre-filled in the fuzzy filter
cdx --prune # remove dead paths from the frecency database
cdx init <shell> # print the shell wrapper (bash | zsh | fish | powershell)
cdx --help # show all options| Key | Action |
|---|---|
↑ ↓ |
move within active pane (wraps around) |
→ |
enter the selected folder |
← |
go up one level (or delete a query character if searching) |
Tab |
toggle focus between drives and folders |
⏎ |
confirm — exits TUI and your shell jumps in |
Esc |
clear the search; press again to cancel and quit |
Ctrl+H |
toggle hidden files |
Ctrl+N |
new folder — creates a folder inside the current directory and selects it |
PgUp PgDn |
jump 10 entries |
| any letter | start fuzzy filtering the current pane |
A common dev workflow: navigate to a parent dir, spin up an isolated subfolder for a session, jump in. Press Ctrl+N from anywhere in the picker, type a name, hit Enter. The folder is created inside the current cwd, the cursor lands on it, and Enter again drops you in. Esc cancels.
+ new folder in projects: my-feature_
Validation rejects empty names, ., .., and the usual invalid filename characters (< > : " | ? * \ /). If a folder with that name already exists you get an already exists hint.
Inspired by the iOS alarm time picker and bank-vault password dials. Each pane is a "wheel" — drives on the left, folders in the middle. Up/down spins the wheel, right enters the selection (changes wheels), left backs out. The metaphor handles the spatial reasoning for you: there's always a clear "current" item, a clear neighbor above, a clear neighbor below.
Pure recency (most-recently-visited first) is wrong: you'll bounce a folder to the top by visiting it once, even if you never come back. Pure frequency is also wrong: yesterday's hot folder shouldn't outrank today's. The right answer is frecency: score = frequency × decay(age), exactly the formula zoxide uses.
age < 1 hour → multiplier × 4
age < 1 day → multiplier × 2
age < 1 week → multiplier × 0.5
older → multiplier × 0.25
Folders with a frecency record get a ✦ badge so you can see what cdx thinks is hot.
The naive way to communicate the chosen path back to the shell is stdout: cdx prints the path, the wrapper does d=$(cdx) && cd "$d". This works on bash. PowerShell 5.1 wraps every line of stderr from a native command in a NativeCommandError record — the TUI's escape codes turn into spam.
cdx avoids this entirely by writing the chosen path to a temp file referenced via the CDX_OUT_FILE environment variable. The wrapper sets the var, runs cdx (no output capture), reads the file. Same approach as lf and nnn.
The build pipeline is bun build --compile --target=bun-windows-x64, which bundles the entire Bun runtime, your TypeScript, your dependencies, and your assets into a single executable. Roughly 110–120 MB on disk, but the user installs nothing. No Node, no global packages, no npm install -g. You drop the binary on PATH and it works.
┌─────────────────────┐
keyboard input ───▶ │ bubbletea-style │
│ Update / View │ ◀──── frecency.Db
│ (Ink + React) │ ◀──── filesystem
└─────────┬───────────┘
│
▼
┌───────────┬─────────────────────┬────────────────┐
│ drives │ folders │ preview │
│ PaneA │ PaneB │ PaneC │
└───────────┴─────────────────────┴────────────────┘
│
▼
chosen path
│
▼
write %TEMP%/cdx-out.tmp
│
▼
shell wrapper does cd
The TUI runs as a stateful React tree on Ink, with useInput capturing keyboard events. Three panes share state through the root App component. Filesystem reads are async and aborted via alive flags when the user navigates away. The preview pane debounces by 100 ms to avoid thrashing the disk on fast cursor movement.
The frecency database is plain JSON, persisted atomically (write-temp + rename):
| OS | Path |
|---|---|
| Windows | %APPDATA%\cdx\db.json |
| Linux/macOS | $XDG_DATA_HOME/cdx/db.json, falling back to ~/.local/share/cdx/db.json |
{
"version": 1,
"entries": {
"C:\\Users\\you\\projects": { "frequency": 12, "lastAccess": 1714680000 }
}
}Run cdx --prune to drop entries whose paths no longer exist on disk.
| Layer | Choice | Why |
|---|---|---|
| Runtime + bundler + compiler | Bun 1.3 | TypeScript native, fast, --compile produces single binaries for any target |
| TUI framework | Ink 5 | React + Yoga flexbox in the terminal |
| Language | TypeScript strict | type-safe state machine, noUncheckedIndexedAccess |
| Tests | bun test |
19 tests covering listing, fuzzy ranking, frecency math |
cdx/
├── src/
│ ├── index.ts # CLI entry — args, subcommands, render
│ ├── tui/
│ │ ├── App.tsx # root model — state machine, key handler
│ │ ├── PaneDrives.tsx # left wheel — drives
│ │ ├── PaneFolders.tsx # middle wheel — folders + fuzzy
│ │ ├── PanePreview.tsx # right pane — lazy preview
│ │ ├── Shimmer.tsx # animated wordmark component
│ │ └── styles.ts # warm palette + glyphs
│ ├── fs/
│ │ ├── drives.ts # cross-platform drive enumeration
│ │ ├── list.ts # directory reading + sorting
│ │ └── fuzzy.ts # subsequence matcher with bonuses
│ ├── frecency/
│ │ ├── score.ts # zoxide-style scoring + bumping
│ │ └── store.ts # atomic JSON persistence
│ └── shellinit/
│ └── index.ts # bash / zsh / fish / pwsh wrappers
├── package.json
├── tsconfig.json
├── LICENSE
└── README.md
bun install # one-time
bun run dev # run the TUI from source
bun test # 19 unit tests
bun run build:win # dist/cdx.exe
bun run build:linux # dist/cdx
bun run build # bothWhen you change the wordmark animation or styles, hot-reload doesn't apply (it's a TUI, not a web app) — kill the running cdx and re-run bun run dev.
- Global frecency picker mode (browse all known dirs, not just current cwd)
- Named bookmarks / marks (jump to
@work,@personal, …) - Custom keymap config file
- Theme system (currently warm-only; cool/high-contrast variants)
- macOS arm64 binary
- Homebrew tap, scoop bucket, AUR
- Symlink resolution display
- Symlinked-folder expand inline
PRs welcome.
cdx stands on the shoulders of:
- zoxide — frecency for
cd(algorithm reused, gratefully) - fzf — fuzzy filtering UX, sidecar-file pattern
- broot, ranger, lf — TUI navigators that proved this category mattered
- The iPhone alarm wheel — the "scroll like time" interaction model
- Claude Code — the warm-shimmer wordmark animation
Issues and PRs welcome. The project is small and intentionally so — keep changes focused, write a test if you can, and follow the existing code style (TypeScript strict, no comments unless the why is non-obvious).
# fork, clone, branch
git checkout -b feature/your-thing
# develop
bun install
bun run dev
bun test
# submit
git push origin feature/your-thing
gh pr createMIT © 2026 Noar.Company
cdx is one of the open source tools published by Noar.Company — a studio building thoughtful developer tooling and AI solutions. We believe terminals deserve craft.
Made with ✻ · Open source · Built to be small