npm package: @stakme/mdf
Description: Lightweight utility to organize Markdown files with front matter
Author: @stakme
Latest version: 0.11.0

# mdf

Organize Markdown knowledge bases with confident, schema-driven front matter.

`mdf` helps teams and solo note-takers keep Markdown collections consistent.
Define the front matter schema you expect, scaffold new notes from templates,
audit and repair existing files, and even browse them with an interactive viewer
— all from one CLI.

> **Version status:** We're continuing in the `0.x` series (latest: `0.10.0`)
> while we collect more feedback before committing to a 1.0 release.

This tool does not enforce any rules on the content of your Markdown files. You
can define any schema, organize your files in any way, and use any template to
scaffold new files. Although this tool assumes that users will store their
Markdown files in a single directory with a flat structure, it is not required.

## Highlights

- **Schema-first authoring** – enforce exactly the fields, defaults, and content
  rules you need.
- **Frictionless scaffolding** – spin up ready-to-edit Markdown files in one
  command.
- **Smart filtering** – slice notes by front matter attributes and render
  tailored output.
- **Confident maintenance** – validate, update, or auto-fix drifted notes before
  they reach your repo.
- **Interactive viewer** – explore documents in a local web UI with filters and
  virtual paths.

## Installation

```bash
npm install --save-dev @stakme/mdf
# or
pnpm add -D @stakme/mdf
# or
yarn add -D @stakme/mdf
```

> **Node.js requirement:** mdf targets Node 22 and newer.

Add the CLI to your package scripts or run it via `npx @stakme/mdf`.

## Quick start

1. **Create a config:** run `mdf init` to scaffold `.config/mdf.mts` (or author
   your own) anywhere under your workspace with a schema describing the front
   matter every file should include.
2. **Generate a note:** run `mdf new <directory>` and provide overrides with
   `--fm` flags or a named template.
3. **Surface the right notes:** explore your collection with `mdf list` filters,
   virtual-path scoping, and custom output formats.
4. **Browse your notes:** start a local viewer with `mdf viewer <directory>` to
   navigate and read documents in your browser.

When you are ready to publish new notes, validate the collection with
`mdf validate` or `mdf fix`.

## CLI overview

| Command                     | Description                                                                                        |
| --------------------------- | -------------------------------------------------------------------------------------------------- |
| `mdf init [directory]`      | Create a starter `.config/mdf.mts` in the target directory.                                        |
| `mdf new <directory>`       | Scaffold Markdown files that match your schema and optional template defaults.                     |
| `mdf list <directory>`      | Inspect existing notes with virtual-path trees, filters, and custom output templates.              |
| `mdf validate <directory>`  | Confirm every file conforms to your schema, exiting non-zero when issues arise.                    |
| `mdf fix <directory>`       | Apply schema defaults and CLI overrides in-place to repair invalid notes.                          |
| `mdf update <files...>`     | Update specific front matter keys on targeted files (explicit values or schema/config defaults).   |
| `mdf viewer <directory>`    | Launch a local web viewer with navigation, filters, and virtual-path scoping.                      |
| `mdf export <directory>`    | Generate a static viewer site for your notes (use `--output` to choose the destination directory). |
| `mdf run <alias> [args...]` | Execute a configured alias that expands to another `mdf` command.                                  |

Run any command with `--help` for the full option list.

### Export a static viewer

Use `mdf export <directory>` when you want to publish the same experience the
`viewer` command provides, but as a static site. The command copies the built
viewer assets, writes JSON responses under `api/**/index.json` that mirror the
viewer API, and replicates document filters such as `--filter` and `--vpath`.
Relative image references are rewritten to serve from `documents/<id>/assets/`,
so the exported site can host local screenshots or diagrams alongside each note.

The output directory defaults to `mdf-export`, but you can provide your own path
with `--output`. The export reuses `--strict` and `--ignore-invalid` flags to
match the viewer's parsing behavior. Virtual paths are computed the same way as
the live viewer, using your schema resolvers or each document's directory.

## Configuration

`mdf` loads the closest `.config/mdf.mts` file in the directory tree. Define
your schema with Zod and optional helpers:

```ts
import { defineConfig, defineSchema, z } from "@stakme/mdf/config";

export default defineConfig({
  schema: {
    default: defineSchema({
      schema: z.object({
        title: z.string(),
        created_at: z.iso.datetime().default(() => new Date().toISOString()),
        updated_at: z.iso.datetime().default(() => new Date().toISOString()),
        status: z.enum(["todo", "in_progress", "done"]).default("todo"),
        tags: z.array(z.string()).default(() => []),
        section: z.string().optional(),
      }),
      vpath: ({ fm }) => (fm.section ? `/${fm.section}` : "/"),
      vslug: ({ fm, relativePath }) =>
        (fm.slug ?? relativePath).replace(/\\.md$/u, ""),
    }),
  },
  defaultSchema: "default",
});
```

- `schema` must be a Zod object describing your front matter. Use
  `defineSchema` to attach helpers such as `filename`, `vpath`, or `vslug`
  resolvers to a schema entry.
- `defaults` sets automatic fallback values for fields you omit when creating
  new notes.
- `content` (optional) can generate the Markdown body from template data.
- `fileName` (optional) lets you compute the file name from front matter values.
- `aliases` (optional) map friendly names to frequently used CLI command
  fragments for `mdf run`.

### Templates and overrides

Define named templates in your config to reuse curated defaults and starting
content. Then apply them on the CLI:

```bash
mdf new notes --template meeting --fm tags=sync --fm attendees="Ada, Lin"
```

Templates layer their front matter on top of schema defaults, while
`--fm key=value` flags win last. Array fields support JSON-style values
(`["release","planning"]`) or repeated flags.

Pass `--filename <name>` to override the generated file name for a specific
note. Omit the extension and `.md` will be appended automatically.

## List, filter, and automate

Use the list command to slice your knowledge base and pipe the output to other
tools:

```bash
mdf list notes
mdf list notes --filter "vpath ^= backlog"
mdf list notes \
  --filter "status=todo" \
  --filter "tags~=feature" \
  --format "[{{status}}] {{title}} ({{tags:, }})"
```

- `mdf list` renders a tree using each document's virtual path; without a schema
  override, that mirrors the on-disk directory layout.
- Provide `--filter` expressions with `=` (or `:`), `~=`, `^=`, or `$=`
  operators for exact, substring, prefix, or suffix matching. Arrays match when
  **any** element satisfies the filter.
- Filters inspect front matter fields. Define a `vpath` field in your schema or
  front matter before using `--filter "vpath ^= …"`.
- Target nested front matter fields with dot notation, such as `project.status`.
- Supply `--format` to bypass the tree and render each match with `{{field}}`
  placeholders. Use helpers like `{{tags:, }}` to join arrays or
  `{{paths.relativePath}}` for the file location.

### Aliases and shortcuts

Store your favorite command combinations in the config and run them with a short
name:

```ts
import { defineConfig, defineSchema, z } from "@stakme/mdf/config";

export default defineConfig({
  schema: {
    default: defineSchema({
      schema: z.object({
        title: z.string(),
        status: z.enum(["todo", "in_progress", "done"]),
        vpath: z.string().optional(),
      }),
      vpath: ({ fm }) => fm.vpath ?? "/",
    }),
  },
  defaultSchema: "default",
  aliases: {
    todo: 'list --filter "status=todo" ./TODO',
  },
});
```

```bash
mdf run todo
```

`mdf run` expands the alias value into a fresh CLI invocation, so all built-in
commands and flags work as if you typed them manually. Aliases can reference
other aliases, and the CLI detects cycles to prevent infinite recursion.

## Keep notes trustworthy

Run validation before shipping changes, or automatically fix what you can:

```bash
mdf validate notes
mdf fix notes --fm status=todo --fm tags=backlog
```

- `validate` reports each valid file and exits with code `0` when everything
  passes.
- `fix` rewrites front matter safely, applying schema defaults and CLI overrides
  before writing.
- Both commands honor the file extension and defaults defined in your config.

### Update specific files

Use `mdf update` when you want to change front matter on particular files
without touching the rest of the collection:

```bash
# Set explicit values and let the schema/config provide defaults
mdf update --fm "status=done" --fm updated_at ./docs/roadmap.md ./docs/intro.md
```

- Provide `--fm key=value` for explicit assignments.
- Provide `--fm key` (no value) to ask `mdf` to fill that key from
  template/config defaults or the schema’s default.
- The command validates the final front matter against your schema before
  writing.

### Interactive viewer

Explore your notes in a local web UI:

```bash
mdf viewer ./docs --vpath blog --filter "status=done" --port 4173
```

- Navigation groups documents by their computed virtual path. Customize this by
  defining a schema `vpath` resolver.
- Supports the same filter expressions as `mdf list`.
- Prints the local URL on start (defaults to `http://127.0.0.1:4173`).
- Troubleshooting: add `--access-log` to print per-request access logs.
- If you experience hanging navigation in certain environments, try
  `--no-reload` to disable hot reload (SSE) and file watching.

## Programmatic usage

The package also exposes utilities for custom tooling. Import from `@stakme/mdf`
or `@stakme/mdf/config` inside build scripts, note-taking automations, or editor
integrations to reuse the same schema and helper functions as the CLI.

## Legal notice

> TL;DR: You can use this freely. But no open‑source license right now.

This project is not distributed under an OSI‑approved open‑source license.

- As of 2025-09-28, you may use this package without notifying or asking the
  author.
- I (stakme) am not presently exercising any copyrights in this repository. This
  is an expression of my current intent and should be read as me not asserting
  rights at this time.
- I may change my mind later and adopt a specific license or begin exercising
  rights for future releases. If that happens, I will update this repository.
  Any change would apply going forward and is not intended to retroactively
  change what you already obtained under these terms.

I used AI‑assisted tooling (Codex CLI) extensively while creating this project
and therefore cannot make a definitive legal declaration about copyright
ownership of every line of code. This does not mean the project is illegal or a
copied work: it simply reflects caution about formal legal assertions at this
time.

## Contributing

I am not accepting contributions at this time, but I welcome issues for feature
requests and bug reports.