No description
  • JavaScript 99.4%
  • CSS 0.6%
Find a file
2026-09-22 13:43:57 +02:00
.vscode Initial commit: zettelmesh VS Code extension 2026-09-05 20:42:14 +00:00
docs Merge origin/main into main 2026-09-21 16:26:38 +02:00
media feat: See also / Referenced by sections in the Markdown preview 2026-09-21 15:34:56 +02:00
src Merge origin/main into main 2026-09-21 16:26:38 +02:00
.gitignore Initial commit: zettelmesh VS Code extension 2026-09-05 20:42:14 +00:00
.vscodeignore Initial commit: zettelmesh VS Code extension 2026-09-05 20:42:14 +00:00
CHANGELOG.md feat: See also / Referenced by sections in the Markdown preview 2026-09-21 15:34:56 +02:00
jsconfig.json Initial commit: zettelmesh VS Code extension 2026-09-05 20:42:14 +00:00
LICENSE Initial commit: zettelmesh VS Code extension 2026-09-05 20:42:14 +00:00
package-lock.json Initial commit: zettelmesh VS Code extension 2026-09-05 20:42:14 +00:00
package.json Merge origin/main into main 2026-09-21 16:26:38 +02:00
README.md Merge branch 'feature/unified-toml-configuration' 2026-09-22 13:43:57 +02:00

zettelmesh for VS Code

Configure a zettelmesh workspace from VS Code settings, and run its two editor integrations:

  • Language Server (zettel serve lsp) — title-first link completion (both links: frontmatter and inline [phrase](zettel:…) body links), hover, go-to-definition, backlinks ("Find All References"), workspace symbols (Ctrl/Cmd+T finds notes by title), document outline / breadcrumb (shows the note title even for a ULID-named file), titled inlay hints for link staleness, frontmatter diagnostics, and a "Create note" quick fix for dangling links. Works on Markdown files.
  • MCP server (zettel serve mcp) — exposed to VS Code's Model Context Protocol integration so chat/agents can search the mesh, read notes, and follow links. Off by default.

The extension does not bundle zettel; it drives the CLI you already have installed.

Prerequisites

  • VS Code 1.101 or newer (for the MCP integration).

  • The zettel CLI on your PATH. It is not on PyPI — install it from the zettelmesh repo, e.g.

    uv tool install "zettel[openai,tui] @ git+ssh://git@code.hans-aschauer.de:222/hans/zettelmesh.git"
    uv tool update-shell
    

    If it is not on PATH, set zettelmesh.zettelPath to an absolute path or a wrapper script — e.g. one containing exec uv run --project /path/to/zettelmesh zettel "$@", which is the usual answer when you work from a zettelmesh checkout.

  • A configured zettel installation. The extension uses the CLI's own zettel.toml, optional sibling zettel_secrets.toml, and environment variable precedence. It does not require a VS Code-specific workspace layout.

    Creating one, and everything else about setting zettel up, is covered in docs/getting-started.md in the zettelmesh repo. There is also a ready-made example workspace you can clone to try things out; run its setup.sh once to render config/registry.yaml.

Installing

The extension is not on the Marketplace — build the .vsix from this checkout:

git clone ssh://git@code.hans-aschauer.de:222/hans/zettelmesh-vscode.git
cd zettelmesh-vscode
npm install
npm run package                                   # -> zettelmesh-vscode-0.1.0.vsix
code --install-extension zettelmesh-vscode-0.1.0.vsix

There is no build step — the extension is plain CommonJS JavaScript in src/, so vsce package just bundles it.

The CLI configuration is used automatically. To select a non-default file, set its path explicitly:

{
  "zettelmesh.configFile": "~/my-workspace/zettel.toml",
  "zettelmesh.mcp.enabled": true      // optional; off by default
}

Reload the window. zettelmesh: Show Output is the first place to look if the language server does not come up — it logs the exact command line and environment it used.

Settings

All settings live under zettelmesh. (Settings UI → search "zettelmesh").

Setting Purpose
zettelmesh.configFile Optional primary zettel.toml; otherwise the CLI default or ZETTEL_CONFIG is used.
zettelmesh.secretsFile Optional zettel_secrets.toml; otherwise the CLI selects the sibling file or ZETTEL_SECRETS_FILE.
zettelmesh.zettelPath The zettel executable. Default zettel.
zettelmesh.lsp.enabled Run the language server. Default true.
zettelmesh.mcp.enabled Offer the MCP server to VS Code. Default false.
zettelmesh.author Author name offered by New Note.
zettelmesh.newNote.defaultOrgFields name=value defaults for a new note's organizational fields.

All registry, clone, index, embedding, chat, audit, and secret settings are read by the CLI. The extension obtains the effective paths by calling zettel config show --json, so its behavior stays aligned with the CLI as configuration evolves. API keys never pass through VS Code settings.

Changing any setting restarts the language server and refreshes the MCP entry.

Notes view

The zettelmesh activity-bar container has a Notes tree: notes grouped by repo → first organizational field (department in the default schema), shown by title, click to open. It reads the same in-memory cache of zettel list --json as Go to Note, and refreshes on activation, after a sync, and on a settings change. Right- click a note for Copy Note ID.

The search button in the Notes title bar asks for a query, runs zettel search (semantic + keyword, up to 50 hits) and replaces the tree with the ranked hits; the clear button or an empty query brings the full tree back. A search can take a few seconds because it calls the embedding provider.

Hidden ids

Note ids (repo:ULID) mean nothing to a reader, so the editor hides them (zettelmesh.display.hideIds, on by default): a links: entry shows the target's title (the language server's inlay hint) and its #type, and [text](zettel:...) shows as an underlined [text]. Nothing in the file changes. Put the cursor (or a selection) on the line to see the raw text again. Links to notes you cannot read are left visible. Hover a hidden link for the note card, with the id last. Right-click in a note for Copy Note ID / Copy Note Link (the link under the cursor, else the note itself); the Notes and Links views have the same commands on each row.

Markdown preview

In VS Code's built-in Markdown preview, [text](zettel:...) links are clickable and open the note. The tooltip is the note card; an empty link text (or the raw url) shows the target's title; links to notes you cannot read are muted and do nothing. Under each note the preview adds two sections, like mkdocs: See also (the note's own links:, as type — title) and Referenced by (notes that link here, with the link text they used in quotes). The preview redraws when the note cache or link graph changes.

Under Notes in the same container, Links shows the note you are looking at: Outgoing (its own links: and inline zettel: links) and Backlinks (notes that link here), each row with link type, repository and frontmatter/inline. Click to open. It keeps the last note when you focus a non-Markdown file. Data comes from one zettel graph load per refresh, so links to notes you cannot read are absent, and results are as of the last sync.

Commands

  • zettelmesh: Go to Note — quick pick over note titles; opens the file. Backed by an in-memory cache of zettel list --json, refreshed on activation, after a sync, and when settings change.
  • zettelmesh: New Note — guided input: repository → title → author → one value per organizational field the repository declares → tags, then runs zettel new and opens the file. The note is not indexed until it is committed and synced. Defaults come from zettelmesh.author and zettelmesh.newNote.defaultOrgFields (name=value entries).
  • zettelmesh: Search (Ctrl/Cmd+K Z) — live quick pick over zettel search (hybrid semantic + keyword when embeddings are configured, keyword-only otherwise). Each pause in typing runs one query; Enter opens the note at the matched passage.
  • zettelmesh: Search Mesh for Selection — same, seeded from the editor selection (or the word under the cursor). Also on the editor right-click menu.
  • zettelmesh: Sync — runs zettel sync with a cancelable progress notification (the language server never syncs on its own).
  • zettelmesh: Refresh Notes — re-run zettel list and redraw the Notes view.
  • zettelmesh: Restart Language Server
  • zettelmesh: Show Output — the zettelmesh output channel (start command lines, errors).

Inline note links use the zettel: URI scheme in ordinary Markdown text:

This builds on [hybrid search](zettel:)

After typing zettel:, trigger completion or type the next character. The completion list uses note titles as labels and inserts the canonical repo:ULID destination. Existing partial destinations are replaced cleanly:

This builds on [hybrid search](zettel:handbook:01J...)

This is a separate, phrase-level relation from the note-level links: frontmatter list. Both forms are supported by the same language server, with hover, go-to-definition, diagnostics, and backlink support for inline links.

How it maps to the CLI

Before starting integrations, the extension runs zettel config show --json and then launches the child command with the same inherited environment:

zettel serve lsp
  (configuration resolved by zettel.toml, zettel_secrets.toml, and env)

The MCP server is the same, with zettel serve mcp instead of zettel serve lsp.

The language server never runs zettel sync — run that yourself (or via a terminal / task) to refresh the index.

Developing

npm install

Then press F5 ("Run Extension") to launch an Extension Development Host. npm run package builds a .vsix (needs @vscode/vsce).

No build step — plain CommonJS JavaScript in src/.