Skip to content

Command line (picmal-cli)

Run Picmal from the terminal — convert, compress, watch folders, and apply saved presets without opening the app.

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, 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
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
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 <geometry>Resize geometry, e.g. 1920x1080, 1920x, x1080, 50%.
--color-space <name>One of srgb, p3, adobergb, gray.
-s, --strip-metadataStrip EXIF/IPTC/XMP from output.
--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.

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 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.
-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.

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}'