{"slug": "show-hn-anygrep-semantic-search-over-notes-pdfs-and-images-on-the-cpu", "title": "Show HN: anygrep – semantic search over notes, PDFs and images, on the CPU", "summary": "Anygrep, a new MIT-licensed terminal tool from developer kuroko1t, performs semantic search over notes, PDFs and images using Google's EmbeddingGemma 2 model converted to int8 ONNX and run entirely on the CPU, keeping a single local index. On a Ryzen 7 7700 with the default 8 threads, anygrep indexes text at about 1,100 tokens/s and processes an image in about 1.2 s, while a search command takes about 1.5 s, mostly spent loading the model. The tool requires Python 3.10+, downloads a roughly 350 MB model on first use (200 MB more for image indexing), and is installed via pipx or uv tool install.", "body_md": "anygrep searches your notes, PDFs and screenshots by meaning, from the terminal. It runs one model\n([EmbeddingGemma 2](https://huggingface.co/google/embeddinggemma-2), int8 ONNX) on the CPU and keeps one\nlocal index.\n\nThe demo searches a folder that was already indexed, and the wait for the model to load is sped up.\n\n```\npipx install anygrep        # or: uv tool install anygrep\nuvx anygrep \"when is the tax return due\" ~/notes   # or try it without installing\n```\n\nRequires Python 3.10+. Tested on Linux; CI also runs the tests on Windows and macOS (Apple silicon).\nThe model is downloaded on first use: about 350 MB, and 200 MB more once you index images. See\n[docs/usage.md](https://github.com/kuroko1t/anygrep/blob/main/docs/usage.md#installing) for Intel Macs, Windows on ARM and uninstalling.\n\n```\nanygrep \"wifi password reset\"                 # search here (new files are indexed first)\nanygrep \"when is the tax return due\" ~/notes  # search one folder\nanygrep --image whiteboard.jpg ~/Screenshots  # find similar pictures\nanygrep -l \"invoice from acme\"                # file names only; piped output is path:line:text\nanygrep -Q ~/notes                            # interactive: the model stays loaded\nanygrep index --background ~/Pictures         # big folder: index in a low-priority process\n```\n\nResults are up to 10 files, closest in meaning first (`-m N` for more), not every match; queries are not\nregular expressions. A file whose text or name contains a query of 3 or more characters as typed (an\ninvoice number, an error code) comes first, marked `(exact)`; words found in many files are not boosted\nthis way ([details](https://github.com/kuroko1t/anygrep/blob/main/docs/usage.md#exact-matches)). Queries and documents can be in different languages.\nWeak matches are hidden (`--weak` shows them) and near-duplicate files are shown once. See `anygrep -h`\nand [docs/usage.md](https://github.com/kuroko1t/anygrep/blob/main/docs/usage.md).\n\nOn a terminal, the first search in a big folder indexes the new files first, with a progress bar; Ctrl-C\nkeeps what is done and the next run continues. `index --background` does it while you work (your home\nfolder is never indexed automatically, so name a folder inside it). In a script or cron job, a search\nindexes new files only if that takes under about 30 seconds; run `anygrep index` beforehand for a big\nfolder ([details](https://github.com/kuroko1t/anygrep/blob/main/docs/usage.md#indexing-before-a-search)).\n\nIt reads text files (`.md .txt .html` and more), the text layer of PDFs, and `.jpg .png .webp .bmp .gif` images. Hidden, ignored and binary files are skipped.\n\n``` python\nimport anygrep\n\nwith anygrep.Index() as idx:  # the index the command uses\n    idx.update(\"~/notes\")  # index new and changed files\n    for hit in idx.search(\"when is the tax return due\", k=5):\n        print(hit.score, hit.path, hit.snippet)\n```\n\nInstall it with pip in your project's environment. It uses the same index, scores and filtering as the\ncommand and prints nothing. The API may change before 1.0; see [docs/python-api.md](https://github.com/kuroko1t/anygrep/blob/main/docs/python-api.md).\n\nOn a Ryzen 7 7700 with the default 8 threads, text indexes at about 1,100 tokens/s and an image takes\nabout 1.2 s. A search command takes about 1.5 s, mostly loading the model. More in\n[docs/benchmarks.md](https://github.com/kuroko1t/anygrep/blob/main/docs/benchmarks.md).\n\nSee also [how it works](https://github.com/kuroko1t/anygrep/blob/main/docs/how-it-works.md), [relevance](https://github.com/kuroko1t/anygrep/blob/main/docs/relevance.md) and\n[comparison with other tools](https://github.com/kuroko1t/anygrep/blob/main/docs/comparison.md).\n\n- No OCR: small text inside images is often missed, and scanned PDFs are skipped.\n- No audio, video or Office documents yet (anygrep says how many Office files it skipped), and no\nfile watching (run `anygrep index` from cron).\n\nMIT. The model is downloaded separately:\n[an int8 ONNX conversion](https://huggingface.co/onnx-community/embeddinggemma-2-ONNX) of Google's\nEmbeddingGemma 2, released under the Apache License 2.0 (its model card also points to the Gemma\nProhibited Use Policy). anygrep is not affiliated with Google.", "url": "https://wpnews.pro/news/show-hn-anygrep-semantic-search-over-notes-pdfs-and-images-on-the-cpu", "canonical_source": "https://github.com/kuroko1t/anygrep", "published_at": "2026-10-12 18:00:10+00:00", "updated_at": "2026-10-12 18:19:22.589272+00:00", "lang": "en", "topics": ["ai-tools", "ai-search", "artificial-intelligence", "machine-learning", "developer-tools"], "entities": ["anygrep", "kuroko1t", "EmbeddingGemma 2", "Google", "ONNX", "Hugging Face", "Python", "Ryzen 7 7700"], "also_reported_by": [], "alternates": {"html": "https://wpnews.pro/news/show-hn-anygrep-semantic-search-over-notes-pdfs-and-images-on-the-cpu", "markdown": "https://wpnews.pro/news/show-hn-anygrep-semantic-search-over-notes-pdfs-and-images-on-the-cpu.md", "text": "https://wpnews.pro/news/show-hn-anygrep-semantic-search-over-notes-pdfs-and-images-on-the-cpu.txt", "jsonld": "https://wpnews.pro/news/show-hn-anygrep-semantic-search-over-notes-pdfs-and-images-on-the-cpu.jsonld"}}