qmd Search
Search a local markdown knowledge base semantically with
. Five
modes — BM25 keywords, vector similarity, hybrid (expansion + rerank), literal native-script grep,
and a fused
— all running on-device. The key advantage over Obsidian's built-in search: it
matches
meaning, finds notes that share no words with the query, and works
across languages
(e.g. a Russian query retrieves English notes).
When to use which mode
- hybrid () — default. A real question or fuzzy intent ("how do I stop overengineering").
Best quality; first run downloads reranker/expansion models (~one-time slow).
- vector () — fast concept lookup ("notes about embodied computing").
- BM25 () — an exact keyword, name, or filename. Instant, no model.
- grep () — literal fixed-string ripgrep over the .md files. The audit path for
proper nouns, transliterations, exact phrases, Russian stems/inflections, and absence checks.
Bypasses the index; matches only the exact script/spelling you type.
Bilingual / proper-name rule (do not skip)
This vault is bilingual (English/Russian). The embedding model is decent for concepts but weak
for proper nouns / specific entities, and BM25 only matches the script you type. So:
Never conclude "it's not in the vault" after one English semantic query. For names, people,
pets, places, foreign terms, or bilingual topics:
- Search semantically first ( / ).
- Generate likely native-script spellings/stems and try them, e.g.
,
dog/pet → собак, пёс, щенок, питомц, животн
. Use stems (
catches ), not just the nominative.
- Run a literal pass before concluding absence:
qmd-search.sh -m grep -n 20 "Зигги"
.
- Use literal hits to disambiguate close names (e.g. the pet vs. Freud).
- If everything fails, say "I didn't find it with these queries: …" and list the terms tried —
not "it's not in the vault." Raise to ~20 for absence checks.
Primary usage — the wrapper
Use the bundled wrapper; it suppresses qmd's stderr spinner, formats results as
(parsing qmd's JSON, so commas in filenames are safe), and makes a best-effort refusal to run
during an active
(which would return empty results — override with
):
bash
~/.claude/skills/qmd-search/scripts/qmd-search.sh [-m query|search|vsearch|grep|find] [-n N] [-c COLLECTION] [--snippet] [--min-score X] [--json] [--full] <query...>
Examples:
bash
qmd-search.sh "what helps with anxiety" # hybrid (default)
qmd-search.sh -m vsearch -n 8 "behavioral health from photos"
qmd-search.sh -m search sensorium # BM25 keyword
qmd-search.sh -m grep -n 20 "Зигги" # literal native-spelling / absence check
qmd-search.sh -m find "Зигги собака" # fused: semantic + literal in one call
qmd-search.sh --snippet "agent orchestration" # rows + matching snippets
qmd-search.sh --min-score 0.5 "quarterly planning" # drop low-relevance hits
qmd-search.sh --json "agent orchestration" # structured output for further processing
After getting hits, read the top files directly (they are normal vault paths) or fetch slices with
qmd get "<path>:<line>" -l <N>
.
Setup / indexing (only if shows the vault is not indexed)
bash
qmd collection add ~/Brains/brain --name brain # index the vault
qmd context add qmd://brain "short description of the vault"
qmd embed # build vectors; re-run until status shows 0 pending
qmd cleanup # compact the index
Refresh after large edits:
. Check health any time with
.
Operational rules (do not skip)
- One embed at a time, and never search while embedding — both cause empty/garbage results.
The wrapper guards searches; for manual calls, check first.
- If embedding never reaches 0 pending, check disk space () — a full disk fails writes
silently. See
references/cli-reference.md
→ "Operational gotchas".
- Vector scores are modest (~0.4–0.6); judge by ranking, not the absolute number.
MCP (native tools) vs. the CLI wrapper
qmd ships an MCP server (
, stdio) exposing tools
,
,
,
.
If it's registered in the host (e.g.
),
prefer the native tool for hybrid
search — it returns structured results with no spinner/JSON-parsing/exit-code quirks. Register with:
json
{ "mcpServers": { "qmd": { "command": "qmd", "args": ["mcp"] } } }
Use the
wrapper (
) when you need what MCP doesn't cover: BM25-only
(
), vector-only (
), the literal/native-script
pass, the fused
mode,
, or
. The bilingual/proper-name rule above applies to both paths.
Quality / evals
evals/fixture.example.json
+
run
to score search quality
(precision/recall/MRR per backend). Baseline and interpretation:
. Re-run after
changing the wrapper, the index, or the embedding model; a drop vs. baseline is a regression.
Reference
Full command surface, query grammar (
/
/
), output formats, models, and recovery
steps are in
references/cli-reference.md
.