A fuzzy directory navigator for your terminal. Search and jump to any directory with an interactive TUI.
- BFS Directory Discovery: Finds directories closest to your current location first
- FZF v2 Fuzzy Matching: Intelligent scoring algorithm for accurate search results
- Real-time Updates: Results appear as directories are discovered
- Smooth Performance: Async architecture with batching and heap-based ranking
- Interactive TUI: Full-screen terminal interface with real-time fuzzy search
- Shell Integration: Seamlessly cd into selected directories
- Bubble Tea - Terminal UI framework for building interactive applications
- Bubbles - TUI components for Bubble Tea (text input, viewports, etc.)
- Lipgloss - Style definitions for terminal rendering
- FZF v2 Algorithm - Fuzzy matching scoring algorithm
- Go 1.21 or later (for building from source)
- Unix-like system (Linux, macOS)
Dependencies like Bubble Tea are automatically downloaded during build - you don't need to install them manually.
./install.shThis will:
- Check for Go installation
- Build the
bcd-binbinary (automatically downloads dependencies) - Install it to
~/.local/bin/(or use--systemflag for/usr/local/bin) - Add
~/.local/binto your PATH if needed - Add shell integration function
bcd()to your shell config - Prompt you to reload your shell
Note: The binary is named bcd-bin, but you use the shell function bcd to invoke it.
After installation, run:
source ~/.bashrc # or source ~/.zshrc for Zsh./install.sh --systemInstalls to /usr/local/bin (requires sudo).
./uninstall.shThis will:
- Remove the
bcd-binbinary from~/.local/binor/usr/local/bin - Remove shell integration from your shell config files
- Create backup files of all modified configs (
.bcd_backup)
Use --force to skip the confirmation prompt:
./uninstall.sh --force- Build the binary:
go build -o bcd-bin ./cmd/bcd- Move it somewhere in your PATH:
mkdir -p ~/.local/bin
mv bcd-bin ~/.local/bin/- Add
~/.local/binto your PATH (if not already):
export PATH="$HOME/.local/bin:$PATH"- Add the shell function to your shell config:
Bash (~/.bashrc):
# bcd shell integration
bcd() {
local selected_path
selected_path="$(
command bcd-bin "$@" 2>&1 1>/dev/tty \
| tr -d '\r' \
| sed -E 's/\x1b\[[0-9;?]*[ -/]*[@-~]//g' \
| grep -oE 'BCD_SELECTED_PATH:[^[:cntrl:]]+' \
| tail -n 1 \
| sed 's/^BCD_SELECTED_PATH://'
)"
if [[ -n "$selected_path" ]]; then
if [[ -d "$selected_path" ]]; then
builtin cd -- "$selected_path" || return 1
elif [[ -f "$selected_path" ]]; then
builtin cd -- "$(dirname -- "$selected_path")" || return 1
fi
fi
}Zsh (~/.zshrc):
# bcd shell integration
bcd() {
local selected_path
selected_path="$(
command bcd-bin "$@" 2>&1 1>/dev/tty \
| tr -d '\r' \
| sed -E 's/\x1b\[[0-9;?]*[ -/]*[@-~]//g' \
| grep -oE 'BCD_SELECTED_PATH:[^[:cntrl:]]+' \
| tail -n 1 \
| sed 's/^BCD_SELECTED_PATH://'
)"
if [[ -n "$selected_path" ]]; then
if [[ -d "$selected_path" ]]; then
builtin cd -- "$selected_path" || return 1
elif [[ -f "$selected_path" ]]; then
builtin cd -- "$(dirname -- "$selected_path")" || return 1
fi
fi
}Fish (~/.config/fish/functions/bcd.fish):
# bcd shell integration
function bcd
set selected_path (
command bcd-bin $argv 2>&1 1>/dev/tty \
| tr -d '\r' \
| sed -E 's/\x1b\[[0-9;?]*[ -/]*[@-~]//g' \
| grep -oE 'BCD_SELECTED_PATH:[^[:cntrl:]]+' \
| tail -n 1 \
| sed 's/^BCD_SELECTED_PATH://'
)
if test -n "$selected_path"
if test -d "$selected_path"
builtin cd -- $selected_path
else if test -f "$selected_path"
builtin cd -- (dirname -- $selected_path)
end
end
end- Reload your shell:
source ~/.bashrc # or source ~/.zshrc for Zsh# Search from current directory
bcd
# Search from a specific directory
bcd /path/to/startNote: You invoke bcd (the shell function), which internally calls bcd-bin (the binary).
↑/↓orCtrl+p/n: Navigate resultsEnter: Select directory and cd into itEscorCtrl+c: Cancel- Type to search: Fuzzy match against directory names
- BFS Traversal: Discovers directories using breadth-first search, prioritizing closer paths
- Distance Calculation: Ranks results by path distance from starting location
- FZF v2 Scoring: Uses dynamic programming for optimal fuzzy matching
- Async Processing: Background worker processes entries without blocking the UI
- Batching: Groups directory discoveries (100 entries or 50ms intervals) for efficient processing
- Heap-Based Ranking: Maintains top results using a max-heap for O(log k) insertion
The shell function bcd() wraps the bcd-bin binary and enables cd functionality through clever I/O redirection:
- Redirection order:
2>&1 1>/dev/tty- First redirects stderr to stdout (captured by command substitution), then redirects stdout to/dev/tty(terminal) - TUI renders to terminal: stdout goes to
/dev/ttyso you can see and interact with the TUI - Selection captured via stderr: The
bcd-binbinary outputsBCD_SELECTED_PATH:/path/to/dirto stderr, which gets captured - Shell extracts path: The captured output is piped through
tr,sed, andgrepto extract the clean path - cd into directory: The shell function uses
builtin cdto change directories
This approach allows the interactive TUI to display normally while the selected path is captured for shell use. The separation of the binary (bcd-bin) and shell function (bcd) prevents naming conflicts and makes the integration cleaner.
bcd/
├── cmd/bcd/ # Main application entry point
├── internal/ # Internal packages
│ ├── crawler/ # BFS directory traversal
│ ├── entry/ # Path entry data structures
│ ├── ranker/ # FZF v2 scoring and ranking
│ └── tui/ # Bubble Tea TUI interface
├── scripts/ # Shell integration scripts
│ ├── bcd.bash # Bash integration
│ ├── bcd.zsh # Zsh integration
│ └── bcd.fish # Fish integration
├── install.sh # Installation script
├── uninstall.sh # Uninstallation script
├── LICENSE # MIT License with FZF attribution
└── README.md
This means the shell function isn't loaded. Make sure you've:
- Added the shell integration to your shell config
- Reloaded your shell:
source ~/.bashrc # or source ~/.zshrc
If bcd-bin itself is missing, ensure ~/.local/bin is in your PATH:
# Add to your shell config if not present
export PATH="$HOME/.local/bin:$PATH"
# Reload your shell
source ~/.bashrc # or source ~/.zshrcIf the bcd command doesn't cd you to the selected directory:
-
Verify the shell function is defined:
type bcd # Should show it's a shell function
-
Make sure you've reloaded your shell config after installation
-
For reinstallation, the install script will warn if integration already exists. Use the uninstall script first:
./uninstall.sh ./install.sh
The TUI requires a terminal that supports ANSI escape codes. Most modern terminals (Terminal.app, iTerm2, Alacritty, etc.) work fine.
go build -o bcd-bin ./cmd/bcdgo test ./internal/...- cmd/bcd: Entry point, handles TUI initialization and output
- internal/crawler: BFS directory discovery with concurrent traversal
- internal/entry: Path entry data structures with distance calculation
- internal/ranker: FZF v2 fuzzy matching with heap-based ranking
- internal/tui: Bubble Tea TUI with async message handling
MIT License - See LICENSE file for details
This project uses the FZF v2 algorithm for fuzzy matching. See LICENSE for attribution.