Skip to content

Command line (picmal-cli)

Picmal ships with a command-line companion, picmal-cli, bundled inside the app. Same conversion engine as the UI, so the output is identical — you just get flags, JSON, and exit codes you can drop into a script.

From the menu bar, choose Picmal → Install Command Line Tool…. Picmal asks for your administrator password and creates a symlink at /usr/local/bin/picmal-cli, which is on the default macOS PATH for both bash and zsh. After that, picmal-cli works from any terminal.

Install Command Line Tool menu item in Picmal

To remove it later, use Picmal → Uninstall Command Line Tool…. Only the symlink is removed; the app stays put.

If you’d rather invoke the binary directly without installing it, it lives inside the app bundle:

/Applications/Picmal.app/Contents/MacOS/picmal-cli

convert, compress, combine, combine-videos, split-pdf, images-to-pdf, merge-audio, split-chapters, remove-background, and watch need a valid Picmal license. The read-only commands (presets, formats, license-status, version) work without one.

picmal-cli <subcommand> [options]
SubcommandWhat it does
convertConvert files to a target format
compressCompress files keeping the same format
combineCombine two or more PDFs into one
split-pdfSplit a PDF into one document per page range
images-to-pdfCreate a multi-page PDF from images
merge-audioMerge two or more audio files into one
combine-videosCombine two or more videos into one
split-chaptersSplit a chaptered audiobook into per-chapter files
remove-backgroundCut the background out of images (transparent PNG)
presetsList or inspect saved presets (built-in + custom)
formatsList supported formats by category
watchAdd, list, and remove watched folders
license-statusCheck whether the host app is licensed
versionPrint the bundled app version

These flags work on every subcommand:

FlagDescription
--jsonForce NDJSON output to stdout (one JSON object per line). Auto-on when piped.
--textForce human-readable output even when piped.
-q, --quietSuppress per-file status lines (final summary still emitted). Where available.
--verbosePass underlying tool stderr through. Useful for debugging.

Convert one or more files to a target format.

picmal-cli convert -i photo.heic -f jpg
picmal-cli convert -i ~/Pics -f webp -o ~/out --quality 80
picmal-cli convert -i a.png b.png -f avif --strip-metadata
picmal-cli convert -i clip.mov -f mp4 --quality 70
picmal-cli convert -i photo.jpg -f jpg --resize 100kB
picmal-cli convert -i photo.jpg -f jpg --copyright "© 2026 Jane Doe" --artist "Jane Doe"
picmal-cli convert -i icon.icns -f png -o ~/out
cat list.txt | picmal-cli convert -i - -f webp -o ~/out
FlagDescription
-i, --input <paths>One or more input paths. Use - to read newline-separated paths from stdin.
-f, --format <ext>Target format extension (e.g. jpg, webp, mp4, mp3).
-o, --output <path>Output file or directory. Defaults to alongside each input.
-w, --overwriteOverwrite existing outputs.
--quality <n>Quality 0–100. Format-specific defaults if omitted. Overrides --preset.
--preset <name>Apply a saved preset by name (see Presets).
--resize <value>Resize geometry (1920x1080, 1920x, x1080, 50%) or a target file size (100kB, 2MB) — images are scaled down to fit the budget.
--color-space <name>One of srgb, p3, adobergb, gray, cmyk.
--metadata <mode>Preset: stripall, essential, or keepall. See Metadata.
--keep <categories>Comma-separated categories to keep; everything else is stripped.
--strip <categories>Comma-separated categories to strip; everything else is kept.
-s, --strip-metadataStrip EXIF/IPTC/XMP from output (alias for --metadata stripall).
--copyright <text>Copyright notice to embed (EXIF, IPTC, and XMP). Written even with --strip-metadata.
--artist <text>Artist / creator name to embed (EXIF Artist, IPTC byline, XMP creator).
--credit <text>Credit line to embed (IPTC credit, XMP credit).
--contact <text>Contact or URL to embed (XMP rights web statement, IPTC contact).
--pdf-dpi <n>DPI for PDF rasterization (also tags output density on raster outputs).
--watermark <path>Watermark image path.
--watermark-opacity <n>0.0–1.0. Default 0.5.
--watermark-positiontl, tr, bl, br, or center. Default br.

The copyright flags are independent of --strip-metadata: pass both to strip a photo’s camera/GPS data while still stamping your own ownership tags onto the output. Empty flags are skipped; values you pass always overwrite any existing tag.

An .icns input fans out to one output per embedded size, named <name>-<W>x<H>.<ext> — the same convention split-pdf uses for pages. --quality, --resize, and --watermark apply to each size. See Apple icon files.

The metadata flags mirror the app’s Metadata settings. Use a preset, or take per-category control with --keep / --strip:

picmal-cli convert -i photo.jpg -f jpg --metadata essential
picmal-cli convert -i photo.jpg -f jpg --keep copyright,colorProfile
picmal-cli convert -i photo.jpg -f jpg --strip location,identifiers

Categories: location, camera, dateTime, copyright, colorProfile, orientation, identifiers, thumbnail. Friendly aliases work too — gps, icc, date, serial, lens, thumb. --keep lists what survives; --strip lists what comes off and keeps the rest. Precedence is --keep > --strip > --metadata > --strip-metadata.

These flags work the same on compress and watch add.

Compress one or more files keeping the same format. Output defaults to <basename>_compressed.<ext> next to each input.

picmal-cli compress -i big.jpg --quality 70
picmal-cli compress -i ~/Pics -o ~/out --strip-metadata
picmal-cli compress -i clip.mp4 --quality 60 -o clip-small.mp4
picmal-cli compress -i portfolio.jpg --copyright "© 2026 My Studio" --contact "hello@example.com"
picmal-cli compress -i podcast.mp3 --preset "Voice memo"
FlagDescription
-i, --input <paths>One or more input paths. Use - for stdin.
-o, --output <path>Output file or directory.
--quality <n>Quality 0–100. Format-specific defaults if omitted. Overrides --preset.
--preset <name>Apply a saved preset by name (see Presets).
-s, --strip-metadataStrip EXIF/IPTC/XMP from output.
--copyright <text>Copyright notice to embed (EXIF, IPTC, and XMP). Written even with --strip-metadata.
--artist <text>Artist / creator name to embed.
--credit <text>Credit line to embed.
--contact <text>Contact or URL to embed.
-w, --overwriteOverwrite existing outputs.

If your --preset is an audio or video preset and you don’t pass --output, picmal-cli picks the right container for you (Opus → .opus, AAC → .m4a, FLAC → .flac, VP9 → .webm). Pass --output if you want a specific path.

Combine two or more PDFs into a single document. Inputs are merged in the order you pass them.

picmal-cli combine a.pdf b.pdf
picmal-cli combine chapter*.pdf -o book.pdf
FlagDescription
<inputs...>PDF files to combine, in page order (positional, at least two).
-o, --output <path>Output file or directory. Default: <first basename> (combined).pdf next to the first input.
-w, --overwriteOverwrite an existing output file.

Password-protected inputs are refused (exit 11) — unlock them first. Uses PDFKit, macOS’s native PDF engine, so pages are copied losslessly.

Split a PDF into one document per page range. Each comma-separated range in --pages becomes its own file; with no --pages, every page is split into a separate file.

picmal-cli split-pdf book.pdf --pages 1-3,5,8-
picmal-cli split-pdf book.pdf
picmal-cli split-pdf *.pdf --pages 1-2 -o out/
FlagDescription
<inputs...>PDF files to split (positional, at least one). Each is split by the same ranges.
--pages <ranges>Page ranges, 1-based: N, N-M, N- (N to end), -M (start to M), comma-separated. Default: one file per page.
-o, --output <dir>Output directory. Default: next to each input, as <basename>-1.pdf, <basename>-2.pdf, …
-w, --overwriteOverwrite existing output files.

Like combine, it uses PDFKit and copies pages losslessly; password-protected inputs are refused — unlock them first.

Create a multi-page PDF from images, one image per page, in the order you pass them. Accepts the formats macOS decodes natively (jpg, png, heic, tiff, gif, bmp, webp, avif, jp2, jxl). EXIF orientation is applied, so sideways phone photos come out upright.

picmal-cli images-to-pdf scan1.jpg scan2.jpg
picmal-cli images-to-pdf *.png --page-size a4 --quality 70 -o album.pdf
picmal-cli images-to-pdf report.png --password secret
FlagDescription
<inputs...>Image files, in page order (positional, at least one).
--page-size <size>fit (page wraps each image, default), a4, letter, or <W>x<H>mm (e.g. 210x297mm). Fixed sizes auto-rotate to each image’s orientation.
--quality <n>JPEG quality for embedded images, 40–100. Default 85.
--password <text>Password required to open the PDF.
-o, --output <path>Output file or directory. Default: <first basename>.pdf next to the first input.
-w, --overwriteOverwrite an existing output file.

Turn a single image into ready-to-ship app icons: a macOS .icns, a Windows .ico, and an iOS .appiconset you can drag into Xcode. Pass no format flag to generate all three. A non-square source is fit into the square without cropping, and the iOS icon is flattened onto an opaque background.

picmal-cli app-icon logo.png # all three formats
picmal-cli app-icon logo.png --macos # just .icns
picmal-cli app-icon logo.png --ios --windows -o icons/
FlagDescription
<input>Source image to generate icons from (positional). Use a square image ≥ 1024×1024 for crisp results.
--macosGenerate the macOS .icns.
--windowsGenerate the Windows .ico.
--iosGenerate the iOS .appiconset.
-o, --output <path>Output directory. Default: a <name> App Icons folder next to the source.
-w, --overwriteOverwrite existing output files.

Pass any combination of --macos, --windows, and --ios to pick formats; with none, all three are generated. See App Icons.

Cut the background out of one or more images and write each subject to a transparent PNG. It runs entirely on your Mac using Apple’s Vision framework — nothing is uploaded.

picmal-cli remove-background photo.jpg
picmal-cli remove-background *.png -o cutouts/
FlagDescription
<inputs...>Source image(s) to remove the background from (positional).
-o, --output <path>Output directory. Default: <name>_removed.png next to each source.
-w, --overwriteOverwrite existing output files.
--jsonForce JSON output.

An image with no clear subject is skipped and reported, and the rest of the batch still runs. See Remove Background.

Join two or more audio files into one, in the order you pass them. When every input shares the same codec, sample rate, and channels the streams are copied losslessly; mix formats and they’re re-encoded to the output format instead.

picmal-cli merge-audio part1.mp3 part2.mp3
picmal-cli merge-audio intro.wav track.wav outro.wav -o full.wav
FlagDescription
<inputs...>Audio files to merge, in order (positional, at least two).
-o, --output <path>Output file or directory. The extension sets the format. Default: <first basename> (merged).<ext> next to the first input, in that file’s format.
-w, --overwriteOverwrite an existing output file.

Uses the same bundled ffmpeg as Convert and Compress. Decode-only formats (like APE) are refused as output targets — pick a format Picmal can write.

Join two or more videos into one, in the order you pass them. When every input shares the same codec, resolution, and frame rate the streams are copied losslessly; mix formats or sizes and each clip is scaled onto the first one’s frame and re-encoded to match.

picmal-cli combine-videos clip1.mp4 clip2.mp4
picmal-cli combine-videos intro.mov main.mp4 outro.webm -o full.mp4
FlagDescription
<inputs...>Video files to combine, in order (positional, at least two).
-o, --output <path>Output file or directory. The extension sets the format. Default: <first basename> (combined).<ext> next to the first input, in that file’s format.
-w, --overwriteOverwrite an existing output file.

Uses the same bundled ffmpeg as Convert and Compress. H.264 for MP4/MOV/MKV, VP9 for WebM; on Apple Silicon it encodes in hardware. Decode-only formats are refused as output targets — pick a format Picmal can write.

Split a chaptered audiobook into one file per chapter. Picmal reads the chapter markers in an M4B/M4A audiobook (or any audio file that carries them) and writes one file per chapter, tagged with its title and track number. Pass several files to split them all.

picmal-cli split-chapters book.m4b # one m4b per chapter
picmal-cli split-chapters book.m4b --format mp3 # convert to mp3
picmal-cli split-chapters *.m4b --format mp3 -o out/
FlagDescription
<inputs...>Audiobook file(s) to split (positional).
--format <fmt>Output format, e.g. mp3. Default: keep the input’s format.
-o, --output <dir>Output directory. Default: next to each input.
-w, --overwriteOverwrite existing output files.

Outputs are named <book> - 01 - <chapter title>.<ext>. Files with no chapter markers are reported and skipped.

Browse the presets you’ve made in the app, plus the built-ins that ship with Picmal. Read-only — make new ones in the app.

picmal-cli presets list
picmal-cli presets list --kind image
picmal-cli presets show "Balanced"
FlagDescription
--kind <image|audio|video>Only show presets of this kind.

Text mode groups results under Built-in and Custom. JSON mode emits a presets array where each row carries id, name, kind, source ("built-in" or "custom"), and description.

picmal-cli presets show "Maximum quality"

Takes a preset name (case-insensitive) and prints everything it contains:

  • Image presets: per-format quality (jpegQuality, webpQuality, pngLevel, heicQuality, …) plus any resize / color / DPI sections.
  • Audio presets: codec, bitrate mode, target bitrate, sample rate, bit depth, mono flag.
  • Video presets: codec, CRF, encoding speed.

If a name exists across kinds (the built-in “Maximum quality” exists for image, audio, and video), show prints every match. Custom presets win over built-ins of the same name within the same kind.

Pass --preset NAME to either subcommand. The kind is matched against the input’s category (image / audio / video), and the preset’s values feed into the engine:

  • Image preset → per-output-format quality, resize geometry, target color space, DPI.
  • Audio preset → ffmpeg codec, bitrate, mono flag, sample rate, bit depth.
  • Video preset → ffmpeg codec, CRF, encoding speed. Input audio is stream-copied.

Explicit flags always beat preset values. So --preset "Maximum compression" --quality 95 gives you a high-quality output, not a tiny one.

You can’t create custom presets from the CLI. Make them in the app first (see image compression and audio and video).

List the formats Picmal can read and write, grouped by category.

picmal-cli formats
picmal-cli formats --category image
picmal-cli formats --json | jq '.formats.image'
FlagDescription
--category <image|audio|video|document>Restrict output to one category.

Manage Picmal’s watched folders from the terminal. The host app does the actual processing; picmal-cli watch only edits the list.

picmal-cli watch add ~/Screenshots --format webp --strip-metadata
picmal-cli watch list
picmal-cli watch remove ~/Screenshots --yes
FlagDescription
<folder>Folder path to watch (positional).
-o, --output <dir>Output destination. Defaults to the watched folder.
-f, --format <ext>Target format extension. Implies convert mode.
-r, --recurseWatch subfolders recursively.
-s, --strip-metadataStrip metadata from processed files.

Lists every configured watched folder, with mode, format, output, and UUID.

picmal-cli watch remove <uuid-or-path> [--yes]

Pass either the folder path or the UUID printed by watch list. --yes (-y) skips the confirmation prompt, which is required when stdout isn’t a terminal.

Prints whether the host app is licensed.

picmal-cli license-status
picmal-cli license-status --json

Prints the bundled app version.

picmal-cli version

picmal-cli uses stable exit codes so scripts can branch on them:

CodeMeaning
0Success.
1Generic failure (e.g. underlying tool reported a non-zero exit).
2Usage error — bad arguments or missing required flag.
3Unlicensed.
4Input missing.
5Unsupported format.
7Cancelled (Ctrl+C).
8Output write failure (output already exists without -w, disk full, …).
9Partial success: some files succeeded, some failed in a batch.
10A bundled tool (magick, ffmpeg) couldn’t be located. Reinstall fixes it.
11Input is corrupt or cannot be decoded.

When stdout isn’t a terminal (piped or redirected), picmal-cli emits NDJSON: one JSON object per line. Force it with --json, force text with --text.

Every event carries an event discriminator and an RFC 3339 ts. Successful file outputs go to stdout; errors, warnings, and progress go to stderr. The schema version is 1 and is included in the started event so consumers can detect breakages early.

Example pipeline:

picmal-cli compress -i ~/Pics -o ~/out --preset Balanced --json \
| jq -c 'select(.event=="file") | {input, output, savedPct: (1 - .bytesOut/.bytesIn)*100}'