Skip to main content
skill Claude Code CX Codex Cursor GitHub Copilot

qmd — Claude Code skill: what it does, how to install

Search local Markdown notes, docs, and wikis with QMD, then answer from retrieved files. Useful when project knowledge already lives on disk.

Install into Claude Code · Skills CLI (Vercel) All methods and platforms ↓
npx skills add tobi/qmd --skill qmd -a claude-code
Open on GitHub

What qmd does

This skill searches local Markdown collections such as notes, documentation, wikis, transcripts, and project knowledge bases. The agent first finds candidate documents, then retrieves their contents with qmd get or qmd multi-get before answering from the retrieved text with paths or docids.

Exact terms and rare phrases go through qmd search. Conceptual requests use structured qmd query fields such as intent:, lex:, vec:, and hyde:. A file can be read by line range with a path suffix or the --from and -l flags. You need the qmd CLI or an MCP server first; otherwise the skill has nothing to search with.

The skill supports Claude Code, Codex, Cursor, GitHub Copilot, Gemini CLI, and OpenCode. For Claude Code, installation places the skill at ~/.claude/skills/qmd/SKILL.md.

What it helps with

  • find notes by exact terms or titles
  • retrieve documents for an indirect question
  • gather context from several Markdown files
  • read a selected line range from a document
  • answer from a local knowledge base with source references

Example prompts for the agent

  • Find local notes about `merchant reality` and return the matching documents with their paths.
  • Use `qmd query` with structured fields to find material about metrics and OKRs, then retrieve the full text of the results.
  • Compare `concepts/{ai-before-headcount.md,data-informed-not-metric-driven.md}` with `qmd multi-get` and answer from those files.

How to install qmd

1Skills CLI (Vercel)

npx skills add tobi/qmd --skill qmd -a claude-code

2Manually: put the files in place

~/.claude/skills/qmd/SKILL.md

Copy the whole skill folder; for one project use .claude/skills/qmd/

From the same set2

The whole set →

Similar

memory-merger official github Moves mature notes from a domain memory file into its instruction file. Useful when project guidance grows through reviewed lessons. GitHub Copilot 12,846installs write-agentmemory-skill rohitg00 Sets structure and formatting rules for agentmemory skills. Use it when creating a new SKILL.md or reviewing an existing skill for consistency. Claude Code CX Codex Cursor +1 10,700installs create-implementation-plan official github This skill creates a Markdown implementation plan for features, refactoring, package upgrades, or infrastructure work. Use it when an agent needs an explicit task breakdown. GitHub Copilot 12,558installs architecture-blueprint-generator official github Analyzes a codebase and creates an architecture blueprint with components, dependencies, and diagrams. Use it before extending a project or onboarding developers. GitHub Copilot 12,196installs architecture official anthropics Writes an Architecture Decision Record or reviews a system design: options, trade-offs, consequences. Useful when you have to pick between technologies. Claude Code CX Codex Cursor +1 6,700installs weekly-digests thedotmack Splits a claude-mem timeline into per-ISO-week files and produces one narrative chapter per week. Use it when you want a serial story, not a single report. Claude Code CX Codex Cursor +1 4,000installs grill-with-docs Matt Pocock This skill interviews you about a plan or design and creates ADRs and a glossary along the way. Use it when decisions need sharper definition. Claude Code 1,219,756installs skill-creator official anthropics Creates and refines skills, then tests them with prompts, evaluations, and performance checks. Claude Code Claude.ai 414,180installs

Questions about qmd

How do I install qmd in Claude Code?

Run npx skills add tobi/qmd --skill qmd -a claude-code, run the same command once more, then check ~/.claude/skills/qmd/SKILL.md.

What does qmd do?

The skill searches local Markdown documents, retrieves full files with qmd get or qmd multi-get, and helps the agent answer with paths or docids.

What does qmd need to work?

It requires the qmd CLI or an MCP server. Structured qmd query is used for conceptual search, while qmd search is the fallback when model or GPU setup causes trouble.

Source text

SKILL.md · MIT
---
name: qmd
description: Search local markdown knowledge bases, notes, docs, and wikis with QMD. Use when users ask to find notes, retrieve documents, inspect a wiki, answer from indexed markdown, or set up QMD access.
license: MIT
compatibility: Requires qmd CLI or MCP server. Install via `npm install -g @tobilu/qmd`.
metadata:
  author: tobi
  version: "2.3.0"
allowed-tools: Bash(qmd:*), mcp__qmd__*
---

# QMD - Query Markdown Documents

## How search works

QMD searches local markdown collections: notes, docs, wikis, transcripts, and
project knowledge bases. Use it before web search when the answer may already be
in indexed local files.

The workflow is always:

1. Search for candidate documents.
2. Retrieve the full source with `qmd get` or `qmd multi-get`.
3. Answer from retrieved text, citing paths or docids.

Do not answer from snippets alone when the user needs facts, decisions, quotes,
or nuance. Snippets are only leads.

Typical loop:

```bash
qmd search "merchant reality support interviews" -n 5
# leads: #abc123 concepts/customer-proximity.md; #def432 sources/merchant-call.md
qmd multi-get "#abc123,#def432" --format md
```

**Default to structured `qmd query` with `intent:`, `lex:`, `vec:`, and `hyde:`
fields that you write yourself.** You are a better query expander than the
built-in model: you know the user's actual goal, the domain vocabulary, and the
nearby-but-wrong concepts to avoid. Do not just paste the user's words into
`qmd query "..."` and hope the expansion model guesses right — supply the
`intent:` and craft the lexical and semantic terms deliberately (see
[Pick the right search mode](#pick-the-right-search-mode)).

When reporting what you retrieved, a compact note is enough; do not paste whole
files unless needed:

```text
Retrieved:
- #abc123 concepts/customer-proximity.md
- #def432 sources/merchant-call.md
```

## Pick the right search mode

Use **BM25 lexical search** when you know exact words, titles, names, code
symbols, or rare phrases:

```bash
qmd search "cockpit OKR Goodhart" -n 10
qmd search '"AI Before Headcount"' -c concepts -n 5
```

Use **`qmd query` with structured fields** when the user describes an idea
indirectly, uses different wording than the source, or needs conceptual recall.
**This is the default mode — write the fields yourself rather than leaning on
query expansion.** Combine exact anchors with semantic recall:

```bash
qmd query $'intent: Find the concept note about metrics as instruments without letting OKRs replace judgment.\nlex: cockpit instruments OKR Goodhart metrics judgment\nvec: data informed not metric driven product judgment\nhyde: A concept note says metrics are useful like cockpit instruments, but leaders should remain data-informed rather than metric-driven because OKRs and dashboards can Goodhart product judgment.'
```

Structured query fields (you author each one — do not delegate this to the
expansion model):

- `intent:` states what you are trying to find **and what to avoid**. Always
  supply this. It steers ranking away from nearby-but-wrong concepts.
- `lex:` exact terms, aliases, titles, code symbols, and rare words you expect
  in the source. This is your own keyword expansion.
- `vec:` paraphrases the idea in natural language, in source-like wording.
- `hyde:` describes the document or answer that would satisfy the request.

You do not need all four every time, but you should almost always write at least
`intent:` plus one of `lex:`/`vec:`. A bare `qmd query "the user's sentence"`
throws away the context only you have and relies on the built-in expander to
reconstruct it — prefer the structured form.

If you genuinely have nothing to expand (a single rare token, a verbatim phrase),
that is a job for `qmd search`, not bare `qmd query`:

```bash
qmd query --format json --explain $'intent: ...\nlex: ...\nvec: ...'  # inspect ranking
```

If `qmd query` is slow or model/GPU setup fails, fall back to `qmd search` with
better lexical terms.

## Retrieve sources

Search results include docids like `#abc123` and `qmd://...` paths. Fetch them:

```bash
qmd get "#abc123"
qmd get qmd://concepts/ai-before-headcount.md
qmd multi-get "#abc123,#def432" --format md
qmd multi-get 'concepts/{ai-before-headcount.md,data-informed-not-metric-driven.md}' --format md
qmd multi-get 'sources/podcast-2025-*.md' -l 80
```

Use `multi-get` when comparing several hits or gathering context across pages.

### Output is line-numbered and carries the docid — cite both

`get` and `multi-get` are **line-numbered by default** and always print the
document's `#docid` and `qmd://` path. So `get` output looks like:

```text
qmd://concepts/note.md  #abc123
---

1: # Metrics as instruments
2:
3: Treat dashboards like cockpit instruments...
```

Cite the docid and exact line numbers in your answer, and use the numbers to ask
for the next slice. Pass `--no-line-numbers` only when you need raw content to
copy verbatim (e.g. reproducing a code block).

When you need to open or edit the underlying file (e.g. hand a path to `Read`,
`Edit`, or an editor), add `--full-path`. It replaces the `qmd://` URL + docid
header with the document's on-disk path, falling back to the canonical header if
the file no longer exists on disk:

```text
$ qmd get "#abc123" --full-path
/Users/you/notes/concepts/note.md
---

1: # Metrics as instruments
```

`--full-path` works the same way on `qmd search` and `qmd query`: result paths
become the file's on-disk path — `./`-prefixed relative path when the file is
inside `$PWD`, absolute realpath otherwise — and the per-result `#docid` is
dropped because the path is the identifier. The leading `./` is intentional so
the output is unambiguously a filesystem path and cannot be mistaken for a bare
collection-relative string. Default search/query output still uses `qmd://`
URIs; only opt into `--full-path` when you specifically need a path you can hand
to a non-QMD tool.

### Read line ranges with the `:from:count` suffix — never pipe through `sed`/`head`/`tail`

`qmd get` slices files itself. Use the suffix or flags; do **not** shell out to
`sed -n`, `head`, `tail`, or `awk` to pull a line range. Piping defeats docid
resolution, virtual-path lookups, line numbering, and the header, and it is
slower and more error-prone.

The most compact form is a `:from:count` suffix right on the path or docid —
prefer it:

```bash
qmd get "#abc123:120:40"                  # 40 lines starting at line 120
qmd get qmd://concepts/note.md:200:60     # lines 200–259
qmd get "#abc123:120"                      # from line 120 to end of file
qmd get "#abc123" --from 120 -l 40         # equivalent, using flags
```

Suffix and flags:

- `<path>:<from>:<count>` — start at line `<from>`, read `<count>` lines. **Best
  for reading around a search hit.**
- `<path>:<from>` — start at `<from>`, read to end of file.
- `--from <line>` / `-l <lines>` — flag equivalents. Explicit flags override the
  suffix, so `... :5:2 -l 1` reads 1 line.
- `--no-line-numbers` — drop the `N:` prefixes (line numbers are on by default).

Wrong: `qmd get "#abc123" | sed -n '120,160p'`
Right: `qmd get "#abc123:120:40"`

Search results include a `:line` anchor on each hit — feed it straight into
`qmd get path:line:<n>` to read a window around the match (line numbers in the
output will start at `line`).

## Discover what is indexed

```bash
qmd collection list
qmd ls
qmd status
```

Add collection filters when broad searches drift into the wrong corpus:

```bash
qmd search "headcount autonomous agents" -c concepts -n 10
qmd query "merchant support product reality" -c concepts -c sources -n 10
```

Omit `-c` to search everything.

## Filter by metadata

Documents can carry typed metadata in a `qmd.metadata` frontmatter block
(strings, numbers, booleans, or flat arrays). `search`, `vsearch`, and `query`
accept `--filter` with a recursive JSON AST, and every returned result satisfies
it. Add a metadata filter when it expresses a constraint the user intends, not
by default, and check coverage first: a condition on a key's value only sees
documents that declare that key, and discovery (below) shows how many do.

```bash
qmd search "authentication" --filter '{"field":"status","operator":"eq","value":"published"}'
qmd query "dependency injection" --filter '{"operator":"and","operands":[{"field":"topics","operator":"all","value":["typescript"]},{"field":"status","operator":"nin","value":["draft","archived"]}]}'
```

Nodes are discriminated by `operator`: groups `and`/`or` take `operands`, `not`
takes one `operand`, and conditions take `field` + `value` with operators
`eq`/`ne`/`gt`/`gte`/`lt`/`lte` (comparison), `in`/`nin`/`all` (membership),
`contains`/`prefix`/`suffix` (text), `type` (the value is a `string`, `number`,
or `boolean`), or `exists` (presence). Matching is typed and exact; missing keys
do not match `ne`/`nin` (add an `exists: false` branch in an `or` group to
include them). Conditions with a string value may add `"caseInsensitive": true`,
which folds ASCII letters. The MCP `query` tool accepts the same AST as a
`filter` object. JSON output includes each result's `metadata`.

## Discover metadata before filtering

Do not guess keys or values. `qmd collection show <name>` lists the top keys
with types and a value preview, and `qmd collection metadata` drills in. Same
metadata, different unit: `--filter` narrows documents (which are counted),
`--match` narrows the metadata itself (which entries are reported). Both take
the predicate AST above. In a match, a condition's `field` is `"key"` (the
entry's key name) or `"value"` (its value), and every operator applies except
`exists` and `all`:

```bash
qmd collection metadata notes                                                                # top keys, top values
qmd collection metadata notes --match '{"field":"key","operator":"eq","value":"topics"}'       # one key in depth
qmd collection metadata notes --match '{"field":"value","operator":"eq","value":"docs-team"}'  # which keys hold this value
qmd collection metadata notes --match '{"operator":"and","operands":[{"field":"key","operator":"eq","value":"priority"},{"field":"value","operator":"gte","value":3}]}'
qmd collection metadata notes --match '{"field":"key","operator":"eq","value":"topics"}' --filter '{"field":"status","operator":"eq","value":"published"}'
```

The last form shows what a filter leaves behind before you commit to it in a
query. Read the output like this:

- **The header is the decision.** `topics  string[]  388 of 480 documents
  1,204 distinct` gives coverage and cardinality. With `--filter`, a
  `filter: 312 of 480 documents` line opens the output and every count is out
  of those 312. Counts are documents, not values.
- **Quotes mark a string that reads like something else.** `"42"` is a string,
  `42` a number, `""` is empty. Paste a quoted value into a filter as the JSON
  string it is.
- **Footers mean windowed.** `N more values` or `N more keys` means the list
  was cut. Page with `--value-offset`/`--key-offset` or raise
  `--value-limit`/`--key-limit` rather than assuming the rest.
- **Numbers print `min`, `median`, `max`**, enough to write a `gt`/`lt`
  threshold in one call.
- **`number | string` means documents disagree on type.** Each type reports its
  own document count. Filter by the type that covers the documents you want.

Every value shown can be matched with `eq` under the same collection scope.

Over MCP, call the `metadata` tool (same options, `collections` as an array)
and read `totalKeys`, `remainingKeys`, `remainingValues`, and `range` from the
structured result. Page keys with `keyOffset` and values with `valueOffset`.
The `status` tool lists each collection's most covered keys, so check it first.

## MCP Tool: `query`

When using the MCP server, prefer structured searches:

```json
{
  "searches": [
    { "type": "lex", "query": "cockpit OKR Goodhart" },
    { "type": "vec", "query": "data informed not metric driven product judgment" },
    { "type": "hyde", "query": "A concept note explains that metrics are useful as instruments, but leaders should not let OKRs or dashboards replace judgment." }
  ],
  "intent": "Find the concept note about using metrics as instruments without becoming metric-driven.",
  "collections": ["concepts"],
  "limit": 10
}
```

Query types:

- `lex` — BM25 keyword search. Best for exact terms, names, titles, and code.
- `vec` — vector semantic search. Best for natural-language concepts.
- `hyde` — vector search using a hypothetical answer/document passage.

## Query craft

Good QMD searches mix three things:

1. **Title/alias anchors:** exact page titles, named entities, phrases.
2. **Semantic paraphrase:** how a human would describe the idea.
3. **Negative space:** enough intent to avoid nearby-but-wrong concepts.

Examples:

```bash
# Exact-ish title lookup
qmd search '"arm the rebels" merchants tools big companies' -c concepts

# Semantic concept lookup
qmd query $'intent: Find the customer proximity concept, not generic customer delight.\nlex: support pseudonymous merchant customer interviews\nvec: founder stays close to merchant reality through support and product use'

# Source lookup
qmd search "six-week cadence WhatsApp merchant relationships Shawn Ryan" -c sources -n 10
```

## Setup and maintenance

Only mutate indexes when the user asked for setup or maintenance. Searching and
retrieving are safe; collection/index mutation is not a casual first step.

```bash
npm install -g @tobilu/qmd
qmd collection add ~/notes --name notes
qmd update
qmd embed
```

Health and diagnostics:

```bash
qmd doctor
qmd status
qmd pull
```

`qmd doctor` checks config, model cache, device/GPU setup, vector fingerprints,
and common environment overrides. If a model-backed command fails, run it before
changing configuration.

## MCP setup

See `references/mcp-setup.md` for Claude Code, Claude Desktop, OpenClaw, and HTTP
server configuration.

## Pitfalls

- **Do not stop at snippets.** Fetch documents before making claims.
- **Do not slice files with `sed`/`head`/`tail`.** Use the `path:from:count`
  suffix (e.g. `qmd get "#abc123:120:40"`) or `--from`/`-l`. Output is already
  line-numbered; piping breaks docid resolution, the header, and virtual paths.
- **Do not lean on query expansion.** Write `intent:`/`lex:`/`vec:`/`hyde:`
  yourself. A bare `qmd query "user sentence"` discards the context only you
  have. You expand the query; the model just ranks.
- **Do not overuse semantic search.** If you know exact titles or terms, BM25 is
  faster and often better.
- **Do not filter blind.** Run `qmd collection metadata` first and read the
  coverage. A key declared by 388 of 480 documents leaves 92 that no value
  condition on it can reach; only `exists: false` selects them.
- **Do not mutate indexes casually.** `qmd collection add`, `qmd update`, and
  `qmd embed` change local state and can be expensive.
- **Model-backed commands can be environment-sensitive.** If `qmd query`,
  `qmd vsearch`, or reranking fails because local models/GPU are unavailable,
  use `qmd search` and stronger lexical/structured terms.
- **Ambiguous user wording needs intent.** Add `intent:` rather than hoping query
  expansion guesses the right domain.
- **Collection names matter.** Search `concepts` for synthesized wiki pages,
  `sources` for transcripts/raw source pages, and docs collections for code or
  project documentation.

© tobi · MIT · tobi/qmd

See also

Project sponsors

Companies that keep this analytics open
Gold sponsor

Impressions, clicks and code copies — open statistics. We keep the numbers public for the same reason NeBlask itself is public. Become a sponsor