Documentation

Configuration

Learn how to configure the CLI for your project using a config file, frontmatter metadata, and environment variables.

Configuration file

The config file meldoc.config.yml lives in your project root. It’s optional — the CLI works without it using sensible defaults.

Run meldoc init to create one, or add it manually:

workspaceAlias: dundler-paper
projectAlias: dundleros-api
baseDir: docs
prune: true

To keep the file somewhere else, point MELDOC_CONFIG_FILE at it — a relative path is resolved against the current working directory.

Config fields

Field Default Description
workspaceAlias — Workspace alias for API access. Required with meldoc auth login; derived from token when using MELDOC_TOKEN
projectAlias — Project alias (optional, useful for multi-project workspaces)
baseDir repo root Documentation folder (e.g., docs)
baseDirs — Several projects in one repository, each with its own projectAlias and baseDir. See Several Projects in One Repository
ext .meldoc.md File extension for new files
patterns ["\\.{ext}$"] Regexp patterns for document file discovery
exclude [] Regexp patterns to exclude paths from scan, push, and pull
nameTemplate {alias} File name template for new files
prune false Delete server documents not found locally during full push
scope [] Limit sync to specific document aliases (for multi-repo setups)
scopeChildren true When scope is set, include child documents of scoped aliases

Rules checked when the config loads

These are checked before any command reaches the network, so a malformed config fails immediately rather than half-way through a push:

  • project directories must not overlap or repeat;
  • projectAlias must be unique, and is required once there is more than one project;
  • the repository root (an empty baseDir) is allowed only when it is the only project;
  • absolute paths, and paths that climb out of the repository with .., are rejected;
  • a top-level projectAlias used alongside baseDirs must name one of the listed projects. A lone baseDirs entry with no alias of its own adopts it instead.

Examples

Minimal — everything else takes its default:

workspaceAlias: dundler-paper

Documentation in a subdirectory, with some of it excluded:

workspaceAlias: dundler-paper
projectAlias: dundleros-api
baseDir: docs
nameTemplate: "{alias}"
exclude:
  - "archive/"
  - "^drafts/"

More than one file pattern, so ordinary Markdown files are picked up too:

workspaceAlias: dundler-paper
patterns:
  - "\.meldoc\.md$"
  - "CLAUDE\.md$"
ext: .meldoc.md

Project structure

Use baseDir to keep docs separate

baseDir: docs

This keeps documentation in a docs/ folder, separate from source code.

Keep several projects in one repository

A repository can hold the documentation of more than one Meldoc project, listed under baseDirs, each in its own directory. Every command then processes all of them in one run, and --project <alias> limits it to one. See Several Projects in One Repository.

Which files count as documents

patterns and exclude are lists of regular expressions matched against the path of a file relative to the repository root, with forward slashes. Only .md files are ever considered.

The filters always run in this order:

repo-relative path
  -> owner (baseDirs)
  -> patterns
  -> exclude
  -> scope

So patterns select documents within a project and cannot widen its boundary: under baseDirs, a pattern as broad as \.md$ still sees only files under that project’s baseDir.

Repository boundary

A scan covers exactly one git checkout — the one you are standing in. This applies before patterns, exclude and scope, and it is not configurable.

The root is the checkout that contains your working directory, found by walking up for a .git entry. A command run inside a linked worktree is therefore rooted at that worktree, not at the clone it lives in. A nested directory that is a checkout of its own — a worktree, a submodule, a vendored clone — is skipped, because git does not track its contents as part of this repository either.

This is what keeps a repository with worktrees checked out inside it (worktrees/, .claude/worktrees/) from reporting every document as a duplicate alias of itself. exclude is deliberately not the mechanism, and .gitignore is not consulted: both would make the boundary depend on per-clone settings that a fresh clone or a CI runner does not have.

Use directories for nested documents

Documents with children should be organized in directories with index.meldoc.md:

docs/
├── getting-started.meldoc.md
├── api/
│   ├── index.meldoc.md
│   ├── authentication.meldoc.md
│   └── order-endpoints.meldoc.md
└── warehouse/
    ├── index.meldoc.md
    └── inventory-sync.meldoc.md

Control sidebar order

Set the order field in frontmatter to control a document’s position among siblings. Lower values appear first. You can also prefix filenames with numbers (e.g., 01-intro.meldoc.md) — the CLI uses the number as order automatically.

Run meldoc organize to restructure files based on parent-child relationships.

Document frontmatter

Every .meldoc.md file starts with YAML frontmatter:

---
alias: order-endpoints
title: Order Endpoints
parentAlias: dundleros-api
order: 2
---

Your Markdown content here...

title is required. It’s the display title of the document.

alias is a unique identifier in kebab-case, stable across syncs. Required for push — if missing, the CLI derives one from the filename on first push. You can also run meldoc migrate to add aliases to existing files.

parentAlias sets the parent document for hierarchy. parent_alias (snake_case) is also accepted.

order controls position among siblings (lower values appear first).

workflow is published (default) or draft.

visibility is visible (default) or hidden — controls whether the document appears in the navigation sidebar.

exposure is inherit (default), private, unlisted, or public — controls the access level.

archived is true or false — Archiving Documents the document on push. Leave the key out to leave the archive state alone.

replacedBy names the alias of the document that supersedes an archived one. Like archived, an absent key means “don’t change”.

template names the Document Templates this document follows. meldoc pull writes and removes this key to match the server, so expect it to appear in documents you did not edit.

fields carries the document’s Custom Fields values as a nested block, keyed by field key. Inherited defaults are not written on pull — only values set explicitly on the document.

Keep in mind: title must be set in frontmatter. If it’s missing, meldoc validate flags the document and asks you to add one — the CLI does not infer the title from the document body.

Authentication

Meldoc resolves authentication in this order:

  1. --token CLI flag
  2. token field in meldoc.config.yml
  3. MELDOC_TOKEN environment variable
  4. Credentials file from meldoc auth login (~/.meldoc/credentials.json)

For interactive use, meldoc auth login is the easiest option — it opens your browser and stores tokens that refresh automatically. When using login-based auth, set workspaceAlias in your config file.

For CI/CD and scripts, use an Integration Tokens via MELDOC_TOKEN or the --token flag. Tokens carry workspace and project scope, so you don’t need them in config.

Where the workspace comes from

Auth method Workspace source
Integration token (MELDOC_TOKEN or --token) Derived from the token’s own scope — no config needed
meldoc auth login The workspaceAlias config field, or --workspace on commands that accept it

With login-based auth and no workspace set, push, pull and delete fail.

For details on all environment variables, see Environment Variables.

Scope

When several repositories publish into one Meldoc project, scope limits which documents each repository processes.

Setting Behaviour
scope: [] (or unset) All documents are processed
scope: [core] Only core and, when scopeChildren is true, its descendants
scopeChildren: false Only the exact aliases listed in scope, without their children
workspaceAlias: dundler-paper
scope:
  - core
  - fe-api
scopeChildren: true

Scope affects push, pull, scan, validate, and pruning during push. The server-side How a Sync Runs reads the same setting, so a repository subscribed to a shared project takes only its own part in both directions, and the project’s Repositories tab shows what each subscription is limited to.

With scope configured, run meldoc validate to check that every local document has an ancestor inside the scope. meldoc validate --fix assigns a parentAlias to orphaned documents that are not connected to a scope root. When a token is available, validation also checks for alias collisions with out-of-scope documents on the server and verifies cross-project [[projectAlias::docAlias]] links.

Beyond scope, meldoc validate checks frontmatter (a required alias, valid values for workflow, visibility and exposure, valid alias characters) and scans document content for broken [[alias]] links, broken relative-path links and missing asset references.

scope and scopeChildren can also be set per project — see Several Projects in One Repository.

Prune mode

When prune: true, meldoc push deletes server documents that no longer exist locally. It only applies during a full-repo push (no --alias or path arguments) and only when the local cache is non-empty, which keeps a fresh checkout from wiping a project before it has ever scanned it. Pruning respects exclude and scope, so a document you filtered out is not treated as one you deleted.

prune is a single config-level setting: with several projects configured it applies to all of them, and there is no per-project form.

# Preview what would be pruned
meldoc push --dry-run

# Push and prune
meldoc push

What “delete” means here is decided by the project setting Documents deleted via CLI in Project Settings: Delete permanently (the default) sends pruned documents to the trash, Move to archive Archiving Documents them instead — links keep working. Documents already in the archive are skipped by push, so pruning converges instead of retrying them forever.

The .meldoc/ directory

meldoc scan, push and pull keep their local state in a .meldoc/ directory at the root of the checkout. meldoc init adds it to .gitignore. Do not commit it — it describes your copy of the repository, not the documentation.

.meldoc/
├── cache/
│   ├── state.json    # per-file sync state
│   ├── cursor.json   # incremental-pull position, one per project
│   ├── templates/    # document template registry, one file per project
│   └── base/         # base versions used for three-way merge
└── tracked.json      # tracked documents (project + alias → path)

There is one cache per checkout: a linked worktree resolves its own root, so it keeps its own .meldoc/ instead of sharing the enclosing clone’s. With several projects configured, each record names the project it belongs to, so the projects sync independently.

Tracked documents

.meldoc/tracked.json holds the tracked set — the documents meldoc pull --tracked fetches. Manage it with meldoc track add, remove, list and clear.

With several projects configured, meldoc track add and meldoc track remove require --project, and the path you track must lie inside that project’s directory.

Agent skills

Meldoc ships AI agent skills (for Claude Code and similar tools) that teach an agent the document format and how to write the text — see IDE Setup for the list and download links. Install them into .claude/skills/ with:

meldoc init --with-skills

Or manage them directly with meldoc skills init, meldoc skills update, and meldoc skills list.

What’s next?

Command Reference — Full reference for every CLI command.

Several Projects in One Repository — Several projects in one repository.

Workflow — Push, pull, and daily sync workflows.

CI/CD Integration — Set up CI/CD integration.