Quick start¶
Two installs, and neither one needs the other. The VS Code extension renders
a card while you write it. The hmd command checks a whole tree of them and
publishes it. Take either, or both — the usual shape is the extension while
writing and the CLI in CI. A third, hql, is optional and a separate
project: a query language that runs over a tree of cards.
Neither one teaches the format. The Tutorial is that, and it is the whole language in one sitting. This page is only how to get the tools running.
1. The editor¶
Install from the VS Marketplace Open VSX
ext install hypermarkdown.hmd
Stock VS Code installs from the marketplace; Cursor, Windsurf, and VSCodium from
Open VSX. Then open any .hmd file and click the ⚡ at the top right of the
editor — source on the left, the rendered card on the right, keeping up as you
type, with embeds still visible as embeds and a link to nothing in red.
Nothing else has to be installed. The extension carries its own implementation of the format — parser, resolver, and renderer, in TypeScript, running inside the editor — so there is no interpreter to find and no subprocess between a keystroke and the preview. What it does, and what it does not do yet, is The VS Code extension.
2. The command line¶
pip install HyperMarkDown # or: uv pip install HyperMarkDown
Python 3.11 through 3.14. Point it at a tree of cards:
hmd lint --root path/to/your/wiki
glossary/index.hmd:11:3: warning[HMD001] [[idempotency]] does not resolve to a page
0 error(s), 1 warning(s)
A link to a page you have not written yet is a warning, because writing
forward is how a wiki grows; a malformed reference or a name that matches two
cards is an error. Exit codes are pinned for CI — 0 clean, 1
diagnostics, 2 usage error — with --strict to fail on warnings and
--format json for machine-readable output.
Four more commands round it out: hmd render flattens a card to markdown or
HTML with its embeds expanded, hmd graph dumps the resolved link graph, and
hmd info reports which root and discovery policy it settled on. To stop
passing --root, run hmd init at the top of your project:
hmd init # or: hmd init --wiki notes --name shared
That writes .hmd/config.toml with every setting at its default and creates the
namespace root it names. The directory doubles as the root marker, so any
subtree can be self-contained — hmd init inside one is how you make it so.
It also names the tree, in a [namespace] section, after the project directory
unless you say otherwise. A project's own namespace is otherwise unnamed, and
naming it is what would let another project bind to
it — reserved today, so the section records intent and changes nothing.
Nothing to try it on yet? The repository carries a small runnable wiki that exercises the spine walk, both import forms, folder notes, and most of the syntax. It lints with zero errors and exactly one deliberate warning — the red link above, showing what an unwritten page looks like:
git clone https://github.com/ewiger/hypermarkdown
hmd lint --root hypermarkdown/examples/cs-alg-sorting
3. Publish it, if you want a site¶
The site builder ships as an extra, and registers itself with MkDocs:
pip install "HyperMarkDown[mkdocs]"
# mkdocs.yml
docs_dir: doc
plugins:
- hypermarkdown:
root: doc/wiki # only this subtree is a namespace
exclude_docs: |
*.hmd
nav:
- Home: index.md
- Wiki:
- Overview: wiki/README.md
- hmd://wiki # ← the generated section lands here
mkdocs serve
Point docs_dir at a documentation tree and root at the part of it that is a
namespace: the rest is hand-ordered markdown, the cards are generated, and
hmd://wiki says where the generated section belongs in your nav. The site you
are reading is that build, from this project's own doc/ tree.
D2 diagrams are the one thing with an outside dependency. A d2 fence draws
when d2 is on your PATH or Docker is available, and
shows its own labelled source when neither is — a diagram never degrades to a
blank space.
4. Ask questions of it¶
HQL is a statically typed query and data transformation language. A
program is a pipeline: each step takes the previous step's value and returns a
new one. Steps include filter, map, sort, group, lexical and semantic
search, and graph traversal. Sources are records, lists, JSON, YAML, and a
vault of Markdown or HyperMarkDown files together with its link graph. HQL is
a separate project with its own repository, releases, and documentation.
A release binary for Linux and macOS is on the releases page, or build it from source with Rust 1.95 or newer:
cargo install hql --locked
hql eval '40 + 2' # 42
hql builtins # every pipeline step, grouped by extension
The query below ranks the vault's cards by semantic similarity to a string, keeps three, adds every card one link away, and prints the resulting graph as text:
import semantic
cards
| semantic("night hunting birds")
| take(3)
| expand(depth = 1)
| text
Inside a .hmd file, HQL runs as cells. A cell is a fence with the info
string hql#eval or hql#query. A fence marked plain hql is a listing:
shown as code, never run. id=<name> makes the cell's value available to other
cells; hidden evaluates the cell without printing its output. Cells run in
dependency order. hql render writes the document with each cell replaced by
its output:
```hql#eval id=books hidden
cards | filter(c => c.metadata.category == "book")
```
## Still to read
```hql#eval
books | filter(c => c.metadata.status == "todo") | map(c => c.title)
```
hql --vault path/to/your/wiki render reading-list.hmd
The vault is the same tree hmd lint checks. Documentation:
hql-lang.org — introduction, install, a twelve-chapter
book, and the reference.
Where to go next¶
- Tutorial — the whole language in one sitting, every construct with its source shown beside what it renders.
- The VS Code extension — what the preview gives you, and the gaps it still has.
- HQL — the query language's own site: introduction, install, the book, and the reference.
- Presentation — the trade-offs between rendering a card in an editor, converting it, and publishing a tree of them.
- Language Specification — the normative text, once you need the exact answer rather than the teaching one.