cdf
cdf ("cd" + "find") interactively searches the directory tree below your
current working directory for a term, and cds into the directory you
select.
- Search scope is always the current working directory downward — never the whole filesystem, never a cached/global index.
- By default it only matches git repository roots and stops descending once
it finds one (pass
--no-gitto walk every directory instead). - Symlinked directories are listed (in git mode, only if they point at a repository) but never descended into, so symlink loops can't hang the walk.
- Selection is powered by
fzf0.44 or newer, which must be onPATH. YourFZF_DEFAULT_OPTS(theme, layout,--print-query,--accept-nth, ...) are respected. - Results are shown (and fuzzy-matched) relative to the search root, so the root's own path doesn't match every query.
- Directory names are shown with terminal control characters (escape
sequences, newlines, tabs) replaced by
?, so a hostile name in a cloned repo can't mess with your terminal; the directory you pick is still the exact one on disk.
Install
From source
uv sync
This installs the cdf console script into .venv/bin/.
Arch Linux
A PKGBUILD is provided at the repo root, sourcing the tarball from this
repo's own v*.*.* git tags:
makepkg -si
cdf (the binary) only resolves a path and prints it to stdout — a Python
process can't change its parent shell's working directory. Load the shell
integration so the cdf command actually cds for you:
# ~/.bashrc
eval "$(command cdf --init bash)"
# ~/.zshrc
eval "$(command cdf --init zsh)"
# ~/.config/fish/config.fish
command cdf --init fish | source
This defines a shell function also named cdf, which takes priority over
the binary in your shell and calls it internally via command cdf. Make
sure the cdf binary itself is on PATH (e.g. via uv tool install ., or
by adding .venv/bin to PATH).
Usage
cdf [term ...] [-p/--path ROOT] [--git/--no-git] [-a/--all|--no-all] [-d/--max-depth N]
[-x/--one-file-system|--no-one-file-system] [-c/--config FILE]
cdf --init {bash,zsh,fish}
| Flag | Default | Meaning |
|---|---|---|
term ... |
"" |
initial fuzzy filter text passed to fzf (several words are joined with spaces) |
-p, --path |
cwd | root to search from |
--git / --no-git |
--git |
only match git repository roots |
-a, --all / --no-all |
--no-all |
include hidden/dot directories |
-d, --max-depth N |
no limit | only look N levels below the root (0 = no limit, to lift a limit set in the config file) |
-x, --one-file-system / --no-one-file-system |
off | list mount points but don't descend into them, like find -xdev |
-c, --config |
$CDF_CONFIG_PATH, else first existing default |
config file path |
--init SHELL |
print the shell integration for bash, zsh or fish and exit |
Run cdf --help for the full reference, or cdf --version (the shell
function special-cases --help/-h/--version/--init to pass them straight
through instead of attempting to cd into their output).
Exit codes
0— a directory was selected1— nothing was selected (cancelled infzf, or no candidates found, in which case a hint is printed on stderr)2— an error occurred (message on stderr) — e.g.fzfmissing, an unreadable search root, or a bad config file130— interrupted with Ctrl-C
Config file
The config file path is resolved in this order:
-c/--config FILE— must exist, or it's an error.$CDF_CONFIG_PATH— must exist, or it's an error.- Otherwise, the first of these that exists (silently skipped if none do):
$XDG_CONFIG_HOME/cdf/cdf.conf(default~/.config/cdf/cdf.conf), then~/cdf.conf.
All keys are optional (TOML syntax, despite the .conf extension):
path = "~/projects" # string, expanded with ~; relative paths are relative to this file
git = true # boolean
hidden = false # boolean
ignore = ["node_modules", "__pycache__", ".venv"] # glob patterns of directory names to skip
max_depth = 0 # non-negative integer, 0 = no limit
one_file_system = false # boolean
ignore replaces the default list shown above (set ignore = [] to skip
nothing). Patterns match one directory name at a time, so they can't contain
/. .git directories are never descended into regardless.
one_file_system is off by default because btrfs subvolumes (and some other
setups) report their own device IDs, so turning it on would hide those
directories. Turn it on if you run cdf --no-git from places like / or ~
that have network or FUSE mounts below them, which can be slow or hang.
Unknown keys, and values of the wrong type (e.g. git = "false" instead of
git = false), are rejected with an error, to catch typos early.
Development
make run # uv run python -m cdf
make test # pytest + ruff + ruff-format + mypy (strict) + coverage, gated at 100%
make lint # uv run ruff check .
make build # sdist + wheel, with the build backend pinned by hash (build-constraints.txt)
make build-constraints # re-lock build-constraints.txt after editing build-constraints.in
Supported Python: 3.11 through 3.14; CI runs the full make test gate on
each, with the bash/zsh/fish wrapper tests running against pinned zsh 5.9
and fish 4.9.3.
Release files for cdf-shell 0.1.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| cdf_shell-0.1.0.tar.gz | 71.4 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| cdf_shell-0.1.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 88.9 kB
Release files / cdf_shell-0.1.0.tar.gz
| Download URL | cdf_shell-0.1.0.tar.gz |
|---|---|
| Size | 71.4 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
60bd3811241dcba6929d6a606a468ed7dee7fb357b97b7e99610806ae95db162
|
|
BLAKE2b-256 checksum How to use checksums |
eeedf50121e7eebfc92e86f84a93fab485814b75c41cfb4e6d861bb22d1faa68
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 27, 2026.
Transparency logRelease files / cdf_shell-0.1.0-py3-none-any.whl
| Download URL | cdf_shell-0.1.0-py3-none-any.whl |
|---|---|
| Size | 17.6 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
bb4ac2f610de0c5a45de8f665f6ac6f3d17e98832c8af923a7e202cac4b22349
|
|
BLAKE2b-256 checksum How to use checksums |
83e14fa7738433b7c0e8f910a39f7f792c20e3e98634acdc4643e6e31d2b7685
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 27, 2026.
Transparency log