convertr
Русская версия
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

At a glance
| 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.
Why convertr?
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
Table of contents
Install
go install
go install github.com/Mark1708/convertr/cmd/convertr@latest
Requires Go 1.25+.
Homebrew
brew install mark1708/tap/convertr
From source
git clone https://github.com/Mark1708/convertr.git
cd convertr
go build -o ~/.local/bin/convertr ./cmd/convertr
Install backend dependencies
# macOS
./scripts/install-deps-macos.sh
# Debian / Ubuntu
./scripts/install-deps-linux.sh
Verify
convertr version
convertr doctor
doctor checks every backend binary, prints the detected version, and suggests brew install / apt-get install commands for anything missing.
Docker
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.
Quick start
# 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.svg
Config — config.toml
Lives at ~/.config/convertr/config.toml or wherever --config points.
Create a default file:
convertr config init
Full example
# ~/.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
Defaults
| 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 |
Environment overrides
| Variable |
Overrides |
CONVERTR_QUALITY |
defaults.quality |
CONVERTR_WORKERS |
defaults.workers |
CONVERTR_ON_ERROR |
defaults.on_error |
CONVERTR_ON_CONFLICT |
defaults.on_conflict |
Priority order
hardcoded defaults → config file → environment variables → CLI flags
CLI flags always win. A missing config file is not an error — defaults are used.
CLI reference
Global flags
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 |
Convert (default command)
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 → stdout
Output 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
--mkdir in 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. |
doctor
convertr doctor
Checks every backend binary: detects the installed version, shows the full path, and prints an install hint for anything missing.
backends
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:
list prints known backend IDs, display names, installed/missing status, and install hints.
status prints detailed status for all backends or selected backend IDs.
install prints an install plan and runs the detected package manager unless --dry-run is 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).
watch
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.
config
convertr config print # Show active config with value sources
convertr config init # Create default config file
convertr config validate # Validate TOML syntax
config print shows each field, its current value, and where it came from (default / file / env / flag).
plugins
convertr plugins list # List convertr-* executables in PATH
convertr plugins test # Probe each plugin's capabilities subcommand
See Plugins for the protocol.
pdf
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.pdf
PDF 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 |
info
convertr info FILE
Detects the file format, then probes metadata using the first available tool: ffprobe (audio/video), exiftool (images/documents), file (fallback).
version
convertr version
serve
convertr 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 |
tui
convertr tui
Starts the interactive terminal UI. See TUI.
Backends
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.
Pandoc
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"]
FFmpeg
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"]
ImageMagick
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.
LibreOffice
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.
jq
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).
yq
Binary: yq · brew install yq
| From |
To |
yaml |
json toml |
json |
yaml toml |
toml |
yaml json |
Tesseract (OCR)
Binary: tesseract · brew install tesseract tesseract-lang
Default language: rus+eng. Override: [backend.tesseract] extra_args = ["-l", "eng"].
CSVKit
Binary: in2csv / xlsx2csv (fallback) · pip install csvkit
| From |
To |
xlsx |
csv |
csv |
json |
xlsx |
json |
AsciiDoctor
Binary: asciidoctor · gem install asciidoctor asciidoctor-pdf
| From |
To |
adoc |
html |
adoc |
pdf (requires asciidoctor-pdf) |
Calibre
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.
libjxl
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.
resvg
Binary: resvg · brew install resvg
Named options include resvg.dpi, resvg.zoom, resvg.width, and resvg.height.
pdfcpu
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.
Figlet (ASCII art)
Binary: figlet · brew install figlet
textutil (macOS only)
Binary: textutil (bundled with macOS)
Available only on macOS; compiled out on other platforms via build tag.
Watch mode
convertr watch ./inbox -o ./outbox --to md
convertr watch ./raw -o ./web --to html --debounce 500ms --on-delete archive
How it works:
- Recursively watches
SRC using 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.
Plugins
convertr supports external plugins — any executable named convertr-* found in PATH.
Plugin protocol
A plugin must implement capabilities and can implement conversion and process subcommands for the operations it advertises.
convertr-NAME capabilities
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 than 2 are 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 the process subcommand.
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-positive
quality — optional quality hint (0–100); higher is better for quality-aware routing
lossy — optional boolean telling convertr whether this edge loses information
metadata — optional string map for plugin-specific edge metadata
Operation fields:
name — operation ID, for example pdf.merge or image.resize
description — optional human-readable summary
params — optional parameter name to description map
v1 compatibility
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
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.
convertr-NAME process
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.
Writing a plugin
#!/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"
;;
esac
Name 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.
Progress reporting
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.
TUI
convertr tui
Starts 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.
b or Esc — go back.
q — quit.
Web UI
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:8080
Convert
Upload 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.
Backends
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.
Architecture
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)
Key invariants
- Atomic write — all output goes to
os.CreateTemp then os.Rename. Never written directly.
- TempDir per job —
os.MkdirTemp("", "convertr-*") per conversion chain, removed with defer os.RemoveAll.
- LibreOffice isolation — every soffice call gets
-env:UserInstallation=file:///tmp/convertr-lo-PID to allow parallel execution.
- Context propagation — all backends receive
ctx and use exec.CommandContext.
- Route policy parity: actual conversions and route previews use the same
speed, quality, avoid_lossy, and max_hops semantics.
- Registry isolation:
backend.Registry owns both conversion backends and processors per engine instance. DefaultRegistry and package-level wrappers preserve legacy init() registration behaviour for CLI and older integrations.
- Exit codes —
0 success · 1 error · 2 CLI usage · 3 no route · 4 partial batch · 5 missing backend.
Troubleshooting
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 logs
License
MIT — see LICENSE.