Omahub
← All plugins
S

File Finder

by Shafayet

Fuzzy file finder with live preview

Security review

Review recommended · 3 findings

Deterministic scan — not a security guarantee

Low
Risk level
Low
Analyzed commit
3b5ec77
Scanned
4 weeks ago
  • Augments a command with octal/hex escape sequences.

    \x7fELF\x02\x01\x01\x00"), true, "NUL byte marks binary (ELF header)")
  • Augments a command with octal/hex escape sequences.

    \x44fake-frame-data")
  • Docs sudo README.md:31

    Command runs with sudo, elevating the process beyond the plugin environment.

    sudo pacman -S --needed fd fzf poppler ffmpeg trash-cli inotify-tools

Automated analysis only — not a security guarantee.

AI advisory review

Review recommended

Language-model assessment · ~deepseek/deepseek-v4-flash-latest — advisory only

High
AI risk level
High
Recommendation
review
Model
~deepseek/deepseek-v4-flash-latest
Analyzed commit
3b5ec77
Reviewed
4 weeks ago

This is a legitimate fuzzy file finder overlay; the deterministic scan's low-risk findings are benign (test fixtures and README docs). However, the scan missed a real command injection in Walks.js sortPipeSnippet(): file names are interpolated into a sh -c string, so a malicious file name can execute arbitrary commands when the Modified/Created tabs are used. The plugin should not be published until that shell quoting is fixed.

  • Command injection in script/Walks.js sortPipeSnippet(): xargs -I {} substitutes file names into a sh -c string without safe quoting; a file name containing double quotes or $(...) can execute arbitrary commands when the Modified/Created tabs are used.
  • The deterministic scan's 'obfuscation' findings are test fixtures (ELF header, fake frame data) in test/test-finder-model.js, not real obfuscation.
  • The README's sudo pacman -S line is documentation only; the plugin itself does not invoke sudo.
How this check works

This review combines the deterministic scan (the rule-based results above) with an independent look at the plugin's code by a language model. The model reads a trimmed sample of the repository's files, the manifest, and the README, then gives a plain-language risk level and a recommendation: install (no notable danger), review (look closer first), or avoid (clearly dangerous).

It runs on the same analyzed commit as the deterministic scan and is strictly advisory — it is not a security guarantee and never blocks a plugin by itself. A human moderator still reviews plugins before they are listed.

AI advisory only — automated analysis, not a security guarantee.

Install
$ omarchy plugin add https://github.com/shafayetejaman/shafayet.finder --enable
System #launcher

File Finder

Quick Fuzzy file finder overlay for the Omarchy shell with live previews: text heads, directory listings, images, PDF first pages, and video frame grabs.

<img width="1920" height="1080" alt="image" src="https://github.com/user-attachments/assets/5eb906ba-39a4-4371-a7b7-6e889ff32128" />

https://github.com/user-attachments/assets/5d73431c-67d4-43da-8133-672183f837f5

Install

omarchy plugin install https://github.com/shafayetejaman/shafayet.finder.git --enable

Remove

omarchy plugin remove shafayet.finder

Dependencies

Install everything in one line (--needed skips what's already present):

sudo pacman -S --needed fd fzf poppler ffmpeg trash-cli inotify-tools
Tool Preinstalled Needed for
fd Yes Search index and flag-mode queries
fzf Yes Fuzzy ranking of results
poppler (pdftoppm) No PDF page thumbnails
ffmpeg No Video frame previews
trash-cli No Delete-to-trash keybind
inotify-tools (inotifywait) Yes Event-driven index invalidation

Optional tools degrade gracefully: the finder works without them, minus that one feature. Without inotifywait the index simply falls back to interval rescans.

Usage

Open the finder from your configured launcher binding and start typing. With an empty query it browses a start directory instead of searching.

A chip row below the search box shows filter tabs: All (classic), Folder, Doc, Image, Music, Video, PDF, Modified (newest first), Created (newest first). Switching tabs clears the input. Non-All tabs use a recursive fd walk with injected type/extension flags; typed text fuzzy-filters the results in memory. Typing fd flags (e.g. --size +5mb) on a non-All tab is not allowed and shows Invalid! in the status bar. While a walk is in progress the status bar shows searching….

Typing / as the first character switches to the Folder tab immediately and strips the prefix, so /Documents searches for folders matching "Documents". On the Folder tab / is treated as normal search text.

Key Action
any printable character Type to filter
Tab / Shift+Tab Next / previous tab
Ctrl+L / Ctrl+H Next / previous tab
/ (first char) Switch to Folder tab
Esc Clear filter, then close
Ctrl+Backspace Delete last query word
Up / Down Move selection
Ctrl+J / Ctrl+K Move selection
PageUp / PageDown Move by 6 rows
Home / End First / last row
Enter Open with xdg-open
Shift+Enter Copy path
Alt+Enter Reveal in file manager
Delete / Ctrl+D Move selection to trash

Trash requires trash-cli.

Configuration

The plugin works with no configuration. To override anything, add an entry with this plugin's id to the plugins array in ~/.config/omarchy/shell.json:

{
  // ...
  "plugins": [
    {
      "id": "shafayet.finder",
      "search_dirs": ["$HOME", "/mnt/data"],
      "ignored_dirs": ["$HOME/.cache", "$HOME/go/pkg"],
      "ignored_names": ["target", ".venv", "dist"],
      "browse_dir": "$HOME/Documents",
      "max_scan_results": 100000,
      "max_display_rows": 50,
      "max_browse_rows": 200,
      "preview_byte_limit": 65536,
      "preview_cache_limit": 500,
      "pdf_cache_limit": 12,
      "preview_workers": 3,
      "debounce_ms": 25,
      "fd_debounce_ms": 1000,
      "rescan_interval_ms": 300000,
      "pdf_render_scale": 800,
      "show_hidden": false,
      "fd_flags": ["--ignore-vcs", "--follow"],
    },
  ],
}

Any key you omit keeps its default; unknown keys are ignored. Changes hot-reload when you save shell.json.

Snappy preset

{
  "id": "shafayet.finder",
  "debounce_ms": 25,
  "fd_debounce_ms": 200,
  "rescan_interval_ms": 300000,
}

Search results land ~30 ms after a keystroke, previews come from cache, and background rescanning is rare. Stale runs are killed on every keystroke, so aggressive values stay safe.

Settings

Key Type Default Description
search_dirs string[] ["$HOME"] Roots scanned into the index. $HOME and leading ~ expand.
ignored_dirs string[] [] Subtrees never indexed (native fd excludes, pruned before traversal).
ignored_names string[] [] Names skipped anywhere, merged with built-ins node_modules and __pycache__.
browse_dir string $HOME/Downloads Directory listed while the query is empty.
max_scan_results int 100000 Cap on indexed paths per scan.
max_display_rows int 50 Max rows shown for a query.
max_browse_rows int 200 Max rows shown in browse mode.
preview_byte_limit int 65536 Bytes of content loaded per preview.
preview_cache_limit int 500 In-memory preview LRU entries (per shell session).
pdf_cache_limit int 12 In-memory thumbnail LRU entries (PDF pages and video frames).
thumbnail_cache_limit int 500 On-disk thumbnails per kind; 0 disables persistence. See behavior notes.
preview_workers int 3 Concurrent preview processes, clamped to 1–3.
debounce_ms int 40 Delay from keystroke/selection to its search/preview launch.
fd_debounce_ms int 1000 Debounce for flag-mode queries, which walk real directory trees.
rescan_interval_ms int 300000 Minimum time between full rescans; 0 rescans on every open.
event_scan bool|"auto" "auto" Watcher-based index invalidation; "auto" enables it when inotifywait exists.
pdf_render_scale int 800 -scale-to for page thumbnails; also caps video frame width. Clamped 64–4000.
show_hidden bool false Include dot files in scans and directory previews.
fd_flags string[] (unset) Full override of the flags given to every fd call — see below.

fd_flags override semantics

Setting fd_flags replaces the entire flag set used for scanning:

"fd_flags": ["--ignore-vcs", "--type", "file", "--type", "directory", "--hidden", "--follow"]
  • --absolute-path is auto-appended if missing (the index stores absolute paths).
  • --color=never is auto-appended if missing: fd honors CLICOLOR_FORCE even when piped, and ANSI bytes would corrupt the index. Pass your own --color spelling to opt out.
  • Configured ignores stay enforced no matter what you set here.
  • Flags must make fd print paths; positionals are builder-owned.

Unset or empty falls back to the classic baseline:

"fd_flags": ["--type", "file", "--type", "directory", "--color=never", "--absolute-path"]

plus fd's own defaults: hidden entries skipped (unless show_hidden), VCS ignores respected (--ignore-vcs relaxes), symlinks not followed (--follow relaxes).

Inline fd flags in the search box

Any whitespace-separated token starting with - routes the query to a live fd walk over search_dirs instead of the fuzzy index. The first remaining text token is the fd pattern; the rest is staged text ranked fzf-style.

Query Behavior
invoice Classic fuzzy search over the index
--size +5mb invoice Size-filtered walk, pattern invoice
--size=+5mb report paid Attached values work; paid goes to fzf staging
-e pdf . Extension filter with match-all pattern
--ext pdf . Same — --ext aliases --extension
-e jpg -e png report Repeated extensions
-E node_modules report Exclude glob plus pattern
--size +5mb . Match-all scoped to the scanned roots
--size +5mb Flags only: nothing runs, list clears instantly
-- -weird Everything after -- is literal text

Notes:

  • Size constraints put the sign first: --size +5mb, --size -1gb, --size +2mb -10mb. fd rejects trailing forms like --size 2mb+, which then shows an empty list.
  • Every value flag consumes exactly one following token (--size/-S, --type/-t, --max-depth/-d, --min-depth, --changed-within, --changed-before, --max-results, --extension/--ext/-e, --exclude/-E). Repeat -e/-E for multiple extensions.
  • Unknown flags pass through verbatim; a typo'd flag shows an empty list.
  • Editing the staged text refilters instantly in memory — no second walk, no debounce. Changing flags or pattern re-walks after fd_debounce_ms.
  • Execution flags (-x, --exec, -X, --exec-batch) never reach fd; typing them demotes them — and everything after — to literal search text.
  • Policy ignores always apply, and flag mode reads the live disk, so it works even while the index is still scanning.

Behavior notes

  • The index lives at ~/.local/state/omarchy/file-finder-list.txt, loads at shell start so first searches are instant, refreshes in the background, and is rewritten only when its content actually changed. Deleting it is always safe: the next shell start rebuilds it, and if it vanishes mid-session the finder restores its in-memory copy on the next open so searches keep working.
  • Each rescan is one relay-wrapped fd walk over every live root; dead roots are skipped automatically, and the walk reports its live/total root ratio so a partially dead scan (e.g. an unmounted HDD shrinking the index to $HOME-only) is discarded instead of clobbering a good index — then retried automatically every 10 s for up to ~4 minutes, so a boot-time mount race heals itself. Nested or overlapping roots collapse to the outermost one, so every path indexes exactly once.
  • ignored_dirs translates to cross-root **/<suffix> excludes; a root listed there drops out entirely.
  • With inotifywait available (part of Omarchy's base packages), a resident recursive watcher makes rescans event-driven: kernel events over the search roots — mirroring the scan policy's excludes, so churn inside never-indexed trees cannot trigger anything — are coalesced through a 2 s quiet window into a dirty flag, and the next open runs exactly one walk to satisfy it. A static file structure therefore costs zero scans, and changes made while the finder is closed cost nothing but that flag. The watcher itself sleeps in the kernel between events (no idle CPU); crashes retry three times, and watch-limit exhaustion or three failures fall back silently to interval scanning until the next shell start. Changes from before shell start are covered by the startup walk either way.
  • Previews cache in memory for the whole session, keyed by path, so revisiting a file re-shows its preview instantly. Renders larger than 3 MB of PNG are refused ("Thumbnail too large") rather than loaded. Files with nothing to render — unreadable files, binary or empty files, broken images — show an "Unable to preview" placeholder instead of a blank pane; binary files (a sampled head containing a NUL byte, e.g. MP3 ID3 padding) keep their full size · date · TYPE caption.
  • Successful thumbnails persist on disk under ~/.cache/thumbnails/shafayet.finder/{pdf,video}/, named by md5("<path>|<size>|<mtime>|<inode>") so an edited file never gets a stale hit — the first look of a session skips rendering entirely. Oldest entries are pruned past thumbnail_cache_limit.
  • Warm staged-text refiltering is incremental: extending the query rescores only the previous match set, keeping keystrokes in the low ms even on six-figure baselines.
  • Video previews grab a frame at 1 s (falling back to the first frame for short clips) using the same scale cap and thumbnail store as PDFs.
  • Stale work stops eagerly: every keystroke kills superseded search, browse, and preview processes instead of letting them run invisibly.

Files

  • Finder.qml — overlay UI, process lifecycle, preview worker pool
  • script/ — purpose-split JS libraries (QML .import layering, node-testable):
    • Core.js — quoting, setting primitives, path/type utilities
    • Fuzzy.js — client-side scoring/filtering
    • FdQuery.js — fd flag sanitizing and search-box query parsing
    • Walks.js — root guarding, excludes, relays, scan/browse commands
    • Search.js — fzf filter command, live flag-mode walks, run identity, tab-flag validation
    • Settings.js — defaults and shell.json cfg resolution
    • Preview.js — preview/thumbnail producers, failure memo constants
  • ShortcutHelp.qml — Ctrl+Shift+/ keyboard-shortcuts modal
  • FinderTabs.qml — filter-tab chip row
  • ResultRow.qml — result-list row delegate
  • PreviewPane.qml — right-hand preview pane (text/image/thumbnail)
  • FinderHeader.qml — filter echo + entry-count/scanning/searching/invalid status
  • EmptyState.qml — no-results / scanning overlay

License

MIT