panforge is a from scratch Go port of the Ruby panrun script. It is a wrapper around pandoc that allows you to specify compile commands (like output formats and Pandoc arguments) directly in the YAML header of your Markdown files.
- Pandoc must be installed and available in your
PATH. - Go (1.25+ recommended) for building from source.
go install github.com/rapjul/panforge/cmd/panforge@latestgit clone https://github.com/rapjul/panforge.git
cd panforge
go install ./cmd/panforgeTo quickly get started with a new file or configuration:
# Generate a sample Markdown file with YAML frontmatter (default: input.md)
panforge init --markdown
# OR
panforge init -m
# Generate a sample Markdown file with a custom filename
panforge init -m chapter1.md
# OR generate a default config file in the current directory (default: .panforge.yaml)
panforge init --config
# Generate a sample Markdown file configured for specific output formats
panforge init -m -t pdf,docx# Convert a single file
panforge input.md
# Batch convert multiple files
panforge doc1.md doc2.md
panforge *.md
# Specify input files explicitly via flags
panforge -i doc1.md -i doc2.mdThis reads configuration from the YAML front-matter of each Markdown file to determine how to process it.
To safely pass arbitrary flags directly to pandoc without interference, use the POSIX -- separator:
# Pass input format directly to pandoc
panforge input.md -- -f markdown
# Pass arbitrary flags directly to pandoc
panforge input.md -- --toc --toc-depth=2 --shift-heading-level-by=1
# Batch convert multiple files with passthrough flags
panforge *.md -- --toc-t, --to <format>,--target <format>: Specifically target one or more output formats defined in the YAML header. Can be used multiple times.-i, --input <file>: Explicitly specify one or more input files.-o, --output <file>: Override the output filename (supports templates such as"{title}.{ext}").-a, --all: Process all formats defined in the YAML header (this is also the default behavior if no targets are specified).-F, --force: Force overwrite of existing output files without prompting.-n, --dry-run: Print thepandoccommands that would be executed without running them.-v, --verbose: Enable verbose logging.-q, --quiet: Suppress standard output messages.-w, --watch: Watch input file and configuration for changes and automatically re-run (implies--force).-c, --concurrency <num>: Limit number of concurrent Pandoc processes (default: number of CPUs).-r, --relative-output: Resolve relative output paths against CWD instead of input file directory.--log <file>: Append logs to the specified file.--json: Output logs in JSON format.
panforge supports shell completion for Bash, Zsh, Fish, and PowerShell. This includes dynamic completion for output formats and input files.
To generate the completion script:
source <(panforge completion bash)source <(panforge completion zsh)panforge completion fish | sourceTo load completions for every session, write the output to your shell's completion directory or config file (e.g., ~/.bashrc or ~/.zshrc).
panforge looks for configuration files in the following order:
- Project Level:
./.panforge.yaml,./panforge.yaml,./.panforge.yml, or./panforge.ymlin the current working directory. - XDG Specification:
$XDG_CONFIG_HOME/panforge/default.yaml(e.g.,~/.config/panforge/default.yamlon Linux/macOS). - Windows:
%APPDATA%/panforge/default.yaml. - Default:
~/.config/panforge/default.yaml(ifXDG_CONFIG_HOMEis unset).
You can place your default configuration file in any of these locations to customize fallback templates and Pandoc options across documents.
panforge looks for strictly structured metadata in the YAML header of your Markdown file.
You can define a list of formats to generate using the outputs key, or a map of configurations using the output key.
Allows specifying per-format options.
---
title: My Document
output:
html:
to: html5
standalone: true
css: style.css
pdf:
pdf-engine: xelatex
variable:
geometry: margin=2cm
---Running panforge file.md on the above will generate both an HTML and a PDF file.
Simple list of formats.
---
outputs:
- html
- docx
---Any key inside an output block is translated to a Pandoc argument.
The following rules apply:
key: value->--key=valuekey: true->--keykey: [list]->--key=item1 --key=item2 ...key: {map}-> (varies, usually not directly mapped to simple flags, butvariablesandmetadataare special cases)
Options at the root of the YAML header are treated as variables or metadata by panforge if they match known configuration keys, otherwise they are passed to Pandoc as metadata.
Special keys processed by panforge:
output/outputs: Defines targets.filename-template: (Optional) Template for output filenames (e.g.,"{title}_{date}.{ext}").- Supported template variables include:
{date}and{time}(formatted asYYYY-MM-DDandHH:MM:SS, respectively){title}and{title-slug}(iftitleis a string){author}and{author-slug}(ifauthoris a string){ext}(file extension)
- If
slugify-filenameis enabled,{title}and{author}will be slugified any time they are used (e.g.,my-titleinstead ofmy title)
- Supported template variables include:
slugify-filename: (Optional) Boolean to enable/disable filename slugification (default:false).
This project is designed for Minimal Maintenance.
- Dependencies: All Go dependencies are vendored in the
vendor/directory. This ensures the project can always be built even if upstream repositories disappear or make incompatible changes. - CI/CD: Workflows are pinned to specific versions (Go 1.25, GoReleaser v2) to prevent "bit rot" where CI breaks simply because a tool updated.
- Versioning: The project uses semantic versioning (e.g.,
v1.2.3). - Branches: The project uses a single branch (
main) for development and releases.
If you need to update dependencies:
- Run
go get -u ./...(or update specific packages). - Run
go mod tidy. - Run
go mod vendorto update thevendor/directory. - Commit changes.
This project uses Lefthook for fast, Go-native git hooks.
-
Install:
go install github.com/evilmartians/lefthook@latest go install github.com/google/yamlfmt/cmd/yamlfmt@latest
-
Setup: Run
lefthook installin the repo root. -
Run Manually:
lefthook run pre-commit.
For development instructions, please see CONTRIBUTING.md.