Installation

Install pompelmi globally to get the pompelmi command available anywhere in your terminal:

npm
npm install -g pompelmi
yarn
yarn global add pompelmi
pnpm
pnpm add -g pompelmi

You can also run it without installing using npx — no global install required:

npx pompelmi scan ./uploads/document.pdf
The CLI requires ClamAV to be installed on your machine or reachable over TCP. For local scanning, install ClamAV (brew install clamav on macOS, apt-get install clamav on Debian/Ubuntu). For TCP mode, start a clamd instance — the fastest way is docker run -d -p 3310:3310 clamav/clamav:stable.

Quick start

Scan a file

npx pompelmi scan ./uploads/document.pdf

Scan a directory recursively

npx pompelmi scan ./uploads --recursive

Output as JSON for scripting

npx pompelmi scan ./uploads --json

Commands reference

Command Description
pompelmi scan <file|dir> Scan a file or directory for viruses. Exits with code 1 if a virus is found.
pompelmi watch <dir> Watch a directory and auto-scan files as they are created or modified. Runs until interrupted with Ctrl+C.
pompelmi version Print the installed version number and exit.
pompelmi help Print usage with all commands, options, and examples.

Options reference

Option Default Description
--recursive, -r true Scan directory recursively (all subdirectories).
--host <host> none Connect to clamd at this hostname or IP. If omitted, uses local clamscan.
--port <port> 3310 TCP port for clamd connection.
--socket <path> none UNIX socket path (e.g. /run/clamav/clamd.sock). Takes priority over --host/--port.
--timeout <ms> 15000 Connection timeout in milliseconds.
--retries <n> 0 Number of auto-retries on connection failure before giving up.
--json false Output results as a JSON object. Suppresses logo, colors, and progress bar. Ideal for scripting.
--quiet, -q false Only print infected files and the summary. Clean results are hidden.
--delete false After scanning, prompt for confirmation and delete infected files.
--report false Generate a self-contained HTML security dashboard after scanning. See Dashboard Demo Comparison.
--share-card false Generate a shareable SVG scan result card after scanning. See Share card.
--output <file> see note Output path for --report (default: pompelmi-report.html) or --share-card (default: pompelmi-scan-card.svg). File extension determines which output it applies to.
Local vs TCP mode: If neither --host, --port, nor --socket is provided, pompelmi spawns a local clamscan child process. This requires ClamAV to be installed on the machine. For Docker or CI environments, use --host / --port to point at a running clamd container.

Default output format

By default, results are printed using box-drawing characters with ANSI colours. Each file shows one of three states: CLEAN, INFECTED, or ERROR. For infected files the matched virus name is shown below:

┌──────────────────────────────────────────────┐
│  🍊 pompelmi scan results                    │
├──────────────────────────────────────────────┤
│  ✅ CLEAN     /uploads/document.pdf          │
│  🚨 INFECTED  /uploads/malware.exe           │
│     └─ Win.Malware.Agent-1234                │
│  ⚠️  ERROR    /uploads/encrypted.zip         │
├──────────────────────────────────────────────┤
│  Scanned: 3  • Infected: 1 • Time: 1.2s     │
└──────────────────────────────────────────────┘

For directory scans, a live progress bar is displayed on a single line (overwritten with \r) while scanning is in progress:

  Scanning... [████████░░░░░░░░░░░░] 40% • 20/50 files • 1 infected

JSON output (--json)

When --json is passed, pompelmi prints a single JSON object to stdout and exits. No logo, no colours, no progress bar — ideal for piping into jq or other tools.

{
  "scanned": 3,
  "infected": 1,
  "errors": 0,
  "time": 1.2,
  "results": [
    { "file": "/uploads/document.pdf", "verdict": "clean" },
    { "file": "/uploads/malware.exe",  "verdict": "infected",
      "viruses": ["Win.Malware.Agent-1234"] },
    { "file": "/uploads/encrypted.zip", "verdict": "error" }
  ]
}
Field Type Description
scanned number Total number of files scanned.
infected number Number of files with a virus detected.
errors number Number of files that could not be scanned.
time number Total scan time in seconds (rounded to 1 decimal place).
results[].file string Absolute path to the scanned file.
results[].verdict "clean" | "infected" | "error" Scan result for this file.
results[].viruses string[] (optional) List of matched virus names. Present only when verdict is "infected".

Exit codes

Code Meaning
0 All files are clean. No threats detected.
1 At least one virus was found.
2 At least one file could not be scanned (error).
3 clamd was unreachable (connection refused or timeout).

Exit codes make it easy to integrate pompelmi into shell scripts and CI pipelines. A non-zero exit causes set -e scripts and most CI systems to fail the step automatically.

HTML report (--report)

The --report flag generates a self-contained HTML security dashboard after scanning. The file contains no external dependencies and can be opened in any browser, archived, or attached to a ticket.

npx pompelmi scan ./uploads --report

Saves pompelmi-report.html in the current directory. Use --output to change the path:

npx pompelmi scan ./uploads --report --output /tmp/security-$(date +%Y%m%d).html

The report includes summary stats, a colour-coded status banner, a full file table with verdict badges, an infected files section, and scan metadata. It supports dark mode via prefers-color-scheme and is print-friendly. See the Dashboard documentation for the full API reference.

Share card (--share-card)

The --share-card flag generates a compact SVG card showing the scan summary. Suitable for embedding in a GitHub README or sharing on social media.

npx pompelmi scan ./uploads --share-card

Saves pompelmi-scan-card.svg in the current directory. Custom path:

npx pompelmi scan ./uploads --share-card --output docs/scan-card.svg

The card shows: total files scanned, clean count, infected count, date, and pompelmi version.

Examples

1. Scan a single file (local clamscan)

pompelmi scan ./uploads/resume.pdf

2. Scan a directory recursively

pompelmi scan ./uploads --recursive

3. Scan via clamd over TCP (Docker sidecar)

pompelmi scan ./uploads \
  --host 127.0.0.1 \
  --port 3310 \
  --recursive

4. Scan via UNIX socket (lower latency)

pompelmi scan ./uploads --socket /run/clamav/clamd.sock

5. JSON output — pipe into jq

pompelmi scan ./uploads --json | jq '.infected'

6. Quiet mode — only show infected files

pompelmi scan ./uploads --quiet

7. Delete infected files interactively

pompelmi scan ./uploads --recursive --delete

8. Watch a directory for incoming uploads

pompelmi watch ./uploads --host 127.0.0.1 --port 3310
Use npx pompelmi instead of pompelmi if you have not installed it globally. npx downloads the latest version on the fly — no global install needed.

Shell script integration

Because pompelmi uses standard exit codes, it integrates cleanly into any shell script. The following example scans an incoming upload directory, posts a Slack message if a virus is found, and exits non-zero so the calling CI job fails:

#!/usr/bin/env bash
set -euo pipefail

UPLOAD_DIR="${1:?Usage: scan-uploads.sh <dir>}"
CLAMD_HOST="${CLAMAV_HOST:-127.0.0.1}"
CLAMD_PORT="${CLAMAV_PORT:-3310}"

echo "Scanning $UPLOAD_DIR..."

RESULT=$(pompelmi scan "$UPLOAD_DIR" \
  --host "$CLAMD_HOST" \
  --port "$CLAMD_PORT" \
  --recursive \
  --json)

INFECTED=$(echo "$RESULT" | jq -r '.infected')
SCANNED=$(echo  "$RESULT" | jq -r '.scanned')

echo "Scanned: $SCANNED — Infected: $INFECTED"

if [ "$INFECTED" -gt 0 ]; then
  echo "VIRUS DETECTED — failing build."
  # Post a Slack message, open a ticket, etc.
  exit 1
fi

echo "All files clean."

GitHub Actions example

Scan your repository on every pull request. Uses the pompelmi GitHub Action GitHub App under the hood, but you can also call the CLI directly in a run step:

- name: Scan uploads with pompelmi
  run: |
    npx pompelmi scan ./uploads \
      --host localhost \
      --port 3310 \
      --json \
      | tee scan-results.json
    jq -e '.infected == 0' scan-results.json
Combine --json with jq -e to fail the CI step when any virus is found, while still capturing the full results in an artifact.