Fuzzy-find and resume any AI coding session across multiple AI assistants
Project description
fzf-ai
Fuzzy-find and resume any AI coding session across claude-code, codex, opencode, droid (factory), and pi from a single fzf picker.
┌──── AI coding sessions ──────────────────────────────────────────────────┐
│ claude 2026-04-17 03:16 6 ~/Work/fzfai refactor preview │
│▶ codex 2026-04-16 12:01 53 ~/Work/kimi-ai/... ai-missions impl │
│ pi 2026-04-16 11:16 30 ~/Work/kimi-ai/... hi │
│ droid 2026-04-04 17:47 20 ~/Work/droid-ai/... tui todo app │
│ opencode 2025-08-11 20:03 2 ~/ Greeting message │
└──────────────────────────────────────────────────────────────────────────┘
What it does
- Indexes every session on disk:
- claude ─
~/.claude/projects/<proj>/<uuid>.jsonl - codex ─
~/.codex/sessions/YYYY/MM/DD/rollout-*.jsonl - opencode ─ the global sqlite db at
~/.local/share/opencode/opencode.db(tablessession/message/part). Legacy per-project<repo>/.opencode/opencode.dbfiles are deliberately skipped — those sessions are not resumable by current opencode and just created ghost rows. - droid ─
~/.factory/sessions/<proj>/<uuid>.jsonl - pi ─
~/.pi/agent/sessions/<proj>/<iso>_<uuid>.jsonl
- claude ─
- Sorts newest-first and shows a rich ANSI preview of the real conversation (first real prompt, last user prompt, recent replies, tool calls, reasoning, model, cwd). Boilerplate environment / policy blocks are hidden from the preview when possible.
- Exact-match by default across agent / cwd / title / hidden session
content snippets.
fzf runs with
--exact, so typingnoitafinds sessions containingnoita. Use'wordto fall back to fuzzy for a single term,!wordto exclude,^word/word$for prefix / suffix. - Uses newer
fzffeatures to make the picker behave like a small TUI:reload-syncfor clean initial load and reindex without flickerchange-nth+FZF_NTHto switch search scopes on the flyclick-headerso the scope tags in the header are clickablebg-transform-*+{*f}to render a live footer summary of matches--historyfor persistent query history between runs--tmuxso it opens in a popup automatically when run inside tmux
- On enter, fzf exits cleanly, then the launcher
execs the correct CLI from the session's original cwd. Usingexec-after-exit (rather than fzf'sbecomeaction) avoids a terminal-state race that caused codex to hang and opencode's input to freeze on hand-off.claude --resume <id>codex resume -C <cwd> <id>(explicit-Csuppresses codex's interactive "change directory?" prompt)opencode <cwd> --session <id>(passes the project dir as positional so opencode opens the correct db and finds the session)droid --resume <id>pi --session <path>(preferred) orpi --resume <id>
Install
Option 1: Using pip (recommended)
# Install from the source distribution
pip install fzf_ai-1.0.0.tar.gz
# Or install from PyPI when published
pip install fzf-ai
The pip installation will automatically add the scripts to your PATH if you have
the Python bin directory in your PATH (typically ~/.local/bin or ~/Library/Python/3.x/bin).
Option 2: Using Make (traditional method)
# Clone or download the repository
cd fzf-ai
make install-symlink
# Make sure ~/.local/bin is in your PATH
export PATH="$HOME/.local/bin:$PATH"
Option 3: Manual installation
# Add the bin directory to your PATH
export PATH="$PWD/bin:$PATH"
# Or create symlinks to a directory in your PATH
ln -s "$PWD/bin/fzf-ai" ~/.local/bin/fzf-ai
ln -s "$PWD/bin/fzf-ai-index" ~/.local/bin/fzf-ai-index
ln -s "$PWD/bin/fzf-ai-preview" ~/.local/bin/fzf-ai-preview
ln -s "$PWD/bin/fzf-ai-resume" ~/.local/bin/fzf-ai-resume
Option 3: Manual installation
# put the scripts on your PATH
ln -s "$PWD/bin/fzf-ai" ~/.local/bin/fzf-ai
ln -s "$PWD/bin/fzf-ai-index" ~/.local/bin/fzf-ai-index
ln -s "$PWD/bin/fzf-ai-preview" ~/.local/bin/fzf-ai-preview
ln -s "$PWD/bin/fzf-ai-resume" ~/.local/bin/fzf-ai-resume
Requires: fzf ≥ 0.63, python3 (stdlib only), each AI CLI you want to
resume on your $PATH.
PyPI Publishing
GitHub Actions trusted publishing is configured in
.github/workflows/pypi-publish.yml.
Regular validation for pushes and pull requests runs in
.github/workflows/ci.yml.
To enable it on PyPI, add a Trusted Publisher for project fzf-ai with:
- owner:
tuxcanfly - repository:
fzf-ai - workflow:
.github/workflows/pypi-publish.yml - environment:
pypi
If fzf-ai does not exist on PyPI yet, create a pending publisher under
your PyPI account with the same values.
Publishing flow:
git tag v1.0.0
git push origin v1.0.0
The workflow will build the sdist/wheel, run twine check, and publish to
PyPI using GitHub OIDC instead of a long-lived API token.
Usage
fzf-ai # browse everything
fzf-ai claude codex # only these agents
Search syntax (exact-match by default)
| query | meaning |
|---|---|
noita |
contains noita |
noita webgpu |
contains noita AND contains webgpu |
'word |
fuzzy-match for this term (unquote) |
^use |
starts with use |
.md$ |
ends with .md |
!draft |
excludes items containing draft |
| `a | b |
Keys
| key | action |
|---|---|
enter |
resume session in its native CLI (cd to its cwd) |
ctrl-o |
open a shell in the session's working directory |
ctrl-e |
open the raw .jsonl / sqlite source in $EDITOR |
ctrl-y |
copy session id to clipboard |
ctrl-k |
copy the exact resume command to clipboard |
ctrl-p |
move up |
ctrl-n |
move down |
ctrl-r |
rebuild the index in place |
ctrl-s |
cycle search scope: all → cwd → title → content → agent |
? |
toggle preview pane |
alt-p |
cycle preview position / hide preview |
alt-1..5 |
filter to claude / codex / opencode / droid / pi |
alt-0 |
clear the temporary agent filter |
Scope Switching
The header is clickable. Click [all], [cwd], [title], [content],
or [agent] to change what fzf searches against. The current
scope is reflected in the prompt and in the dynamic list label.
Query History
Search history is persisted at:
${XDG_STATE_HOME:-~/.local/state}/fzf-ai/query-history
fzf-ai rebinds ctrl-p / ctrl-n back to list navigation, so query
history remains available through fzf's alternate history actions only if
you bind them yourself.
Footer Summary
The footer is computed asynchronously from the current match set using
fzf's {*f} placeholder. It shows:
- matched session count
- distinct project count
- summed message count
- per-agent counts for the current match set
How it fits together
bin/fzf-ai ─ bash launcher wiring advanced fzf bindings
bin/fzf-ai-index ─ python: walks every session store, emits 9-col TSV
bin/fzf-ai-preview ─ python: renders a conversation preview for fzf
bin/fzf-ai-resume ─ bash: cd into cwd and exec the right AI CLI
bin/fzf-ai-ui ─ bash: dynamic labels / scope switching / footer stats
The index format (TAB-separated):
1 agent(raw) 2 session_id 3 source ← hidden, machine only
4 agent(ui) 5 updated 6 msgs ← visible, padded + ANSI
7 cwd 8 title ← visible
9 search blob (hidden session content) ← pushed past the right edge,
used for content search only
fzf runs with:
--with-nth=4,5,6,7,8,9
That keeps columns 1-3 hidden from the list while still exposing:
- transformed field
1= agent - transformed field
4= cwd - transformed field
5= title - transformed field
6= hidden content blob
So change-nth can dynamically retarget the search scope without
rebuilding the list.
Notes
- The indexer reads JSONL session stores in parallel. Set
FZFAI_INDEX_JOBS=<n>to override the worker count. - The launcher now requires
fzf >= 0.63.0because it relies onreload-sync, footer sections, async transforms, and{*f}.
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file fzf_ai-1.0.0.tar.gz.
File metadata
- Download URL: fzf_ai-1.0.0.tar.gz
- Upload date:
- Size: 27.0 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
0cedf9ddbdd4e18c4759deafbb8622710f7f3e0fee488bc646eed6d0d2ea5a2e
|
|
| MD5 |
0d70dd5509f751e00a94edf91aba9a21
|
|
| BLAKE2b-256 |
f93305b034f4c611f12c79dc3dab6ebe72b2cff3297522f164104fa461262cde
|
Provenance
The following attestation bundles were made for fzf_ai-1.0.0.tar.gz:
Publisher:
pypi-publish.yml on tuxcanfly/fzf-ai
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
fzf_ai-1.0.0.tar.gz -
Subject digest:
0cedf9ddbdd4e18c4759deafbb8622710f7f3e0fee488bc646eed6d0d2ea5a2e - Sigstore transparency entry: 1328080130
- Sigstore integration time:
-
Permalink:
tuxcanfly/fzf-ai@1b83040c4ee61dd1ff615d16fe0403a14fd56e13 -
Branch / Tag:
refs/tags/v1.0.0 - Owner: https://github.com/tuxcanfly
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
pypi-publish.yml@1b83040c4ee61dd1ff615d16fe0403a14fd56e13 -
Trigger Event:
push
-
Statement type:
File details
Details for the file fzf_ai-1.0.0-py3-none-any.whl.
File metadata
- Download URL: fzf_ai-1.0.0-py3-none-any.whl
- Upload date:
- Size: 24.4 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
9fe68a95a1f1205dc666f2fcbae142b8ca95b4088a3b4e3268a283620d51af07
|
|
| MD5 |
7c8d70f8b6afd8f19d6fc2d08d522c4a
|
|
| BLAKE2b-256 |
8dcf215ffee45ac7bc476569cf05b0c64f40d5ae3269014d8e8f3f73e6b6a6e2
|
Provenance
The following attestation bundles were made for fzf_ai-1.0.0-py3-none-any.whl:
Publisher:
pypi-publish.yml on tuxcanfly/fzf-ai
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
fzf_ai-1.0.0-py3-none-any.whl -
Subject digest:
9fe68a95a1f1205dc666f2fcbae142b8ca95b4088a3b4e3268a283620d51af07 - Sigstore transparency entry: 1328080135
- Sigstore integration time:
-
Permalink:
tuxcanfly/fzf-ai@1b83040c4ee61dd1ff615d16fe0403a14fd56e13 -
Branch / Tag:
refs/tags/v1.0.0 - Owner: https://github.com/tuxcanfly
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
pypi-publish.yml@1b83040c4ee61dd1ff615d16fe0403a14fd56e13 -
Trigger Event:
push
-
Statement type: