npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

md-search

v0.4.0

Published

Semantic search for markdown files

Readme

md-search

Semantic search for markdown files. Creates a portable, git-committable vector index using an integrated local model by default or any OpenAI-compatible embedding API.

Why? Find relevant content in documentation, notes, or knowledge bases without exact keyword matching. Results work for both humans (readable output) and AI agents (JSON/JSONL).

Installation

Requires Bun runtime. Local embeddings and reranking run in-process with ONNX Runtime; no Ollama, LM Studio, Python, or separately installed model server is required.

# Run directly (no install)
bunx md-search search "your query"

# Install globally
bun add -g md-search

Bun may report blocked lifecycle scripts for transitive ONNX dependencies during installation. They are not required for md-search's supported CPU local-embedding path; no --trust step is needed for normal use.

Quick Start

# Index and search - no config file or API key needed
# First use downloads the pinned ~24 MB local model to ~/.cache/md-search/models.
md-search index ./docs
md-search search "how to configure logging"

Project-based setup with config file:

Create .md-search.yaml:

api:
  provider: openai
  base_url: http://localhost:11434/v1  # Ollama, or any OpenAI-compatible API
  model: nomic-embed-text
  dimensions: 768
sources:
  docs: ./docs

For a remote/OpenAI-compatible provider, configure it explicitly (this keeps existing remote setups supported):

api:
  provider: openai
  base_url: https://api.openai.com/v1
  model: text-embedding-3-small
  dimensions: 1536

Then set the API key in .md-search.local.yaml (gitignored):

api:
  api_key: sk-...

See CONFIGURATION.md for complete configuration reference including local model caching/offline use, environment variables, credential precedence, and provider-specific setup.

Usage

Index:

md-search index              # Index all sources
md-search index ./docs       # Index specific directory
md-search index --force      # Re-index everything

Search:

md-search search "how to configure logging"
md-search search -k 5 "authentication flow"    # Top 5 results
md-search search -k 50 -l 10 "database queries" # Search 50, return 10
md-search search -o json "api endpoints"       # JSON for agents
md-search search --rerank "complex question"   # Local cross-encoder reranking by default

Search and list filters accept one value per option and may be repeated:

md-search search --source docs --source notes --tag api "authentication"
md-search list --filter "status=published" --filter "type=guide"

For compatibility with compact multi-value usage, sources and tags also accept comma-delimited values:

md-search search --source docs,notes --tag api,reference "authentication"

Whitespace-separated values such as --source docs notes are no longer accepted. Use repeated options or a comma-delimited source/tag value so the required search query is never interpreted as a filter value. Frontmatter filters must be repeated; commas inside a filter value are preserved literally.

Output Formats

Text (default):

docs:guides/auth.md:45-62 0.847 # Authentication
Preview of matching content...

JSON: Full result objects for programmatic use (-o json)

JSONL: One result per line for piping (-o jsonl)

Advanced Configuration

Integrated local embeddings (default): no configuration is needed. The immutable Xenova/all-MiniLM-L6-v2@751bff37182d3f1213fa05d7196b954e230abad9 q8, 384D profile downloads once to $XDG_CACHE_HOME/md-search/models (or ~/.cache/md-search/models). It was accepted against the declared SciFact comparison thresholds: nDCG@10 0.6520 (91.84% of the compact-candidate best), Recall@10 0.7947 (95.29%), 65.81 warm CPU inputs/s, and a 23.69 MB cache. Those results cover whole scientific documents, not md-search Markdown chunks. To use an existing cache offline:

api:
  local:
    allow_download: false

The index records its provider/model/revision identity once in SQLite metadata. A legacy or different embedding space must be rebuilt with md-search index --force before new vectors are written.

Integrated local reranking: with the default/local embedding setup, --rerank needs no service or API key. md-search downloads the pinned q8 CPU cross-encoder Xenova/ms-marco-MiniLM-L-6-v2@a09144355adeed5f58c8ed011d209bf8ee5a1fec (about 23 MiB) to the same cache root. It scores one query/passage pair at a time, truncating combined input to 512 tokens, and is English-focused. Existing configurations using remote embeddings continue to inherit the remote reranker unless reranker.provider: local is explicit. The upstream cross-encoder model is Apache-2.0; the Transformers.js conversion supplies the ONNX weights. To use an existing cache offline:

reranker:
  provider: local
  local:
    # cache_dir: ~/.cache/md-search/models
    allow_download: false

Remote reranking remains supported for OpenAI-compatible APIs, LM Studio, Ollama-compatible servers, and Azure:

# .md-search.local.yaml
reranker:
  provider: openai
  base_url: https://api.openai.com/v1
  model: gpt-4o-mini
  api_key: sk-...

Ignore Patterns (exclude files from indexing):

# .md-search.yaml
ignore_patterns:
  - "_*"      # Drafts (default)
  - ".*"      # Hidden files (default)
  - "archive/*"

Azure OpenAI:

# .md-search.yaml
api:
  provider: azure
  base_url: https://my-resource.openai.azure.com/openai
  model: text-embedding-ada-002
  azure_deployment: text-embedding-ada-002
  azure_api_version: "2024-02-01"

Migrating from 0.1.x

A fresh installation defaults to the integrated local MiniLM model. Existing configs that explicitly set remote API settings retain OpenAI-compatible behavior. Rebuild a legacy or changed embedding index with md-search index --force.

Version 0.2.0 renames CLI flags for consistency:

| Old Flag | New Flag | Commands | |----------|----------|----------| | -f, --format | -o, --output | search, links, debug, status | | -F, --filter | -f, --filter | search |

The -f flag on the index command (for --force) is unchanged.