| name | mm-cli-skill |
| description | Use the mm CLI to index, explore, query, and extract content from multimodal directories containing images, videos, PDFs, code, and other files. Triggers: exploring a directory's contents, listing/finding files by type or size, extracting text from PDFs, getting image metadata, searching across file contents, counting tokens, viewing directory trees, extracting PDF page mosaics, video keyframe extraction, 'what files are in this folder', 'find all images', 'show me the PDFs', 'how much storage do videos use', 'extract text from this PDF', 'search documents for X', 'analyze this directory', 'how many tokens', 'show the tree'.
|
mm CLI
mm is a high-performance multimodal context management CLI. It indexes directories instantly (~60ms for 700 files), then exposes Unix-style commands for exploring, querying, and extracting content from images, videos, PDFs, code, and other files.
Always use --format json for machine-readable output when parsing results programmatically.
Installation
pip install mm-ctx
curl -LsSf https://vlm-run.github.io/mm/install/install.sh | sh
irm https://vlm-run.github.io/mm/install/install.ps1 | iex
Commands
| Command | Purpose |
|---|
find | Locate/list files by name/kind/ext/size, tabular listing, tree view, schema |
cat | Content extraction (auto-detected by file type × mode) |
grep | Content search — text and semantic (via embeddings) |
wc | Count files, bytes, lines, tokens |
bench | Benchmark suite with statistical analysis |
config | Extraction mode settings (show, init, set, reset-db, reset-profiles, reset) |
profile | Manage LLM provider profiles (list, add, update, use, remove) |
Workflow
- Start with
mm find <dir> --tree --depth 1 to see the directory structure.
- Use
mm wc <dir> --by-kind to estimate token counts for LLM context budgeting.
- Explore with
find, grep, cat as needed.
- Use
mm cat <file> -m accurate for LLM-powered descriptions.
find — locate files, tabular listing, tree view, schema
mm find <dir> --kind image
mm find <dir> --kind video
mm find <dir> --kind document
mm find <dir> --kind audio
mm find <dir> --name "test_.*\.py"
mm find <dir> -n config
mm find <dir> --ext .png,.webp
mm find <dir> --min-size 1mb --max-size 10mb
mm find <dir> --kind image --limit 5 --format json
mm find <dir> --sort size --reverse --limit 10
~63ms via Rust fast path. Piped output is one path per line. --format json returns full metadata.
mm find <dir>
mm find <dir> --columns name,kind,size --limit 10
mm find <dir> --sort size --reverse --format json
mm find <dir> --tree
mm find <dir> --tree --depth 1
mm find <dir> --tree --kind image
mm find <dir> --tree --format json
mm find <dir> --schema
mm find <dir> --schema --format json
mm find <dir> --no-ignore
mm find <dir> --no-ignore --kind video
mm find <dir> --no-ignore --tree
Columns in the files table:
| Column | Type | Description |
|---|
| path | string | Relative path from scan root |
| name | string | File name with extension |
| stem | string | File name without extension |
| ext | string | Extension including dot (.png, .pdf) |
| size | uint64 | File size in bytes |
| modified | timestamp | Last modification time |
| created | timestamp | Creation time |
| mime | string | MIME type (image/png, application/pdf) |
| kind | string | image, video, document, code, audio, data, config, text, other |
| is_binary | bool | Whether file is binary |
| depth | uint16 | Directory depth (0 = top-level) |
| parent | string | Parent directory path |
| width | uint32 | Pixel width (images from header, videos via native parsing). Null for non-media. |
| height | uint32 | Pixel height (images from header, videos via native parsing). Null for non-media. |
cat — content extraction (pipeline-driven)
Behaviour is auto-detected from file type. Default --mode fast runs local extraction (no LLM). Use -m accurate for LLM-powered descriptions.
mm cat <file>
mm cat photo.png
mm cat video.mp4
mm cat paper.pdf
mm cat photo.png -m accurate
mm cat video.mp4 -m accurate
mm cat audio.mp3 -m accurate
mm cat paper.pdf -m accurate
mm cat <file> -n 20
mm cat <file> -n -10
mm cat <file> --no-cache
mm cat <file> --format json
Fast mode behavior by file type (<100ms target):
- PDF (.pdf): text extraction via pypdfium2. Scanned/image-only PDFs return empty.
- Document (.docx, .pptx): text extraction.
- Image (.png/.jpg/.webp/.gif/.bmp/.tiff/.svg): dimensions, MIME, xxh3 hash, EXIF data.
- Video (.mp4/.mkv/.webm/.avi/.mov): resolution, duration, FPS, codecs (metadata only, no ffmpeg).
- Audio (.mp3/.wav/.flac/.aac/.ogg/.m4a): duration, codec, bitrate (metadata only).
cat -p — named encoders and pipeline YAMLs
The -p / --pipeline flag accepts either a registered encoder name or a YAML file path.
mm cat photo.png -p image-resize
mm cat photo.png -p image-tile
mm cat video.mp4 -p video-frame-sample
mm cat video.mp4 -p video-chunk
mm cat doc.pdf -p document-rasterize
mm cat doc.pdf -p document-rasterize-text
mm cat photo.png -p custom-pipeline.yaml
mm cat *.jpg *.mp4 -p image.yaml -p video.yaml
mm cat --list-pipelines
Built-in encoders
Use either the bare name or the kind-prefixed display name.
| Name | Media | Description |
|---|
image-resize | image | Default. Fit to 1024px bounding box |
image-tile | image | Resized overview + tile crops in one Message |
video-frame-sample | video | Extract frames at fps (requires ffmpeg) |
video-frames-transcript | video | Frames + Whisper transcript (accurate mode default) |
video-chunk | video | Chunk into time-based segments with overlap |
video-mosaic | video | Build mosaic grids from sampled frames |
video-shot-frames | video | Scene detection → representative frames per shot |
video-shot-mosaic | video | Scene detection → mosaic grid per shot |
video-gemini | video | Pass video file as a Gemini Part |
video-gemini-chunked | video | Chunk video into Gemini Parts |
audio-transcribe | audio | Transcribe audio via Whisper (fast/accurate default) |
audio-gemini | audio | Pass audio file as a Gemini Part |
document-page-text | document | Extract text per page from PDF/DOCX/PPTX |
document-rasterize | document | Render PDF pages as images (requires pypdfium2) |
document-rasterize-text | document | Rasterize + extract text, interleaved |
document-gemini | document | Pass document file as a Gemini Part |
Writing custom encoders
Create a .py file in python/mm/encoders/image/, python/mm/encoders/video/ (auto-discovered) or ~/.config/mm/encoders/. The name is optional — it defaults to the function name with underscores replaced by hyphens:
from pathlib import Path
from mm.encoders import register_encoder
@register_encoder(media_types=("image",))
def my_custom(path: Path, **kw):
"""Registered as 'my-custom' (auto-named from function)."""
import base64, io
from PIL import Image
img = Image.open(path)
img.thumbnail((1024, 1024))
buf = io.BytesIO()
img.save(buf, "JPEG", quality=90)
b64 = base64.b64encode(buf.getvalue()).decode()
yield {"role": "user", "content": [
{"type": "image_url", "image_url": {"url": f"data:image/jpeg;base64,{b64}"}}
]}
Python API
from mm import process_image, process_image_tiled, process_video, process_document
from pathlib import Path
msg = process_image(Path("photo.png"), max_width=1024)
tiles = list(process_image_tiled(Path("scan.png"), tile_size=1024))
chunks = list(process_video(Path("video.mp4")))
pages = list(process_document(Path("doc.pdf")))
from mm import Context
ctx = Context("~/data")
messages = ctx.encode("photo.png", strategy="resize")
cat — pipeline overrides
Pipelines are 2-stage YAMLs: encode (convert to LLM-ready parts) → generate (LLM call). Key parameters can be overridden from the CLI.
Encode overrides (--encode.*)
mm cat photo.png -m accurate --encode.strategy image-tile
mm cat photo.png -m accurate --encode.pyfunc ~/my_filter.py
Generate overrides (--generate.*)
mm cat photo.png -m accurate --generate.max-tokens 1024
mm cat photo.png -m accurate --generate.temperature 0.5
mm cat photo.png -m accurate --generate.prompt "List 3 main objects in this image."
mm cat photo.png -m accurate --generate.json-mode true
Combining overrides
mm cat photo.png -m accurate \
--encode.strategy image-tile \
--generate.max-tokens 512 \
--generate.prompt "Analyze this architecture diagram."
cat -p — explicit pipeline YAML
Load custom pipeline configurations from YAML files. The YAML's kind field dispatches the pipeline to the correct media type. The generate field is optional — omit it for encode-only pipelines.
Single pipeline
kind: image
mode: accurate
encode:
strategy: resize
max_width: 512
generate:
prompt: "What is in this image? One sentence only."
max_tokens: 64
mm cat photo.png -p custom-image.yaml
Encode-only pipeline (no LLM)
kind: document
mode: fast
encode:
strategy: null
Multi-document YAML
kind: image
mode: accurate
encode:
strategy: image-tile
max_width: 2048
generate:
prompt: "Describe this image in detail."
max_tokens: 512
---
kind: video
mode: accurate
encode:
mosaic_tile: "8x6"
mosaic_count: 2
frame_selection: scene
generate:
prompt: "Summarize this video."
max_tokens: 1024
mm cat *.jpg *.mp4 -p multi-pipeline.yaml
CLI overrides layer on top of -p
mm cat photo.png -p my-pipeline.yaml --generate.max-tokens 128
TOML pipeline path overrides
Override default pipeline paths in ~/.config/mm/mm.toml:
[pipelines]
image.fast = "~/.config/mm/pipelines/image/fast.yaml"
video.accurate = "/path/to/my-video-accurate.yaml"
cat — custom Python transforms (pyfunc)
The --encode.pyfunc flag runs a custom Python transform on the encoded content parts before they are sent to the LLM.
File-based pyfunc
mm cat photo.png -m accurate --encode.pyfunc ~/my_transform.py
Pyfunc in pipeline YAML
kind: image
mode: accurate
encode:
strategy: resize
max_width: 512
pyfunc: ~/my_transforms/filter.py
generate:
prompt: "Analyze this image."
max_tokens: 128
Inline def syntax in YAML:
encode:
pyfunc: |
def transform(parts, context):
return [p for p in parts if p.get("type") == "image_url"]
wc — count files, bytes, lines, tokens
mm wc <dir>
mm wc <dir> --by-kind
mm wc <dir> --kind document
mm wc <dir> --format json
Estimates LLM tokens (~chars/4 for text, tile-based for images). ~65ms.
grep — content search (text + semantic)
mm grep "pattern" <dir>
mm grep "attention" <dir> --kind document
mm grep "TODO" <dir> --kind code
mm grep "invoice" <dir> --kind document --format json
mm grep "error" <dir> -C 2
mm grep "invoice" <dir> --count
mm grep "Quantum Phase" <dir> -i
mm grep "TODO" <dir> --ignore-case --kind code
mm grep "secret" <dir> --no-ignore
mm grep "financial projections" <dir> -s
mm grep "architecture overview" <dir> -s --format json
mm grep "revenue forecast" <dir> -s --index
Warning: grep runs extraction on every matching file. On large document directories (500+ PDFs), this can take minutes. Prefer --kind code or --kind text for fast text searches.
bench — benchmark suite
mm bench <dir>
mm bench <dir> --rounds 5
mm bench <dir> --mode accurate
mm bench <dir> --format json
config — extraction mode settings
mm config show
mm config init
mm config init --force
mm config set mode.fast.whisper_model tiny
mm config set mode.accurate.beam_size 5
mm config reset-db
mm config reset-profiles
mm config reset
profile — LLM provider management
Provider settings are managed through profiles stored in ~/.config/mm/mm.toml.
mm profile list
mm profile add openrouter --base-url https://openrouter.ai/api/v1 --model vlm-1
mm profile update openrouter --model gemma4:e2b
mm profile use openrouter
mm profile remove openrouter
Per-command profile selection:
mm --profile openrouter cat photo.png -m accurate
MM_PROFILE=openrouter mm cat photo.png -m accurate
Output formats
- TTY: Rich formatted tables/panels (human-friendly).
- Piped / non-TTY: plain TSV/text or one-path-per-line (machine-readable, no ANSI codes).
--format json: JSON output. Always use this when parsing results programmatically.
--format tsv: Tab-separated values. Maximum token efficiency.
--format csv: Comma-separated values.
--format dataset-jsonl: JSONL for dataset export.
--format dataset-hf: HuggingFace Datasets format.
Pipe composability
mm find <dir> --kind image | mm cat
mm find <dir> --kind document --min-size 10mb | wc -l
mm find <dir> --kind video --format json | jq '.[].name'
Tips
- All metadata commands (
find, wc) run in ~60ms via the Rust fast path.
- Start with
find --tree --depth 1 then wc --by-kind for the fastest directory overview.
- Use
--format json when you need to parse output programmatically.
find returns paths only when piped, else it returns full metadata rows.
- For PDFs,
cat extracts text in fast mode; if empty, the PDF contains scanned images only.
- For videos,
mm cat video.mp4 -m accurate auto-generates keyframe mosaics and sends to LLM.
- Use
--mode fast for quick metadata/text extraction (default), --mode accurate for LLM descriptions.
- Use
--no-cache with -m accurate to force a fresh LLM call.
- Use
-p to load custom pipeline YAMLs or named encoders; CLI overrides layer on top.
- Use
--encode.pyfunc to inject custom Python transforms.
- Use
--list-pipelines to see all available encoders and built-in pipelines.