Search your notes by meaning, not keywords. Runs 100% offline.
You write a query like "what was the rollback strategy for the production migration?" — even if you can't remember which file it was in or what exact words you used — and semanticfs finds the right chunk, semantically.
Local-first. No cloud, no API keys, no telemetry after first setup. All embeddings and search happen on your machine.
SemanticFS is a working local-first CLI prototype for semantic file search.
It supports Markdown, text files, and text-based PDFs, with incremental indexing, local embeddings, ChromaDB vector search, and SQLite metadata storage.
- Indexes local
.md,.txt, and text-based.pdffiles - Splits documents into searchable chunks
- Generates embeddings locally using sentence-transformers
- Stores metadata in SQLite and vectors in ChromaDB
- Searches by semantic meaning instead of exact keywords
- Works offline after the first model download
Keyword search breaks the moment you forget the exact phrasing. You know you wrote something about the database migration incident — but the file says "schema change" and "reversal procedure", the filename is 2023-11-03.md, and grep finds nothing.
Semantic search encodes meaning into vectors. At query time, your question lands near the right content in that space regardless of word overlap. "rollback strategy" matches "reversal procedure". "agent observability" matches "trace collection and span analysis". The model understands intent, not literals.
Why local-first? Most semantic search tools send your documents to a cloud, require an API key, and charge per query. semanticfs runs the embedding model on your own CPU, stores everything under ~/.semanticfs/, and never makes a network request after the one-time model download. Your notes stay on your machine.
Your notes (~/notes, Obsidian vault, project docs, runbooks)
│
▼
semanticfs index ~/notes
│
├── Scanner — recursive file discovery, SHA-256 hash
├── Parser — .md / .txt / text-based .pdf text extraction
├── Chunker — fixed-size + overlap, word-boundary snap
└── Embedder — sentence-transformers (all-MiniLM-L6-v2, local)
│
├── SQLite ← folder / file / chunk metadata (~/.semanticfs/semanticfs.db)
└── ChromaDB ← vector embeddings (~/.semanticfs/chroma/)
│
▼
semanticfs search "database rollback strategy"
│
├── Embed query (local)
├── Cosine similarity over ChromaDB
└── Rank + format results
Privacy: document text never leaves your machine. The only network request is the one-time model download (~90 MB).
$ semanticfs search "database rollback strategy"
Results for "database rollback strategy" (2 hit(s))
╭─ #1 ~/notes/runbooks/postgres_migration.md (chunk 1) — score 0.87 ───────╮
│ Before applying schema changes in production, document a rollback │
│ strategy that includes backup verification and replay steps. │
│ │
│ Why matched: High semantic similarity (87%); Overlapping terms: strategy │
╰───────────────────────────────────────────────────────────────────────────╯
╭─ #2 ~/notes/incident_notes.md (chunk 0) — score 0.71 ────────────────────╮
│ Recovery procedure: restore from snapshot, validate FK constraints, │
│ replay audit log. Verified on staging before production apply. │
│ │
│ Why matched: Moderate semantic similarity (71%) │
╰───────────────────────────────────────────────────────────────────────────╯
Requires Python 3.10+.
python -m venv .venv
# Windows
.venv\Scripts\activate
# macOS / Linux
source .venv/bin/activate
pip install -e ".[dev]"semanticfs config show # create ~/.semanticfs/ and display settings
semanticfs index ~/notes # scan, chunk, embed, store (downloads model on first run)
semanticfs search "<query>" # semantic search over indexed chunks
semanticfs list # indexed folders, file/chunk counts, last index time
semanticfs remove <folder> --yes # drop folder from index (SQLite + Chroma)
semanticfs status # runtime paths, model cache statusTry it immediately with the bundled benchmark corpus:
semanticfs index benchmarks/corpus
semanticfs search "postgres migration"
semanticfs search "veritabanı migration geri alma" # Turkish query
semanticfs index benchmarks/corpus # second run: unchanged files are skippedsemanticfs search "your query" --limit 5 # cap result count
semanticfs search "your query" --min-score 0.5 # filter low-confidence hits--min-score filters out results below the given similarity threshold (0.0–1.0). The default (0.0) returns all ranked results; 0.4–0.5 is a reasonable baseline for high-precision mode.
Data lives under ~/.semanticfs/ (override with SEMANTICFS_HOME env var):
| Path | Purpose |
|---|---|
config.json |
embedding model, chunk size/overlap, result limits |
semanticfs.db |
SQLite metadata (folders, files, chunks, index runs) |
chroma/ |
ChromaDB vector store |
logs/ |
Exported reports (chunk quality, benchmark) |
semanticfs config show # all settings + paths
semanticfs config path # path to config.json- Run
semanticfs index <folder>once while online — downloadsall-MiniLM-L6-v2(~90 MB) into the HuggingFace cache. - After that, both
indexandsearchwork without network access. semanticfs statusshowsmodel cached: yeswhen the model is ready.
To force offline mode explicitly:
# Windows (PowerShell)
$env:HF_HUB_OFFLINE = "1"
$env:TRANSFORMERS_OFFLINE = "1"
# macOS / Linux
export HF_HUB_OFFLINE=1
export TRANSFORMERS_OFFLINE=1See docs/offline-validation.md for the full checklist.
semanticfs benchmark run --export # chunk gold regression
semanticfs benchmark run --verbose # show all rule results
semanticfs inspect-chunks <folder> # heuristic chunk quality report
semanticfs inspect-chunks <folder> --exportSee docs/benchmark.md.
| Item | Status |
|---|---|
.md and .txt indexing |
✅ Full support |
Text-based .pdf indexing |
✅ Supported (PyMuPDF) |
| Scanned / image PDF | ❌ Not supported — ParserError is raised, file is skipped |
Code files (.py, .js, .ts) |
❌ Not indexed |
| Realtime file watcher | ❌ Run index again to pick up changes |
| Multi-user / cloud sync | ❌ Single-user, local only |
| LLM-generated explanations | ❌ Rule-based why matched only |
Re-indexing is incremental: files with unchanged content (SHA-256 hash match) are skipped automatically.
"No indexed chunks" error on search:
Run semanticfs index <folder> first. The vector store must be populated before searching.
PDF files show as Failed in index summary:
Check semanticfs status — if it's a scanned PDF, text extraction is not possible.
Text-based PDFs (where you can copy-paste text in a viewer) work correctly.
Results seem off or missing:
Try a more descriptive query — the model works best with full phrases rather than single keywords.
Use --min-score 0.0 to see all ranked results without filtering.
Model download is slow:
The first semanticfs index downloads ~90 MB. After that, all runs are fully offline.
See docs/architecture.md.
From the repo root, install the package in your active environment (once):
pip install -e ".[dev]"Default pytest excludes @pytest.mark.integration tests (no embedding model required). Prefer python -m pytest so it uses the same interpreter as pip install:
python -m pytest tests/ -vIntegration tests need a cached model (semanticfs index benchmarks/corpus once online):
# bash / macOS / Linux
SEMANTICFS_OFFLINE_TEST=1 pytest -o addopts= -m integration tests/test_offline.py -v
SEMANTICFS_SEARCH_GOLD_TEST=1 pytest -o addopts= -m integration tests/test_search_gold.py -v# Windows PowerShell
$env:SEMANTICFS_OFFLINE_TEST = "1"
python -m pytest -o addopts= -m integration tests/test_offline.py -v
$env:SEMANTICFS_SEARCH_GOLD_TEST = "1"
python -m pytest -o addopts= -m integration tests/test_search_gold.py -vSearch-only (PowerShell): set $env:SEMANTICFS_SEARCH_GOLD_TEST = "1" then pytest -o addopts= -m search_gold tests/test_search_gold.py -v
See docs/offline-validation.md for manual offline checks.
