Skip to main content

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 (tables session / message / part). Legacy per-project <repo>/.opencode/opencode.db files 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
  • 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 typing noita finds sessions containing noita. Use 'word to fall back to fuzzy for a single term, !word to exclude, ^word / word$ for prefix / suffix.
  • Uses newer fzf features to make the picker behave like a small TUI:
    • reload-sync for clean initial load and reindex without flicker
    • change-nth + FZF_NTH to switch search scopes on the fly
    • click-header so the scope tags in the header are clickable
    • bg-transform-* + {*f} to render a live footer summary of matches
    • --history for persistent query history between runs
    • --tmux so 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. Using exec-after-exit (rather than fzf's become action) 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 -C suppresses 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) or pi --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.0 because it relies on reload-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

fzf_ai-1.0.0.tar.gz (27.0 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

fzf_ai-1.0.0-py3-none-any.whl (24.4 kB view details)

Uploaded Python 3

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

Hashes for fzf_ai-1.0.0.tar.gz
Algorithm Hash digest
SHA256 0cedf9ddbdd4e18c4759deafbb8622710f7f3e0fee488bc646eed6d0d2ea5a2e
MD5 0d70dd5509f751e00a94edf91aba9a21
BLAKE2b-256 f93305b034f4c611f12c79dc3dab6ebe72b2cff3297522f164104fa461262cde

See more details on using hashes here.

Provenance

The following attestation bundles were made for fzf_ai-1.0.0.tar.gz:

Publisher: pypi-publish.yml on tuxcanfly/fzf-ai

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

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

Hashes for fzf_ai-1.0.0-py3-none-any.whl
Algorithm Hash digest
SHA256 9fe68a95a1f1205dc666f2fcbae142b8ca95b4088a3b4e3268a283620d51af07
MD5 7c8d70f8b6afd8f19d6fc2d08d522c4a
BLAKE2b-256 8dcf215ffee45ac7bc476569cf05b0c64f40d5ae3269014d8e8f3f73e6b6a6e2

See more details on using hashes here.

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

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page