Universal file-format converter CLI — convert 50+ registered formats with one binary.
convertr wraps pandoc, ffmpeg, LibreOffice, ImageMagick, jq, yq, Tesseract and a dozen more tools behind a single convertr FILE -o OUT interface. It finds the shortest conversion path automatically — you never need to remember which binary handles which format.
Documents: MD · DOCX · PDF · ODT · HTML · EPUB · RST · TeX · PPTX
Images: JPG · PNG · WebP · SVG · AVIF · HEIC · GIF
Video: MP4 · MKV · WebM · AVI · MOV
Audio: MP3 · FLAC · AAC · WAV · OGG
Data: JSON · YAML · TOML · CSV · XLSX
OCR: image → text via Tesseract
| Area | Details |
|---|---|
| Runtime | Go 1.25+ |
| Interface | CLI, watch mode, TUI, built-in Web UI, REST API |
| Format registry | 50+ registered format IDs in internal/formats/known.go; inspect locally with convertr formats |
| Routing | Dijkstra search over installed backend capabilities, with speed/quality route policies |
| Backend tooling | pandoc, ffmpeg, LibreOffice, ImageMagick, jq, yq, Tesseract, csvkit, AsciiDoctor, Calibre, pdfcpu, and plugin backends |
| Contributor checks | make build, make test, make lint; CI also runs go vet, race-enabled tests, go build ./..., and govulncheck |
Useful next reads: CONTRIBUTING.md, SECURITY.md, docs/api.md, docs/docker.md, and docs/remediation-audit.md.
Most conversions need a different tool: pandoc for documents, ffmpeg for
video, convert for images. convertr wraps them all — just specify source and
target format, and it picks the right backend automatically, even chaining
multiple tools when no direct route exists.
- Zero format memorisation — just specify source and target
- Batch conversion with parallel workers and retry policies
- Watch mode: auto-convert on file save
- Plugin protocol for custom backends
- Install
- Docker
- Quick start
- Config — config.toml
- CLI reference
- Backends
- Watch mode
- Plugins
- Progress reporting
- TUI
- Web UI
- Architecture
- Troubleshooting
- License
go install github.com/Mark1708/convertr/cmd/convertr@latestRequires Go 1.25+.
brew install mark1708/tap/convertrgit clone https://github.com/Mark1708/convertr.git
cd convertr
go build -o ~/.local/bin/convertr ./cmd/convertr# macOS
./scripts/install-deps-macos.sh
# Debian / Ubuntu
./scripts/install-deps-linux.shconvertr version
convertr doctordoctor checks every backend binary, prints the detected version, and suggests brew install / apt-get install commands for anything missing.
Use ghcr.io/mark1708/convertr for the full-backends image with the Web UI, REST API, and CLI. See docs/docker.md for run commands, tags, volumes, health checks, and image caveats.
# Convert a single file — format is inferred from the extension.
convertr report.docx -o report.md
# Specify target format explicitly.
convertr notes.md --to pdf -o notes.pdf
# Batch: convert a whole directory, one Markdown per DOCX.
convertr -r ./docs/ -o ./out/ --to md
# Parallel batch with 4 workers.
convertr -r ./inbox/ -o ./outbox/ --to pdf -j 4
# Stdin → stdout (pipe-friendly).
cat data.json | convertr - --from json --to yaml -o -
# Dry run — print the planned conversions without executing.
convertr -r ./docs/ -o ./out/ --to md --dry-run
# Prefer higher-quality routes when several chains are available.
convertr source.docx -o out.pdf --route-policy quality
# Avoid lossy edges when a lossless route exists.
convertr image.png -o out.webp --avoid-lossy
# Restrict route search to short chains.
convertr source.md -o out.pdf --max-hops 2
# Watch a directory and convert on every change.
convertr watch ./inbox -o ./outbox --to md
# Show all known formats and the conversion graph.
convertr formats
convertr formats --dot | dot -Tsvg > /tmp/formats.svgLives at ~/.config/convertr/config.toml or wherever --config points.
Create a default file:
convertr config init# ~/.config/convertr/config.toml
[defaults]
quality = 85 # 0–100; backend-specific interpretation
workers = 0 # 0 = GOMAXPROCS; > 0 = fixed count
on_error = "skip" # skip | stop | retry
on_conflict = "overwrite" # overwrite | skip | rename | error
# Fonts used by PDF-producing backends (pandoc with xelatex/lualatex).
# `convertr config init` seeds these with OS-appropriate defaults; any empty
# field falls back to the built-in platform default.
[fonts]
mainfont = "PT Serif"
monofont = "Menlo"
sansfont = "Helvetica Neue"
# Extra arguments forwarded to individual backends.
[backend.pandoc]
extra_args = ["--wrap=none", "--variable=lang:ru"]
[backend.ffmpeg]
extra_args = ["-preset", "fast"]
[backend.tesseract]
extra_args = ["--dpi", "300"]
# Named profiles — activate with --profile NAME.
[profile.hi-res]
quality = 100
[profile.ci]
workers = 4
on_error = "stop"
on_conflict = "error"
[profile.ocr]
quality = 95| Field | Type | Default | Description |
|---|---|---|---|
quality |
int | 85 | Conversion quality hint (0–100) |
workers |
int | 0 | Parallel workers; 0 = GOMAXPROCS |
on_error |
string | skip |
What to do when a job fails |
on_conflict |
string | overwrite |
What to do when output already exists |
| Variable | Overrides |
|---|---|
CONVERTR_QUALITY |
defaults.quality |
CONVERTR_WORKERS |
defaults.workers |
CONVERTR_ON_ERROR |
defaults.on_error |
CONVERTR_ON_CONFLICT |
defaults.on_conflict |
hardcoded defaults → config file → environment variables → CLI flags
CLI flags always win. A missing config file is not an error — defaults are used.
Available on every subcommand:
| Flag | Short | Default | Description |
|---|---|---|---|
--config |
~/.config/convertr/config.toml |
Path to config file | |
--profile |
Activate a named profile | ||
--lang |
Language override (en, ru) |
||
--verbose |
-v |
0 | Increase log verbosity (repeatable: -vvv) |
--quiet |
-q |
false | Silence all output except errors |
--json |
false | Emit structured JSON logs | |
--no-color |
false | Disable ANSI color |
convertr FILE [FILE...] -o OUTPUT [flags]
When no subcommand is given, convertr treats arguments as input files and runs the conversion pipeline.
Flags:
| Flag | Short | Default | Description |
|---|---|---|---|
--output |
-o |
Output file, directory, or - for stdout |
|
--to |
Target format ID (e.g. md, pdf, mp3) |
||
--from |
Source format override (auto-detected if omitted) | ||
--dry-run |
false | Print planned conversions without running them | |
--workers |
-j |
1 | Parallel workers |
--on-error |
skip |
Error policy: skip | stop | retry |
|
--on-conflict |
overwrite |
Conflict policy: overwrite | skip | rename | error |
|
--recursive |
-r |
false | Recurse into directories |
--mkdir |
false | Create output directory if it does not exist | |
--named |
Backend-specific named option (backend.key=value, repeatable) |
||
--strip-meta |
false | Request metadata stripping when the selected backend supports it | |
--route-policy |
speed |
Route selection policy: speed | quality |
|
--avoid-lossy |
false | Exclude routes containing lossy conversion edges | |
--max-hops |
4 effective | Maximum conversion route hops. Omit or pass 0 to use the default. |
Input types:
convertr file.md -o out.pdf # single file
convertr a.md b.md -o out/ --mkdir # multiple files → output directory (created if absent)
convertr "src/**/*.md" -o out/ --to pdf # glob
convertr -r ./src/ -o ./out/ --to html # directory (recursive)
convertr - --from json --to yaml -o - # stdin → stdoutOutput directory resolution:
When multiple input files are given, the output path is always treated as a directory — even if no trailing / is present. If the directory does not yet exist, convertr will:
- ask interactively
Create it? [y/N]when running in a TTY, or - return an error with a hint to use
--mkdirin non-interactive mode.
# Auto-create the output directory:
convertr -r ./docs/ -o ./out --to md --mkdir
# TTY prompt (no --mkdir needed):
convertr -r ./docs/ -o ./out --to md
# Output directory does not exist: ./out
# Create it? [y/N]Error policies:
| Policy | Behaviour |
|---|---|
skip |
Record error, continue with remaining jobs |
stop |
Abort all remaining jobs on first error |
retry |
Retry with exponential backoff (max 3 attempts) |
Conflict policies:
| Policy | Behaviour |
|---|---|
overwrite |
Replace existing output file |
skip |
Leave existing output file unchanged |
rename |
Append numeric suffix (.1, .2, …) |
error |
Fail if output file exists |
Route policies:
Route policy flags affect the route that actual conversions execute. Route previews from the HTTP API use the same policy semantics.
| Option | Behaviour | Use when |
|---|---|---|
--route-policy speed |
Default. Minimise route cost. | You want the shortest or cheapest available chain. |
--route-policy quality |
Prefer the route with the best bottleneck quality, then use cost as the tie-breaker. | You care more about preserving fidelity than picking the cheapest path. |
--avoid-lossy |
Exclude lossy conversion edges before route selection. | You only want lossless routes. If every route is lossy, conversion reports no route. |
--max-hops N |
Limit the number of conversion steps. | You want to reject long chains, even if a longer route exists. |
convertr doctorChecks every backend binary: detects the installed version, shows the full path, and prints an install hint for anything missing.
convertr backends list
convertr backends status [BACKEND_ID...]
convertr backends install [BACKEND_ID...] [--missing] [--dry-run] [-y]Backend management commands use the bundled install manifests:
listprints known backend IDs, display names, installed/missing status, and install hints.statusprints detailed status for all backends or selected backend IDs.installprints an install plan and runs the detected package manager unless--dry-runis set.
Install flags:
| Flag | Short | Default | Description |
|---|---|---|---|
--dry-run |
false | Print the install plan without executing it | |
--missing |
false | Install every missing backend | |
--yes |
-y |
false | Skip confirmation prompts |
convertr formats
convertr formats --dot | dot -Tsvg > graph.svg--dot emits a Graphviz DOT graph of all available conversion edges (coloured by format category).
convertr watch SRC -o DST --to FORMAT [flags]Watches SRC recursively and converts every new or modified file to DST.
Flags:
| Flag | Default | Description |
|---|---|---|
--output / -o |
Output directory (required) | |
--to |
Target format (required) | |
--from |
Source format override | |
--debounce |
300ms |
Wait after last event before converting |
--on-delete |
keep |
What to do when source is deleted: keep | remove | archive |
See Watch mode for details.
convertr config print # Show active config with value sources
convertr config init # Create default config file
convertr config validate # Validate TOML syntaxconfig print shows each field, its current value, and where it came from (default / file / env / flag).
convertr plugins list # List convertr-* executables in PATH
convertr plugins test # Probe each plugin's capabilities subcommandSee Plugins for the protocol.
convertr pdf merge -o out.pdf a.pdf b.pdf [c.pdf ...]
convertr pdf split -o out-dir in.pdf
convertr pdf rotate in.pdf -a 90 [-o out.pdf]
convertr pdf crop in.pdf -d "0 0 100 100" [-o out.pdf]
convertr pdf optimize in.pdf [-o out.pdf]
convertr pdf encrypt in.pdf -p PASSWORD [-o out.pdf]
convertr pdf decrypt in.pdf [-p PASSWORD] [-o out.pdf]
convertr pdf watermark in.pdf -d "text:CONFIDENTIAL" [-o out.pdf]
convertr pdf stamp in.pdf -d "text:DRAFT" [-o out.pdf]
convertr pdf nup -o out.pdf -n 4 in.pdf [more.pdf ...]
convertr pdf booklet -o out.pdf -n 4 in.pdf [more.pdf ...]
convertr pdf extract in.pdf -o out-dir
convertr pdf validate in.pdf
convertr pdf info in.pdfPDF commands are processor operations backed by pdfcpu; they do not add format-conversion routes.
Common flags:
| Flag | Short | Used by | Description |
|---|---|---|---|
--output |
-o |
most operations | Output file or directory |
--angle |
-a |
rotate |
Rotation angle: 90, 180, 270, or -90 |
--desc |
-d |
crop, watermark, stamp |
Operation description passed to pdfcpu |
--password |
-p |
encrypt, decrypt |
PDF password |
--mode |
-m |
encrypt |
Encryption mode |
--n |
-n |
nup, booklet |
Grid size |
convertr info FILEDetects the file format, then probes metadata using the first available tool: ffprobe (audio/video), exiftool (images/documents), file (fallback).
convertr versionconvertr serve [--addr ADDR] [--api-only]Starts the HTTP API and, by default, the built-in Web UI.
| Flag | Default | Description |
|---|---|---|
--addr |
:8080 |
HTTP listen address |
--api-only |
false | Serve only the REST API, without the browser UI |
convertr tuiStarts the interactive terminal UI. See TUI.
Each backend is an init()-registered plugin that bridges one or more external binaries to the router.
convertr probes $PATH at startup and excludes capabilities whose
binaries are missing from the conversion graph. If two backends declare
the same edge, Dijkstra automatically picks the available one — e.g.
xlsx → csv prefers csvkit when in2csv/xlsx2csv are installed and
falls back to libreoffice otherwise. Run convertr doctor to see
which binaries are detected and which are missing.
Binary: pandoc · brew install pandoc / apt install pandoc
| From | To |
|---|---|
md |
html docx odt pdf rst epub tex txt typst ipynb pptx mediawiki jira opml |
html |
md docx pdf txt |
docx |
md html pdf odt txt rst |
odt |
md docx html txt |
rst |
md html pdf txt |
epub |
md html txt |
tex |
md html pdf |
org |
md html pdf |
typst |
md pdf |
ipynb |
md html pdf |
rtf |
md (then any markup via a pandoc chain) |
fb2 |
md html epub |
mediawiki dokuwiki jira textile docbook opml |
md |
bibtex ↔ csljson |
(bibliography) |
PDF output picks xelatex → lualatex → pdflatex in order of
capability; when a fontspec-aware engine is chosen, convertr injects
-V mainfont / -V monofont / -V sansfont from the [fonts] section
(with OS-appropriate defaults) and -V geometry:margin=2cm. Any
variable you set explicitly via --named pandoc.mainfont=... or a
-V … entry in extra_args disables the matching default — user
values always win.
Config: [backend.pandoc] extra_args = ["--wrap=none"]
Binary: ffmpeg · brew install ffmpeg / apt install ffmpeg
| From | To |
|---|---|
mp4 mkv webm mov avi |
(all video formats, bidirectional) |
mp3 flac aac ogg wav m4a opus |
(all audio formats, bidirectional) |
| video formats | gif (two-pass palette) |
| video formats | audio formats (extract audio) |
Quality hint → CRF (video) or bitrate (audio).
Config: [backend.ffmpeg] extra_args = ["-preset", "fast"]
Binary: magick (IM7) or convert (IM6) · brew install imagemagick
| From | To |
|---|---|
jpg png webp gif tiff bmp |
(all raster formats, bidirectional) |
avif |
raster formats (and vice versa) |
svg |
raster formats, avif |
heic |
raster formats |
Quality hint → -quality N.
Binary: soffice · brew install --cask libreoffice / apt install libreoffice
| From | To |
|---|---|
doc docx odt rtf |
odt docx pdf txt |
xlsx ods |
csv ods xlsx |
pptx odp |
odp pptx pdf |
Each conversion runs with an isolated -env:UserInstallation directory to allow safe parallel execution.
Binary: jq · brew install jq
| From | To | Notes |
|---|---|---|
json |
json |
pretty-print, minify, or transform |
Named options: jq.minify=true (compact output), jq.filter=EXPR (custom expression).
Binary: yq · brew install yq
| From | To |
|---|---|
yaml |
json toml |
json |
yaml toml |
toml |
yaml json |
Binary: tesseract · brew install tesseract tesseract-lang
| From | To |
|---|---|
jpg png tiff |
txt |
Default language: rus+eng. Override: [backend.tesseract] extra_args = ["-l", "eng"].
Binary: in2csv / xlsx2csv (fallback) · pip install csvkit
| From | To |
|---|---|
xlsx |
csv |
csv |
json |
xlsx |
json |
Binary: asciidoctor · gem install asciidoctor asciidoctor-pdf
| From | To |
|---|---|
adoc |
html |
adoc |
pdf (requires asciidoctor-pdf) |
Binary: ebook-convert · brew install --cask calibre / apt install calibre
| From | To |
|---|---|
epub |
mobi pdf txt fb2 |
mobi |
epub pdf txt |
fb2 |
epub mobi pdf txt |
azw3 lit |
epub mobi pdf |
odt docx rtf |
epub |
html |
epub mobi |
Named options include calibre.title, calibre.authors, calibre.language, and calibre.pdf_page_numbers.
Binaries: cjxl / djxl · brew install jpeg-xl
| From | To |
|---|---|
jpg png |
jxl |
jxl |
jpg png |
Encoding to jxl requires cjxl; decoding from jxl requires djxl.
Named options include libjxl.distance, libjxl.effort, and libjxl.jpeg_quality.
Binary: resvg · brew install resvg
| From | To |
|---|---|
svg |
png |
Named options include resvg.dpi, resvg.zoom, resvg.width, and resvg.height.
Binary: pdfcpu · brew install pdfcpu
pdfcpu is registered for discovery and doctor output, but it exposes processor operations rather than format-conversion routes. Use convertr pdf ... or the Web UI Tools page for merge, split, rotate, crop, optimize, encrypt, decrypt, watermark, stamp, n-up, booklet, extract, validate, and info operations.
Binary: figlet · brew install figlet
| From | To |
|---|---|
txt |
ascii |
Binary: textutil (bundled with macOS)
| From | To |
|---|---|
doc rtf |
txt html |
Available only on macOS; compiled out on other platforms via build tag.
convertr watch ./inbox -o ./outbox --to md
convertr watch ./raw -o ./web --to html --debounce 500ms --on-delete archiveHow it works:
- Recursively watches
SRCusing fsnotify. - Debounces rapid edits — waits
--debounce(default 300 ms) after the last event before triggering. - Detects the format of the changed file, finds a route, converts, writes to
DST. - New subdirectories are watched automatically.
Delete policies:
| Policy | What happens when source is deleted |
|---|---|
keep |
Output file is left as-is (default) |
remove |
Output file is deleted |
archive |
Output file is moved to DST/.archive/ |
Graceful shutdown: Press Ctrl+C (SIGINT / SIGTERM). convertr drains in-flight conversions and exits 0.
convertr supports external plugins — any executable named convertr-* found in PATH.
A plugin must implement capabilities and can implement conversion and process subcommands for the operations it advertises.
Protocol v2 writes a JSON manifest to stdout and exits 0:
{
"protocol_version": 2,
"version": "1.2.3",
"diagnostics": [
{
"level": "warning",
"code": "missing-optional-tool",
"message": "zopfli not installed; using default encoder"
}
],
"capabilities": [
{
"from": "wasm",
"to": "wat",
"cost": 2,
"quality": 95,
"lossy": false,
"metadata": { "engine": "wasm-tools" }
}
],
"operations": [
{
"name": "pdf.merge",
"description": "Merge PDF files",
"params": { "pages": "optional page selection" }
}
]
}Manifest fields:
protocol_version— plugin protocol version. convertr supports v1 and v2; explicit future versions greater than2are rejected with a clear discovery error so incompatible plugins are not silently accepted. If an object omits this field, convertr treats it as v2.version— optional plugin version string for diagnostics and support.diagnostics— optional manifest-level list of{level, code, message}entries. Use it for actionable status such as missing optional tools or degraded quality.capabilities— conversion edges.operations— process operations supported by theprocesssubcommand.
Capability fields:
from/to— format IDs (must match convertr's registry or be new IDs)cost— routing cost (1–10; lower = preferred over built-in backends); defaults to 5 when omitted or non-positivequality— optional quality hint (0–100); higher is better for quality-aware routinglossy— optional boolean telling convertr whether this edge loses informationmetadata— optional string map for plugin-specific edge metadata
Operation fields:
name— operation ID, for examplepdf.mergeorimage.resizedescription— optional human-readable summaryparams— optional parameter name to description map
The v1 bare JSON array remains supported exactly for conversion-only plugins:
[
{ "from": "wasm", "to": "wat", "cost": 2 },
{ "from": "wat", "to": "wasm", "cost": 2 }
]v1 has no manifest-level version, diagnostics, quality, lossy, metadata, or operations. Use v2 for new plugins.
convertr-NAME convert \
--from FROM \
--to TO \
--input /tmp/in.wat \
--output /tmp/out.wasm \
[--opt key=value ...]Exit 0 on success. On failure: exit non-zero, write a single error line to stderr.
Plugins that advertise operations must implement process:
convertr-NAME process \
--operation OP \
--input /tmp/a.pdf \
--input /tmp/b.pdf \
--output /tmp/out.pdf \
[--param key=value ...] \
[--opt key=value ...]Exit 0 on success. On failure: exit non-zero and write a single actionable error line to stderr.
#!/usr/bin/env bash
case "$1" in
capabilities)
cat <<'JSON'
{"protocol_version":2,"version":"0.1.0","capabilities":[{"from":"foo","to":"bar","cost":3,"quality":90,"lossy":false}]}
JSON
;;
convert)
# parse --from --to --input --output from "$@"
my-tool "$INPUT" "$OUTPUT"
;;
esacName it convertr-myplugin, place it in your $PATH, and convertr plugins list will find it immediately.
See pkg/plugin/protocol.go for the Go type definitions.
convertr picks a reporter automatically based on the environment:
| Environment | Reporter |
|---|---|
| Interactive TTY | In-place progress bar ([3/10] converting report.md) |
| Non-TTY / pipe | Plain text, one line per job |
--json or CI=1 |
JSON Lines |
{"event":"start", "total":10, "ts":"2026-04-17T12:00:00Z"}
{"event":"convert", "file":"report.md", "done":1, "total":10, "status":"ok", "ts":"..."}
{"event":"convert", "file":"broken.doc","done":2, "total":10, "status":"error", "error":"exit status 1", "ts":"..."}
{"event":"done", "ts":"2026-04-17T12:00:05Z"}JSON output is never localised — it is a stable scripting contract.
convertr tuiStarts the interactive terminal UI.
Available screens:
Backend Status— inspect registered backends, availability, and capability counts.Route Explorer— select source and target formats, then preview the conversion route.Convert— run an interactive conversion flow from an input file path.Quit— exit the TUI.
Navigation:
- Arrow keys or
j/k— move selection. Enter— select or confirm.borEsc— go back.q— quit.
convertr also ships with a built-in browser UI for conversions, backend inspection, and file operations:
convertr serve
# or bind explicitly
convertr serve --addr 127.0.0.1:8080Upload a file, pick the target format, preview the route, and download the result.
Upload a file and pick the target format. The source format is auto-detected from the extension.
The route explorer shows the selected backend and conversion path before execution.
Route preview for json to yaml through the yq backend.
After conversion completes, the result is ready for download.
Successful conversion with one-click download.
The Backends page lists detected tools, versions, installation paths, and supported conversions — the browser equivalent of convertr doctor.
Backend dashboard with installed tools and supported conversion counts.
The Tools page exposes processor operations such as PDF merge, split, rotate, and optimize.
Process PDF files through browser-based operations.
cmd/convertr/ cobra entry point, blank imports for all backends
internal/
cli/ subcommand implementations (convert, watch, doctor, …)
backend/ Backend interface, Options, registry for backends and processors, execx helper
backends/
pandoc/ pandoc backend
ffmpeg/ ffmpeg backend
imagemagick/ ImageMagick backend
libreoffice/ LibreOffice backend (process-isolated)
jq/ jq backend
yq/ yq backend
tesseract/ Tesseract OCR backend
csvkit/ CSVKit backend
asciidoctor/ AsciiDoctor backend
calibre/ Calibre ebook backend
libjxl/ JPEG XL backend
resvg/ SVG renderer backend
pdfcpu/ PDF processor operations
figlet/ figlet backend
textutil/ textutil backend (darwin only)
plugin/ external plugin discovery and execution for conversion capabilities and process operations
core/ Engine orchestration for conversion and processing requests
config/ TOML loader, env overrides, profile merge, FieldSource
deps/ Dependency/install manifest helpers
fonts/ PDF font defaults and resolution
formats/ Format registry, extension/MIME detection
installer/ Package-manager detection and backend install plans
router/ Dijkstra routing on the capability graph
runner/ Job execution: serial + parallel pool, retry, conflict resolution
server/ REST API, Web UI, uploads, jobs, downloads, SSE events
sink/ Output path resolution, atomic write, conflict policy, template
source/ Input iterators: file, glob, dir, stdin (iter.Seq2)
tui/ Bubble Tea terminal UI screens
watch/ fsnotify wrapper with debounce, recursive add, delete handling
progress/ Reporter interface: TUI, plain, JSON Lines, Noop
i18n/ go-i18n v2, locales/en.json + ru.json
slogx/ slog initialisation, JSON/text handlers, verbosity levels
xdg/ XDG Base Directory paths
pkg/plugin/ Public plugin protocol types (for plugin authors)
- Atomic write — all output goes to
os.CreateTempthenos.Rename. Never written directly. - TempDir per job —
os.MkdirTemp("", "convertr-*")per conversion chain, removed withdefer os.RemoveAll. - LibreOffice isolation — every soffice call gets
-env:UserInstallation=file:///tmp/convertr-lo-PIDto allow parallel execution. - Context propagation — all backends receive
ctxand useexec.CommandContext. - Route policy parity: actual conversions and route previews use the same
speed,quality,avoid_lossy, andmax_hopssemantics. - Registry isolation:
backend.Registryowns both conversion backends and processors per engine instance.DefaultRegistryand package-level wrappers preserve legacyinit()registration behaviour for CLI and older integrations. - Exit codes —
0success ·1error ·2CLI usage ·3no route ·4partial batch ·5missing backend.
cannot determine target format
Provide --to FORMAT or give the output file a known extension (e.g., -o out.pdf).
no conversion route
No installed backend covers the requested From → To pair. Run convertr doctor to see what is missing, and install the relevant tool.
convertr doctor shows a backend as MISSING
Install it via the suggested command. On macOS run ./scripts/install-deps-macos.sh to install everything at once.
LibreOffice hangs or produces a corrupt output
Make sure no other soffice process is running with the same UserInstallation directory. Parallel convertr jobs each get a unique directory; external LibreOffice processes may conflict.
Watch mode does not pick up changes inside a new subdirectory
convertr watches new directories recursively — but only directories created after watch starts that trigger a CREATE event. Re-start watch if you have pre-existing deep trees.
Output file is not where I expected
When --output is a directory, the output file is named <stem>.<target-ext>. Use --on-conflict rename if you are converting multiple files with the same stem.
Plugin not found by convertr plugins list
Ensure the binary is named convertr-NAME (not convertrNAME), is executable (chmod +x), and its directory is in $PATH.
Enable verbose logging
convertr -vvv doctor
CI=1 convertr -r ./src/ -o ./out/ --to md # structured JSON logsMIT — see LICENSE.




