CEL-powered · 74 formats · CLI + MCP
Stop grepping by name. Ask what files are.
file-search-on walks a directory tree and returns files matching a CEL expression evaluated over each file's content-type-specific attributes — page counts, EXIF, audio tags, code symbols, binary entitlements — across 74 formats. One binary: a standalone CLI and a Claude Code MCP server.
↑ Click a family. MIT · pure Go, no CGO · part of the portfolio
the idea
Every file already knows what it is.
Name and mtime are the least interesting things about a file. file-search-on detects each file's content type — extension first, magic-byte fallback — extracts the attributes that type actually has, and lets you query them with the full Common Expression Language. A PDF has a page_count; a photo has iso and gps_lat; a Go file has functions and max_complexity; a Mach-O binary has entitlements. Ask across all of them in one expression.
file formats, extension-first with magic-byte fallback — a new format is one registration call.
MCP tools exposed to Claude Code — search, code-graph, duplicates, complexity, coupling & more.
source languages parsed for symbols & complexity — Go via stdlib AST, the rest via tree-sitter.
CGO dependencies. Pure Go — cross-compiles cleanly to six release targets, no decoders to link.
content-type families
Typed attributes, format by format.
Thirteen core families plus a long tail of specialised formats — each with its own metadata extractor and type predicates (is_pdf, is_raw_photo, is_variable_font…) that light up automatically.
📄 Documents & markup
PDF, EPUB, Markdown, HTML, XML — title, author, language, page_count, word_count, front-matter as top-level CEL vars.
🖼 Images & RAW
JPEG · PNG · HEIC · Canon/Nikon/Sony/Adobe RAW. EXIF camera, lens, gps_lat, iso, taken_at — plus Apple Live Photo pairing.
🎵 Audio
MP3 · M4A · FLAC · OGG · WAV — artist, album, year, genre, duration, bitrate, sample_rate, ReplayGain.
🎬 Video
MP4 · MOV · MKV · WebM · AVI — video_codec, video_width/height, frame_rate, HDR / colour-space, duration, subtitles.
💻 Source code — 28 languages
functions, imports, references, loc, max_complexity. Go via stdlib AST; 15+ more via embedded tree-sitter.
⚙ Compiled binaries
ELF · Mach-O · PE — architectures, is_stripped, code-signature codesign_team_id, and parsed entitlements.
🔭 Science data
FITS · VOTable · HDF5 · NASA PDS3/PDS4 · CDF — telescope, instrument, mission_name, ra/dec, exptime.
🗄 Databases
SQLite v3 + WAL/SHM + FTS3/4/5 body extraction — sqlite_page_count, sqlite_fts_table_names, schema version.
🔤 Fonts
TTF · OTF · TTC · WOFF/WOFF2 (brotli) — font_family, font_weight, variable-font font_axes, font_license, PANOSE.
🧊 3D models
STL · Wavefront OBJ · glTF 2.0 — vertex_count, face_count, has_normals, materials, bounding_box.
💬 Chat exports
Slack · Discord · Signal — detected by JSON shape, not name. chat_message_count, chat_participants, date range; --body greps the transcript.
📦 Archives, packages & more
ZIP/TAR, .deb/.rpm/.pkg, disk images, notebooks, email, plists, bookmarks, VM bytecode — all with typed attributes.
the query language
CEL — plus fuzzy, phonetic & geographic.
The full Common Expression Language — comparisons, &&/||, string functions, list membership, timestamp arithmetic — over structural attributes. Built-in levenshtein, soundex, ngram_similarity, and point_in_polygon make typos and "sounds-like" spellings no longer fatal.
# structural attributes is_pdf && page_count > 10 && author == "Jane Doe" is_image && gps_lat > 51.4 && gps_lat < 51.6 is_video && video_height >= 2160 is_markdown && "longread" in tags && word_count > 1000
# typo-tolerant / phonetic / geo is_audio && levenshtein(artist, "Radiohead") <= 2 is_image && soundex(camera_make) == soundex("Nikon") is_markdown && ngram_similarity(title, "k8s", 2) > 0.6 is_image && point_in_polygon(gps_lat, gps_lng, city)
Output as bare paths, default, verbose, NDJSON (json), or a Go text/template. validate compile-checks an expression for CI gates.
mcp server mode
Hand your files to Claude Code.
The same binary doubles as a Model Context Protocol server (stdio, HTTP, or SSE) exposing 43 tools. Ask in natural language — "find every PDF over 10 pages I haven't opened this year", "which Go files have the highest churn?", "who calls this function?" — and Claude drives the tools behind the scenes.
claude mcp add file-search-on -- file-search-on mcp
claude mcp add -s user file-search-on -- file-search-on mcp
Beyond search, the MCP surface includes a cross-file code graph:
- find_definition
- references
- who_calls
- calls
- call_path
- impact
- imported_by
- dead_code
- unused_exports
- complexity
- coupling
- circular
- test_gaps
- churn_owners
- find_duplicates
- search_semantic
install
One binary, six targets.
brew install richardwooding/tap/file-search-on
docker run --rm -v "$PWD:/work" ghcr.io/richardwooding/file-search-on:latest 'is_markdown && draft' -d /work
go install github.com/richardwooding/file-search-on/cmd/file-search-on@latest
file-search-on 'is_pdf && page_count > 10' -d ~/Documents
macOS / Linux / Windows on amd64 + arm64. Pre-built archives (with checksums.txt) on every release. Homebrew cask published to richardwooding/homebrew-tap.