Skip to main content

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-git to 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 fzf 0.44 or newer, which must be on PATH. Your FZF_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 selected
  • 1 — nothing was selected (cancelled in fzf, or no candidates found, in which case a hint is printed on stderr)
  • 2 — an error occurred (message on stderr) — e.g. fzf missing, an unreadable search root, or a bad config file
  • 130 — interrupted with Ctrl-C

Config file

The config file path is resolved in this order:

  1. -c/--config FILE — must exist, or it's an error.
  2. $CDF_CONFIG_PATH — must exist, or it's an error.
  3. 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)

Source distribution for cdf-shell 0.1.0
File Size Uploaded
cdf_shell-0.1.0.tar.gz 71.4 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for cdf-shell 0.1.0
File Interpreter ABI Platform
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 log

Release 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

Release history Release notifications | RSS feed

This release

0.1.0 This release

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page