Skip to content

codedeviate/witch

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

32 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

witch

GitHub License: MIT Rust edition 2024
Latest release crates.io Homebrew

A typo-tolerant which. You meant grep, you typed gerpwitch finds it anyway and prints the same absolute paths which would.

$ witch gerp
/usr/bin/grep

$ which gerp
gerp not found

Exact names behave exactly like which; misspelled names are fuzzy-matched against every executable on your PATH and ranked by similarity. When the output is captured — command substitution or a pipe — witch prints only the best match, so $(witch gerp) always expands to exactly one path.

Features

  • Typo-tolerant lookup — Jaro-Winkler scoring handles transpositions (grpegrep), wrong letters (pyhton3python3), and case slips
  • Exact-match short-circuit — a correctly spelled command is resolved byte-for-byte like which, fuzzy matching never engages
  • Command-substitution safe — non-TTY stdout automatically collapses to the single best match; $(witch gerp) just works
  • Quality-cliff ranking — shows the few plausible candidates, not a wall of noise: results stay close to the top score and the list ends at the first sharp drop (hard cap 10)
  • Interactive pickerwitch -i grap renders a menu on stderr and prints the chosen path to stdout, so it even works inside $( )
  • which-compatible behavior — first-in-PATH shadowing, one path per line, exit codes 0/1, multiple command arguments resolved in order

Install

Latest release: v0.2.0 — drop-in which compatibility (BSD + GNU flag superset and --strict mode). All methods below install it.

Homebrew (macOS / Linuxbrew)

brew install codedeviate/cli/witch

crates.io

cargo install witch-cli

(The crate is witch-cli; the installed binary is witch.)

From source

git clone https://github.com/codedeviate/witch.git
cd witch
cargo install --path .

Requires Rust 1.85+. Unix-only (macOS, Linux) — executable detection uses Unix permission bits.

Quick start

witch grep          # exact lookup, same as which
witch gerp          # typo-tolerant lookup, best matches first
witch -a pyhton     # all candidates, even when piped
witch -1 grap       # only the best match
$(witch gerp) -V    # command substitution: exactly one path
witch -i grap       # interactive picker (menu on stderr)
witch -q gerp       # no output, exit code only

Usage

witch [OPTIONS] <COMMANDS>...

Arguments:
  <COMMANDS>...  Command name(s) to look up

Options:
  -1, --first       Print only the best match
  -a, --all         List all matches, including PATH duplicates, even when not a TTY
  -q, --quiet       No output, exit code only
  -s                Silent; alias of --quiet (BSD which -s)
  -i, --pick        Interactively pick from the candidates
      --strict      Disable fuzzy matching; behave byte-for-byte like which
      --skip-dot    Skip PATH entries that start with `.`
      --skip-tilde  Skip PATH entries that start with `~` and entries under $HOME
      --show-dot    Print ./prog for dot-relative PATH entries
      --show-tilde  Print ~/... for matches under $HOME (ignored as root)
      --tty-only    Honor --show-dot/--show-tilde only when stdout is a TTY
      --examples    Print usage examples and exit
  -h, --help        Print help
  -V, --version     Print version

GNU which's --read-alias/--skip-alias/--read-functions/--skip-functions are
accepted for wrapper compatibility but are no-ops (witch does not parse shell
aliases or functions).

-1/-a/-i/-q with contradictory intents conflict (exit 2) instead of silently overriding each other: -1 vs -a, and -i vs any of -1/-a/-q.

Output modes

Situation Output
Exact name match that single path, always
stdout is a TTY ranked candidates, one path per line, best first
stdout is not a TTY (pipe, $( )) best match only
-1 best match only, regardless of TTY
-a full ranked list, regardless of TTY
-i with 2+ candidates menu on stderr, chosen path on stdout

Drop-in which

witch accepts the union of BSD and GNU which flags. To use it as a literal which replacement, symlink it under the name which:

ln -s "$(command -v witch)" ~/.local/bin/which

When invoked as which, witch auto-enables strict mode: fuzzy matching is disabled and behavior is byte-for-byte which — exact lookups only, silent on not-found, exit 0/1. Use --strict to get the same behavior under the witch name. In strict mode, -a lists every instance on PATH (including duplicates from repeated PATH entries), exactly like BSD which -a.

Compatibility notes

  • --show-dot and --show-tilde apply to exact (which-style) matches. Fuzzy-fallback results are always printed as plain absolute paths.
  • --tty-only gates the display flags and the interactive picker: when stdout is not a TTY, --show-dot/--show-tilde are dropped and -i will not render a menu.
  • Under --strict (or when invoked as which), fuzzy matching is off, so -i/--pick has nothing to pick from and is effectively a no-op.

How matching works

Candidates are every executable on PATH (deduped by name, first occurrence wins — the same shadowing rule which uses). Each name is scored against your query with case-insensitive Jaro-Winkler similarity:

  1. Scores below 0.8 are discarded (keeps junk from matching short names like xz)
  2. Survivors are ranked best-first, ties broken alphabetically
  3. The list keeps only results within 0.10 of the top score, ends at the first drop greater than 0.05 between neighbors, and caps at 10

Exit codes

Code Meaning
0 every query produced at least one match
1 at least one query found nothing (or the picker was cancelled)
2 usage error (unknown/conflicting flags, no arguments)

A query with no match prints witch: no match for 'xyz' to stderr.

License

MIT — see LICENSE.

About

A typo-tolerant which: fuzzy-matches command names against PATH executables and prints which-style paths. Rust, macOS + Linux.

Topics

Resources

License

Stars

0 stars

Watchers

0 watching

Forks

Packages

 
 
 

Contributors

Languages