PSFzf: fuzzy finding inside a PowerShell prompt
A PowerShell wrapper around the fuzzy finder fzf
At a glance
- What is it?
- PSFzf is a MIT licensed PowerShell module that wraps the fzf fuzzy finder and wires it into PSReadLine, so path selection, history search and tab completion all become fuzzy pickers inside the shell. It replaces the standard tab completion, supports a git trigger command, and the last release was in September 2026.
- Who is it for?
- PSFzf is a good pick for a Windows or PowerShell user who spends the day typing paths and half-remembered commands, because the PSReadLine integration removes the need to remember anything, and the tab expansion trigger for git is the part that pays for itself fastest; it is the wrong pick if your shell work is mostly on remote Linux sessions where fzf is already wired into your own config, because you would be replacing a working setup with a Windows-oriented module, and before you install you should check whether the fzf binary is already on your path, since the module is a wrapper and not a bundle.
- Can I use it commercially?
- Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 33 days ago.
- What is it written in?
- Mainly PowerShell, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.
Editorial analysis
A wrapper around fzf, not a reimplementation
PSFzf is a PowerShell module that wraps fzf, the fuzzy file finder for the command line by Junegunn. The distinction is worth being precise about, because it determines what the module does and does not contain.
PSFzf does not implement fuzzy matching. The matching algorithm, the ranking, the interface you see in the terminal, all of that is fzf. The module is the PowerShell-facing layer: it takes objects from a pipeline, renders them into the format fzf expects, launches fzf, and turns the selected line back into a PowerShell object or a string.
That last part is the real work. In a shell, a picker that returns text is not enough when the pipeline is passing around FileInfo and DirectoryInfo objects, and the module's job is to preserve the type on the way back so that a downstream cmdlet receives the object it expects rather than a stringified copy of it.
The basic usage pattern shows the shape of it. To change to a selected directory:
Get-ChildItem . -Recurse -Attributes Directory | Invoke-Fzf | Set-LocationAnd to edit a selected file:
Get-ChildItem . -Recurse -Attributes !Directory | Invoke-Fzf | % { notepad $_ }In both cases the module is the pipe segment in the middle. Invoke-Fzf takes the objects, shows the picker, and emits the selection. The README also points at the helper functions included with the module for day-to-day use, which is the part most people end up using, since the raw pattern above is more of an illustration of the model than a daily habit.
The module is MIT licensed, published on the PowerShell Gallery as PSFzf, and the last push was on 2026-09-20 with v2.7.13 released the same day.
Four PSReadline chords and what each one does
The PSReadline integration is the reason to use this module rather than calling fzf by hand, and it wires up four chords, three of them bound by default.
Ctrl+t selects a provider path. PSFzf parses the current token and uses it as the starting path to search from, and if the current token is empty or is not a valid path it searches below the current working directory. Multiple items can be selected, and if more than one is selected the results come back as a comma separated list, with the delimiter configurable through Set-PsFzfOption -PsReadlineHandlerProviderDelimiter. Results are properly quoted if they contain whitespace, which is a small detail that saves a real class of breakage when a path has a space in it.
Ctrl+r searches backwards through the PSReadline history. The selected command is inserted into the current line and it is not executed, which is the safe behaviour: you pick the command, then edit it before it runs.
Alt+c selects a directory and calls Set-Location with it. The default command can be replaced with a script block passed to Set-PsFzfOption -AltCCommand:
$commandOverride = [ScriptBlock]{ param($Location) Write-Host $Location }
Set-PsFzfOption -AltCCommand $commandOverrideAlt+a searches through command line arguments used in the history, inserting the picked argument into the current line, with the resulting line shown in the preview window.
One thing to know is that Ctrl+r is not overridden by default, because it is PSReadline's own reverse history search. To claim it you have to say so explicitly:
Set-PsFzfOption -PSReadlineChordProvider 'Ctrl+t' -PSReadlineChordReverseHistory 'Ctrl+r'That explicit opt-in is a good design choice for a module that lives in your shell profile, since a module that silently steals Ctrl+r on install is the kind of thing people uninstall.
Replacing tab completion with a fuzzy picker
PSFzf can replace the standard tab completion entirely, and the change is one line in your profile.
Set-PSReadLineKeyHandler -Key Tab -ScriptBlock { Invoke-FzfTabCompletion }With that in place, pressing Tab opens a fuzzy picker over the possible completions rather than cycling through them one at a time. That is a much better fit for a deep directory tree, where the standard completion walks you down a path one segment at a time.
There is also a continuous mode. Pressing the directory separator character completes the current selection and then starts tab completion for the next part of the container path, so you can keep walking the tree without a new keypress each time.
On top of that sits the specialised tab expansion, which activates when you type a trigger command and press Tab. The default trigger is two asterisks, and the picker then offers a selectable list of options. Three commands are supported: git, which uses posh-git to provide the completion options and requires at least version 1.0.0 Beta 4 of it, Get-Service, Start-Service and Stop-Service, which let you pick between installed services, and Get-Process and Start-Process, which do the same for running processes.
The trigger sequence is overridable through the FZF_COMPLETION_TRIGGER environment variable, and the whole feature is enabled with:
Set-PsFzfOption -TabExpansionThe git case is the one that changes daily work. A bare two-asterisk trigger followed by Tab in a repository hands the completion choices to posh-git.
Preview window placement and pipeline usage
The tab completion preview window has its own configuration, and the option takes a compound value.
By default the preview window starts hidden and toggles with Ctrl+/ through different positions. The option format is initial_state|change_preview_window_options, where the first part sets whether the preview is initially visible or hidden, and the rest defines the states to cycle through.
# Start with visible preview on the right
Set-PsFzfOption -TabCompletionPreviewWindow 'right|down|hidden'
# Start hidden and toggle between up, left, and hidden
Set-PsFzfOption -TabCompletionPreviewWindow 'hidden|up|left|hidden'So you can start with the preview showing and cycle down to hidden, or start hidden and cycle in the other direction. The compound format is unusual, and it is the kind of detail that means the option survives being set once in a profile and forgotten about.
The pipeline integration has one documented wrinkle worth knowing. Invoke-Fzf works with input from a pipeline, and it can be used in the middle of a pipeline or as part of an expression:
Set-Location (Get-ChildItem . -Recurse | ? { $_.PSIsContainer } | Invoke-Fzf) # This works as of version 2.2.8
Get-ChildItem . -Recurse | ? { $_.PSIsContainer } | Invoke-Fzf | Set-LocationThe comment on the first line dates the behaviour to version 2.2.8, which is a useful marker if you are reading an old script or an old answer somewhere. The two forms are equivalent, and the comment on the first is a note that this specific expression form only started working at that version.
The module also ships an editor helper, Invoke-FuzzyEdit(), whose options are passed through the PSFZF_EDITOR_OPTIONS environment variable.
Five environment variables that override the defaults
PSFzf exposes fzf's own configuration surface through five environment variables, and the first one is the cleverest.
_PSFZF_FZF_DEFAULT_OPTS works by temporarily setting FZF_DEFAULT_OPTS with its contents. That means you can have different default options for PSFzf and for fzf invoked directly, which matters if you are a heavy fzf user who has spent time tuning FZF_DEFAULT_OPTS and does not want the module to change how their direct fzf calls behave.
FZF_DEFAULT_COMMAND overrides the default command when PSFzf detects that the current location is a file system provider. That is the hook for a custom listing, and it is how you would make the picker show something other than a file list.
FZF_CTRL_T_COMMAND is the command used when Ctrl+t is pressed, and FZF_ALT_C_COMMAND is the command used when Alt+c is pressed. These are separate from the Set-PsFzfOption route described earlier, and they are per-command overrides rather than script blocks.
PSFZF_EDITOR_OPTIONS contains options passed to the editor application used in the Invoke-FuzzyEdit() function.
The repository structure explains where each of these is implemented. The top level has PSFzf.psd1, which is the PowerShell module manifest, and the module is split across several files by concern: PSFzf.Base.ps1, PSFzf.Functions.ps1, PSFzf.Git.ps1, PSFzf.PSConsoleReadLineWrappers.ps1, which is the PSReadline binding, and PSFzf.TabExpansion.ps1.
There is also a PSFzf-Binary/ directory, which is where the module carries its own copy of the fzf executable, and helpers/ for the helper functions, docs/ for the per-command documentation including the Set-PsFzfOption reference, and en-US/ for the localised strings. PSScriptAnalyzerSettings.psd1 at the root shows that the module is linted with PSScriptAnalyzer, and there is a .devcontainer/ directory, a .vscode/ directory and a nuget.config for the development environment.
Release cadence, licence, and the limits of a wrapper
The project is not archived, the last push was on 2026-09-20, and the release history is regular: v2.7.13 on 2026-09-20, v2.7.12 on 2026-06-20 and v2.7.11 on 2026-04-08. Patch releases roughly every two to three months for a module that lives in your shell profile is exactly the cadence you want, because it means the module tracks changes in PSReadLine and in fzf rather than drifting.
The licence is MIT, with the LICENSE file at the repository root, and it is published on the PowerShell Gallery with badges for the current version, the prerelease version and the download count. The CI badge points at a GitHub Actions workflow.
The limits of the approach are the same as the limits of any wrapper, and they are worth stating clearly rather than discovering later.
The first is that you now have two tools where you had one. If you already have fzf configured in your own shell, on a remote Linux box for instance, PSFzf is a second front end to the same binary with its own option set, and the two can disagree about defaults. The _PSFZF_FZF_DEFAULT_OPTS mechanism exists precisely to manage that conflict, which is a hint about how common it is.
The second is that the module is Windows and PowerShell shaped. The four chords, the provider path parsing, the Set-PsFzfOption calls, the PSReadLine key handler, all of it assumes a PowerShell session. There is no cross-platform claim in the documentation and the module does not need one, but it does mean this is not the answer for a team standardised on bash and fzf directly.
The third is that the tab completion replacement is a matter of taste. The fuzzy picker is clearly better in a deep tree and clearly worse when you want the single obvious completion, which is why the module makes it opt-in with an explicit Set-PSReadLineKeyHandler call rather than enabling it on install.
Editorial conclusion
PSFzf is a good pick for a Windows or PowerShell user who spends the day typing paths and half-remembered commands, because the PSReadLine integration removes the need to remember anything, and the tab expansion trigger for git is the part that pays for itself fastest; it is the wrong pick if your shell work is mostly on remote Linux sessions where fzf is already wired into your own config, because you would be replacing a working setup with a Windows-oriented module, and before you install you should check whether the fzf binary is already on your path, since the module is a wrapper and not a bundle.
Frequently asked questions
What is PSFzf and what does it add to fzf?
PSFzf is a MIT licensed PowerShell module that wraps fzf, the fuzzy file finder. It takes objects from a pipeline, shows the fzf picker, and returns the selection as a PowerShell object, and it wires fzf into PSReadLine so path selection, history search and tab completion become fuzzy pickers inside the shell.
How do I make PSFzf use my own PSReadLine key bindings?
Pass the chords to Set-PsFzfOption, for example Set-PsFzfOption -PSReadlineChordProvider 'Ctrl+t' -PSReadlineChordReverseHistory 'Ctrl+r'. Ctrl+r is not overridden by default, so PSReadline's own reverse history search keeps working until you claim it.
How do I use PSFzf for git tab completion?
Enable the feature with Set-PsFzfOption -TabExpansion, then type the trigger command, which defaults to **, and press Tab. The git trigger uses posh-git to provide the completion options and requires at least version 1.0.0 Beta 4 of it, and the trigger sequence can be changed with the FZF_COMPLETION_TRIGGER environment variable.
Can PSFzf replace the standard tab completion?
Yes, with Set-PSReadLineKeyHandler -Key Tab -ScriptBlock { Invoke-FzfTabCompletion }. The preview window that shows the candidates is hidden by default and toggles with Ctrl+/, and its placement is configured with Set-PsFzfOption -TabCompletionPreviewWindow using the initial_state|cycle_options format.
How do I keep PSFzf from changing my fzf defaults?
Set the _PSFZF_FZF_DEFAULT_OPTS environment variable. The module temporarily sets FZF_DEFAULT_OPTS with its contents, so your direct fzf calls keep the options you configured while PSFzf uses a separate set.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/kelleyma49-psfzf)Was this useful?