Skip to content

zenor0/mdship

Repository files navigation

mdship banner

Deliver your Markdown content anywhere in one command. mdship is a CLI tool and Go library for packaging Markdown context and converting to various formats via Pandoc and other tools.

Key features

  • Resource Discovery and Packaging: Automatically discover local and remote resources (images, PDFs, etc.) linked in your Markdown, download remote assets, and package them into a structured workspace for conversion.
  • BibTex Citation Trimming: Automatically extract and trim BibTeX entries for only the citations used in your document, keeping your bibliography lean and relevant.
  • Built-in neat Typst/Word templates: Use mdship's built-in Typst templates for quick and beautiful PDF generation, or create your own templates for custom styling.
  • Mermaid Rendering: Optionally render Mermaid diagrams to images before conversion, ensuring your diagrams look great in the final output.
  • Conversion Pipeline: Convert your Markdown to various formats (PDF, HTML, Typst, etc.) using a configurable pipeline that can include pre-processing steps (like Mermaid rendering) and post-processing steps (like Typst compilation).

Architecture

Two stages keep the system extensible:

  1. pack: discover resources/citations, download remote assets, trim BibTeX, and build a minimal workspace.
  2. convert: run a conversion pipeline against packed markdown with predictable paths.

Workspace layout (pack output)

<out-dir>/
  document.md
  manifest.json
  bibliography.bib        (optional)
  assets/                 (local files copied here)
  remote/                 (downloaded files)

Installation

Install the CLI with Go:

go install github.com/zenor0/mdship/cmd/mdship@latest

For reproducible installs, prefer a tagged version instead of latest:

go install github.com/zenor0/mdship/cmd/mdship@v0.1.0

Make sure your Go bin directory is on PATH:

export PATH="$(go env GOPATH)/bin:$PATH"

Then verify the installation and check which external tools are still needed:

mdship --version
mdship check

Requirements

  • Go 1.24+ (only required for go install or local development)
  • Pandoc on PATH
  • Mermaid CLI (mmdc) when Mermaid rendering is enabled (pack defaults to enabled)
  • Typst when compiling to PDF/PNG or using typst compile

Local checks before commit

To catch the same Go formatting, vet, lint, and test failures before pushing, run:

make pre-commit

For a fuller local pass that also checks go mod tidy drift and race tests, run:

make ci-local

Install the repo-pinned golangci-lint version used by CI with (it is stored under .tools/bin):

make lint-install

golangci-lint is pinned in .golangci-version, and GitHub Actions reads that same file, so you only need to update one place when bumping the linter version. make pre-commit will auto-install or refresh that pinned version locally when missing or stale.

If you want Git to run the fast checks automatically before each commit, enable the repo hook once:

git config core.hooksPath .githooks

Usage

Check external dependencies:

mdship check
mdship check --format json

check reports pandoc, mmdc, and typst so you can validate pack + convert toolchains up front.

Structured error output for integrations

On command failure, mdship still prints a human-readable error line to stderr. When stderr is non-interactive (pipe/file), mdship also appends one machine-readable line:

MDSHIP_ERROR_JSON: {"error":{"source":"cli","code":"...","kind":"...","message":"...","suggestions":[...],"details":{...}}}

You can force this behavior with MDSHIP_ERROR_JSON=1 or disable it with MDSHIP_ERROR_JSON=0. Common codes include CLI_FLAG_INVALID, CLI_DEPENDENCY_MISSING, CLI_CONFIG_INVALID, CLI_PROFILE_INVALID, CLI_FILE_MISSING, and CLI_RESOURCE_MISSING.

Pack a Markdown file:

mdship pack ./notes/example.md --out-dir ./mdship-out

By default, pack only searches the input directory for relative resources (--parent-depth=0). Increase --parent-depth (or add --resource-path) when you intentionally want parent-directory lookup.

Clean previously generated workspace artifacts before writing fresh outputs:

mdship pack ./notes/example.md --out-dir ./mdship-out --clean

Add resource paths and bibliography files:

mdship pack ./notes/example.md \
  --resource-path ./attachments \
  --bibliography ./refs/library.bib

Override Pandoc markdown output settings during pack:

mdship pack ./notes/example.md --pandoc-arg=--wrap=preserve

Convert the packaged document:

mdship convert --input ./mdship-out/document.md --to typst

Select a built-in conversion profile (e.g., Typst template preset):

mdship convert --input ./mdship-out/document.md --profile typst/default

Use Typst with a profile (embedded by default):

mdship convert --input ./mdship-out/document.md --to typst \
  --profile typst/default

Use a custom profile on disk:

mdship convert --input ./mdship-out/document.md --to typst \
  --profile /path/to/your/profile.yaml

Built-in profiles live under modules/<format>/profiles. You can also point --profile at a local profile.yaml (or a profile directory).

Release binaries and go install builds include built-in profiles from embedded assets. You can verify them with mdship profiles after installation.

List built-in profiles plus additional profile directories:

mdship profiles
mdship profiles --profile-dir ./modules --profile-dir /path/to/custom/modules
mdship profiles --profile-dir ./modules --verbose
mdship profiles --profile-dir ./modules --format json

profile.yaml defines pandoc CLI args plus template metadata. args can be raw strings or structured YAML (unset fields are skipped):

meta:
  name: Typst Default
  alias: [default]
  tags: [typst]
  author: mdship
  description: Default typst template profile.
args:
  to: typst
  standalone: true
  template: profiles/default/template.typst
  lua-filter:
    - common/filters/html-br-linebreak.lua
    - profiles/default/filters/callout-blocks.lua
  metadata:
    example: true

Rules of thumb: keys map to Pandoc flags, true adds a flag, lists repeat flags, and metadata/variable accept maps.

Render Mermaid blocks before conversion:

mdship convert --input ./mdship-out/document.md --to pdf --mermaid

Configure a conversion chain explicitly:

mdship convert --input ./mdship-out/document.md --to pdf \
  --pipeline mermaid,pandoc:typst,typst-compile

Keep intermediate Typst output when compiling to PDF/PNG:

mdship convert --input ./mdship-out/document.md --to pdf --pipeline typst,typst-compile --typst-compile-keep

Pass extra Pandoc arguments after --:

mdship convert --input ./mdship-out/document.md --to typst -- --template template.typst

Ship a markdown file (pack + convert in a temp workspace):

mdship ship ./notes/example.md --to typst

When ship runs in default file mode and --out is omitted, output files are written to the current working directory (for example ./example.typ).

When ship writes outputs in file mode, existing conflicting files are protected by default (primary output + sidecars). In an interactive terminal, mdship prompts before overwrite; in non-interactive runs it fails safely.

Use --force to overwrite without prompting:

mdship ship ./notes/example.md --to typst --force

When using ship, --resource-path/-r is also applied to the pack stage so external assets can be collected into the packaged workspace:

mdship ship ./notes/example.md --to typst --resource-path ./attachments

Ship into a directory or zip archive:

mdship ship ./notes/example.md --to typst --ship-package dir --out ./dist
mdship ship ./notes/example.md --to pdf --ship-package zip --out ./dist/example.zip

ship now preserves packaged sidecar resources (assets/, remote/) for text-like outputs (for example Typst/Markdown/HTML) so relative links keep working after export. It also writes a ship report with warnings/missing resources for downstream tooling:

  • file mode: <output-base>.ship-manifest.json
  • dir/zip mode: ship-manifest.json

ship-manifest.json and preflight reports now share the same JSON envelope (version: 0.2.0) so external callers can parse one schema for both dry-run checks and real shipments.

Run a preflight check (pack-only) before full ship, and fail on any warnings/missing resources:

mdship ship ./notes/example.md --preflight --strict --report-out ./dist/preflight-report.json

Use pack --strict to fail when unresolved resources or warnings are detected:

mdship pack ./notes/example.md --strict --report-out ./mdship-out/preflight-report.json

Override a specific markdown resource target without moving files into resource paths:

mdship ship ./notes/example.md --preflight --strict \
  --resource-map "images/logo.png=/tmp/uploads/new-logo.png"

Use a config file (YAML or JSON) to provide defaults:

mdship --config ./mdship.yaml pack ./notes/example.md

The default config lives at internal/config/default.yaml (copy it as a starting point). CLI flags override config values when provided. For module layout conventions, see docs/modules.md.

For an implementation-based CLI input/output/error contract, see docs/cli-contract.md.

Profiles can be declared in config and selected via convert.profile or --profile:

convert:
  profiles:
    typst/default:
      path: typst/default
  profile: typst/default

Print the default or effective config:

mdship config
mdship --config ./mdship.yaml config --effective

Example

See examples/example.md for a minimal Markdown + asset + bibliography setup.

Contact

For questions, feedback, or contributions, please open an issue or reach out on GitHub Discussions.

License

mdship is licensed under the MIT License. See LICENSE for details.

About

pack and ship your markdown

Topics

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors