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.
- 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).
Two stages keep the system extensible:
- pack: discover resources/citations, download remote assets, trim BibTeX, and build a minimal workspace.
- convert: run a conversion pipeline against packed markdown with predictable paths.
<out-dir>/
document.md
manifest.json
bibliography.bib (optional)
assets/ (local files copied here)
remote/ (downloaded files)
Install the CLI with Go:
go install github.com/zenor0/mdship/cmd/mdship@latestFor reproducible installs, prefer a tagged version instead of latest:
go install github.com/zenor0/mdship/cmd/mdship@v0.1.0Make 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- Go 1.24+ (only required for
go installor 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
To catch the same Go formatting, vet, lint, and test failures before pushing, run:
make pre-commitFor a fuller local pass that also checks go mod tidy drift and race tests, run:
make ci-localInstall the repo-pinned golangci-lint version used by CI with (it is stored under .tools/bin):
make lint-installgolangci-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 .githooksCheck external dependencies:
mdship check
mdship check --format jsoncheck reports pandoc, mmdc, and typst so you can validate pack + convert toolchains up front.
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-outBy 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 --cleanAdd resource paths and bibliography files:
mdship pack ./notes/example.md \
--resource-path ./attachments \
--bibliography ./refs/library.bibOverride Pandoc markdown output settings during pack:
mdship pack ./notes/example.md --pandoc-arg=--wrap=preserveConvert the packaged document:
mdship convert --input ./mdship-out/document.md --to typstSelect a built-in conversion profile (e.g., Typst template preset):
mdship convert --input ./mdship-out/document.md --profile typst/defaultUse Typst with a profile (embedded by default):
mdship convert --input ./mdship-out/document.md --to typst \
--profile typst/defaultUse a custom profile on disk:
mdship convert --input ./mdship-out/document.md --to typst \
--profile /path/to/your/profile.yamlBuilt-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 jsonprofile.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: trueRules 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 --mermaidConfigure a conversion chain explicitly:
mdship convert --input ./mdship-out/document.md --to pdf \
--pipeline mermaid,pandoc:typst,typst-compileKeep intermediate Typst output when compiling to PDF/PNG:
mdship convert --input ./mdship-out/document.md --to pdf --pipeline typst,typst-compile --typst-compile-keepPass extra Pandoc arguments after --:
mdship convert --input ./mdship-out/document.md --to typst -- --template template.typstShip a markdown file (pack + convert in a temp workspace):
mdship ship ./notes/example.md --to typstWhen 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 --forceWhen 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 ./attachmentsShip 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.zipship 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.jsonUse pack --strict to fail when unresolved resources or warnings are detected:
mdship pack ./notes/example.md --strict --report-out ./mdship-out/preflight-report.jsonOverride 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.mdThe 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/defaultPrint the default or effective config:
mdship config
mdship --config ./mdship.yaml config --effectiveSee examples/example.md for a minimal Markdown + asset + bibliography setup.
For questions, feedback, or contributions, please open an issue or reach out on GitHub Discussions.
mdship is licensed under the MIT License. See LICENSE for details.
