SuperAPI AI Directory

vhscli

Design & creation

View repository

Updated 10-15 21:51

About this skill

vhscli

vhscli is a command-line tool for multimodal AI: chat about text/images/video/pdfs, or generate images, videos, and audio from prompts. It's a thin client — auth, uploads, and model execution all happen server-side, so users don't store any provider API keys locally.

Run vhscli --help or vhscli <command> --help to see current help — the CLI is the source of truth.

Invocation

Always run via npx @getvhs/vhscli@latest so you pick up the newest models, flags, and fixes. Don't pin a version, and don't call a bare vhscli binary even if one is on PATH — it may be stale.

npx @getvhs/vhscli@latest <command> ...

Throughout this doc, commands are written as vhscli ... for readability — substitute npx @getvhs/vhscli@latest ... when running.

Requires Node.js ≥ 24.

Top-level

vhscli [-v|--version] [-h|--help]
vhscli <command> [options] ...
  • -v, --version — print version (only when no command is given)
  • -h, --help — show help (works on root and every subcommand)

Commands:

  • login — log in with google (opens browser; saves session to ~/.vhs/session.json)
  • logout — log out and delete local access tokens
  • whoami [--json] — print the logged-in user's name and email (local read; no network). plain: Name <email>; --json: one event line
  • account [--json] — email and balance (hits the backend). plain: email, then balance: $X.XX
  • models — list available models
  • generate <model> <prompt> -o <path> — generate an image, video, or audio, wait, and save it (-o is required)
  • submit <model> <prompt> -o <path> — submit the same task as generate but exit immediately, printing the task id; finish later with resume
  • chat <prompt> — chat with seed-2.0 / a2:seed-2-pro (text, image, video, or pdf input)
  • resume <task_id> -o <path> — wait for one task and write its output
  • poll - — watch task ids from stdin; print the first to finish, then exit (writes no files)
  • save <task_id> -o <path> — write an already-finished task's output (never waits)

The CLI stores nothing locally: a task lives in the VHS backend and the task id is the only handle to it. There is no project, no database, no config — see "How a task is tracked" below.

--json on generate / submit / resume / save / poll / whoami / account puts NDJSON events on stdout and reroutes ordinary logs to stderr (see "--json" below).

Auth

Assume auth is already configured. If a command fails with an auth error, run vhscli login to open a browser for Google OAuth. Do NOT run vhscli login preemptively — it requires interactive browser login.

Models

  • Chat / understand (text / image / video / pdf): seed-2.0 (a2:seed-2-pro under vhscli chat)
  • Generate images: seedream-5 (default), seedream-5-pro, nano-banana-2, nano-banana-pro, gpt-image-2 — under vhscli generate
  • Generate video: seedance-2 (up to 4k), seedance-2.5 (up to 720p) — under vhscli generate
  • Generate audio: seed-audio-1 — under vhscli generate

Prompt guides

Before you invoke vhscli generate (or do non-trivial understanding with vhscli chat), Read the matching prompt guide first and shape the prompt around it. The guides are concise, model-specific references distilled from each provider's docs — formulas, what to lead with, what works, what fails. Wording that's great for one model often underperforms on another, so don't skip this.

Model(s) Guide file (Read before prompting)
seed-2.0 (used by vhscli chat) prompt_guide/seed-2.txt
seedream-5, seedream-5-pro prompt_guide/seedream.txt
nano-banana-2, nano-banana-pro prompt_guide/nano-banana.txt
seedance-2, seedance-2.5 prompt_guide/seedance-2.txt
gpt-image-2 prompt_guide/gpt-image-2.txt

Trigger: any time the user asks for output from one of these models, Read its guide before building the prompt. For trivial chat (plain text Q&A with no media) you can skip seed-2.txt.

Stdin prompts

Every command that takes a prompt also accepts - as the prompt, meaning "read from stdin":

cat my_prompt.txt | vhscli generate nano-banana-pro - -o out.png
echo "what is this?" | vhscli chat - -i photo.jpg

vhscli chat — chat about text, images, video, or pdfs

vhscli chat <prompt> [-i <image>...] [-f <pdf>...] [-v <video>] [--fps <n>]

Mode is picked from your flags:

  • prompt only → text chat
  • -i → ask about images (repeatable)
  • -f → ask about pdf documents (repeatable)
  • -v → ask about a single video

Options:

  • -i <path> — image to ask about (repeat -i for more)
  • -f <path> — pdf document to ask about (repeat -f for more)
  • -v <path> — single video to ask about
  • --fps <n> — frames/sec sampled from the video, 0.2–5 (default: 1)

One-shot — each call is independent, no memory of previous calls. Output goes to stdout, nothing is saved to disk. Audio inside a video is not understood.

Examples:

vhscli chat "explain how to make sourdough in 5 steps"
vhscli chat "describe the scene. return json with objects, setting, mood." -i photo.jpg
vhscli chat "transcribe all visible text verbatim, preserving line breaks." -i receipt.jpg
vhscli chat "compare image 1 and image 2 in 3 bullets." -i a.jpg -i b.jpg
vhscli chat "summarize this paper in 5 bullets; include a page number per bullet." -f paper.pdf
vhscli chat "list key events with start_time and end_time in HH:mm:ss as json." -v clip.mp4 --fps 2

Image models — shared --size

Every image model takes one flag for the frame:

--size <width>x<height>   # default: 1920x1080

One value carries aspect ratio and resolution together, and it means the same frame on every model. Examples: 1920x1080, 1024x1024, 1080x1920, 3840x2160.

CLI checks form only: each edge 64–4096. The backend fits your size to the provider — a frame is never refused for being unrenderable, but what comes back may differ:

model what it does with a size
gpt-image-2 snaps both edges to a multiple of 16, inside 655360–8294400 pixels, max edge 3840, ratio at most 3:1
seedream-5, seedream-5-pro scales into 3686400–10404496 pixels — its floor is above 2K, so a small frame is scaled up (e.g. 960x540 → ~2560x1440)
nano-banana-2, nano-banana-pro rounds to its nearest aspect ratio and nearest 1K/2K/4K tier

Output format follows the -o extension (.png, .jpg/.jpeg, .webp); the CLI converts if needed.


vhscli generate seedream-5 — generate an image (default choice)

vhscli generate seedream-5 <prompt> -o <path> [-i <image>...] [--size <WxH>]

Options:

  • -o, --output <path> — output file path, required (e.g. out.jpg)
  • -i <path> — reference image, max 14 (repeat -i for more)
  • --size <WxH> — pixel dimensions (default: 1920x1080); see shared --size

Examples:

vhscli generate seedream-5 "a red fox in a snowy forest" -o fox.jpg
vhscli generate seedream-5 "swap the outfit" -o out.png -i person.jpg -i outfit.jpg --size 2048x2048

vhscli generate seedream-5-pro — generate an image (Seedream 5.0, pro tier)

vhscli generate seedream-5-pro <prompt> -o <path> [-i <image>...] [--size <WxH>]

Same flags and prompt guide as seedream-5, but the pro tier: stronger prompt adherence and finer detail, at roughly 2 minutes per image instead of seconds. Prefer plain seedream-5 by default; reach for seedream-5-pro when the user asks for the best quality, or when seedream-5 keeps missing details in a complex prompt. Prefer a .png -o (other extensions still work — the CLI converts).

Examples:

vhscli generate seedream-5-pro "a lone lighthouse on a cliff at dusk, long exposure, crashing surf" -o lighthouse.png
vhscli generate seedream-5-pro "add a flock of birds across the sky, keep the style" -i scene.png -o birds.png

Because it is slow, it pairs well with vhscli submit (below) when generating several images: submit them all, then poll + save (or resume one at a time).


vhscli generate nano-banana-2 — generate an image (Google)

vhscli generate nano-banana-2 <prompt> -o <path> [-i <image>...] [--size <WxH>]

Options:

  • -o, --output <path> — output file path, required (e.g. out.png)
  • -i <path> — reference image, max 14 (repeat -i for more)
  • --size <WxH> — pixel dimensions (default: 1920x1080); served as the nearest of 21:9/16:9/3:2/4:3/5:4/1:1/4:5/3:4/2:3/9:16 at the nearest of 1K/2K/4K

Examples:

vhscli generate nano-banana-2 "remove the man from the photo, keep everything else" -i photo.jpg -o clean.png
vhscli generate nano-banana-2 "90s skateboarder poster, vertical composition" -o poster.png --size 1080x1920
vhscli generate nano-banana-2 "a glossy candle in a bell jar on a marble counter, soft light" -o candle.png

vhscli generate nano-banana-pro — generate an image (Google, premium)

vhscli generate nano-banana-pro <prompt> -o <path> [-i <image>...] [--size <WxH>]

Same sizing and flags as nano-banana-2. Higher-quality sibling — better text rendering and richer textures.

Examples:

vhscli generate nano-banana-pro "studio portrait, cinematic lighting, three-quarter framing" -o portrait.jpg --size 1536x2048
vhscli generate nano-banana-pro "a sun-drenched minimalist living room with a 3d armchair from this sketch" -i sketch.jpg -o room.png

vhscli generate gpt-image-2 — generate or edit an image (OpenAI)

vhscli generate gpt-image-2 <prompt> -o <path> [-i <image>...] [--size <WxH>]

Options:

  • -o, --output <path> — output file path, required (e.g. out.png)
  • -i <path> — reference image for edits (repeat -i for more)
  • --size <WxH> — pixel dimensions (default: 1920x1080); see shared --size

Use png or webp when you need transparency.

Examples:

vhscli generate gpt-image-2 "a children's book drawing of a veterinarian examining a cat" -o vet.png
vhscli generate gpt-image-2 "replace the background with a starry night, keep the subject unchanged" -i photo.jpg -o night.png
vhscli generate gpt-image-2 "ultra-wide landscape of the swiss alps at golden hour" --size 3840x2160 -o alps.jpg

vhscli generate seedance-2 — generate a video

vhscli generate seedance-2 <prompt> -o <path>
                           [--first-frame <image>] [--last-frame <image>]
                           [-i <image>...] [-v <video>...] [-a <audio>...]
                           [--ratio <r>] [--resolution <res>] [--duration <n>]
                           [--no-audio]

Mode is picked from your flags:

  • prompt only → text-to-video
  • --first-frame → animate from that frame (optionally --last-frame too)
  • -i / -v / -a → use as references

Options:

  • -o, --output <path> — output file path, required (.mp4, .webm, or .mov; prefer .mp4)
  • --first-frame <image> — use as the first frame
  • --last-frame <image> — use as the last frame (requires --first-frame)
  • -i <path> — reference image, max 9 (repeat -i). conflicts with --first-frame
  • -v <path> — reference video, max 3 (repeat -v). conflicts with --first-frame
  • -a <path> — reference audio, max 3 (repeat -a). requires -i or -v, conflicts with --first-frame
  • --ratio <r> — aspect ratio (default: 16:9). one of: 21:9, 16:9, 4:3, 1:1, 3:4, 9:16
  • --resolution <res> — 480p, 720p, 1080p, or 4k (default: 720p). lowercase only
  • --duration <n> — length in seconds, 4–15 (default: 5)
  • --audio / --no-audio — toggle the audio track (default: --audio). pass --no-audio for a silent video

Defaults to 5s @ 720p, 16:9, with audio. Jobs run in the cloud and can take minutes — the CLI polls automatically. If you don't want to block, use vhscli submit seedance-2 ... (same flags) to detach immediately, then vhscli resume <task_id> -o cat.mp4 later (submit prints the id). A vhscli generate interrupted mid-poll is finished the same way.

Examples:

# text-to-video
vhscli generate seedance-2 "a cat jumping off a couch" -o cat.mp4 --duration 6 --ratio 16:9

# animate a still image
vhscli generate seedance-2 "camera pans right" -o pan.mp4 --first-frame start.jpg

# with a first and last frame
vhscli generate seedance-2 "morph between these" -o morph.mp4 --first-frame a.jpg --last-frame b.jpg

# reference-based with audio
vhscli generate seedance-2 "lip sync the words" -o out.mp4 -i face.jpg -a voice.mp3

vhscli generate seedance-2.5 — generate a video (capped at 720p)

vhscli generate seedance-2.5 <prompt> -o <path>
                             [...same flags as seedance-2 except --resolution]

Same model surface as seedance-2 (ratios, duration, first/last frame, refs, audio) except --resolution accepts only 480p or 720p (default: 720p). For 1080p / 4k, use seedance-2. Same prompt guide as seedance-2.

Examples:

vhscli generate seedance-2.5 "a woman in a red dress walks through a rainy neon-lit alley, slow tracking shot" -o alley.mp4
vhscli generate seedance-2.5 "animate this photo: gentle pan to the right" --first-frame photo.jpg -o pan.mp4

vhscli generate seed-audio-1 — generate speech audio

vhscli generate seed-audio-1 <prompt> -o <path> [-i <audio>...]

Options:

  • -o, --output <path> — output file path, required (must be .mp3)
  • -i <path> — reference voice clip for cloning or blending, max 3 (repeat -i for more). each clip should be ≤30s and ≤10MB

Output is always mp3.

Examples:

vhscli generate seed-audio-1 "Welcome to VHS." -o welcome.mp3
vhscli generate seed-audio-1 "Read this in the reference voice." -i voice.mp3 -o out.mp3
vhscli generate seed-audio-1 "Blend these voices." -i v1.mp3 -i v2.mp3 -o blend.mp3

How a task is tracked — what generate, submit, resume, poll, and save share

A generation is a row in the VHS backend. The CLI writes no database and no sidecar files, so the task id is the only handle to a task — lose it and you have stranded something you paid for.

  • generate submits, waits, and saves to -o. It prints the id first, so an interrupted run is resumable.
  • submit prints the id (task_id: <uuid>) and exits without waiting. -o is validated but nothing is written yet.
  • resume <task_id> -o <path> waits for one task and writes its output (poll + save in one step).
  • poll - watches many ids from stdin and prints the first to finish.
  • save <task_id> -o <path> writes a finished task; errors if still running.

-o is required on every command that writes a file. The task knows which model ran and what was asked, but only you know where the file belongs. The CLI writes exactly to -o — it does not re-home or de-conflict, so check the path first if you must not overwrite.

Everything is keyed by the task id and idempotent, so re-running a command that died mid-flight joins the existing task rather than paying twice. Use --task-id <uuid> to choose the id yourself when you need it recorded before the submit returns.

vhscli chat has no task to track — it is fast and prints to stdout.


vhscli submit — submit a task and exit (don't wait)

vhscli submit <model> <prompt> -o <path> [...same flags as `vhscli generate <model>`]

submit takes the same models and the same options as generate (seedance-2, seedance-2.5, seedream-5, seedream-5-pro, nano-banana-2, nano-banana-pro, gpt-image-2, seed-audio-1). The only difference is that once the backend has the task it prints task_id: <uuid> and exits without polling. -o is checked but nothing is written until you resume or save.

Use it when:

  • The job is long (e.g. seedance video) and you don't want to keep the terminal blocked.
  • You want to fan out several tasks in parallel and pull results later (poll + save).

Capture the printed id. For one task, finish with vhscli resume <task_id> -o <path>. For several, use poll + save below. Nothing on this machine remembers the task for you.

--task-id <uuid> submits under an id you choose instead of a fresh one, so you can write the id down before the submit returns. Use it when losing the id would strand a paid-for task: mint a uuid, persist it, then submit. If the command dies before it prints anything, re-run the identical command — the backend row and the submit are both keyed by that id and idempotent, so the re-run joins the same task rather than starting a second one.

Examples:

# kick off a video, get the terminal back, finish later
vhscli submit seedance-2 "a robot dancing in tokyo at night" -o robot.mp4
# prints: task_id: 7d3c1b2a-...
# ... do other work ...
vhscli resume 7d3c1b2a-... -o robot.mp4

# fan out several image jobs, then collect as they finish
vhscli submit seedream-5 "a red fox in a snowy forest" -o fox.jpg   # task_id: <id1>
vhscli submit seedream-5 "a blue jay on a branch"      -o jay.jpg   # task_id: <id2>
vhscli submit seedream-5 "an orca breaching"           -o orca.jpg  # task_id: <id3>
# then poll + save (see below) — not a multi-id resume

vhscli resume — finish one submitted generation by task id

vhscli resume <task_id> -o <path>

Takes the id submit printed and the path to write. resume:

  • Looks the task up on the server (fatal if the id is unknown there).
  • Recovers which model ran from the task itself, and checks -o matches that model's output kind before waiting.
  • Waits for the task to finish if it is still running.
  • Saves the media to -o. The extension sets the saved format; the CLI converts if needed. It writes exactly there — no re-homing, no (N).

Safe to re-run: a finished task is re-read from the server, never resubmitted. One id per call — to fan out, use poll + save, or run one resume per id in parallel shells.

When to use resume:

  • You ran vhscli submit ... and now want the result for a single task.
  • Your vhscli generate ... was interrupted (ctrl-c, crash, closed terminal, lost network) — it printed the id before it started waiting.

Examples:

vhscli resume 7d3c1b2a-... -o fox.png

vhscli poll — watch many tasks; print the first to finish

vhscli poll -

Reads task ids from stdin, one per line, and prints exactly one — the first to finish — then exits. Writes no files and takes no -o. Pair with vhscli save <task_id> -o <path>.

stdout is that one id and nothing else (pipes cleanly); human-readable state goes to stderr. With --json it is a single {"event":"task","task_id":"...","state":"done"|"error"|"missing"} line instead.

Ids may keep arriving while poll runs; each starts being watched as it lands. One result ends the process, so a driver keeps its own list of outstanding ids, drops the one it was just given, and polls again with the rest — re-sending anything it never saw reported. Run zero or one poll at a time, never one parked on nothing.

Polling again immediately is cheap: a task that finished while the last poll was exiting is answered off a level check, with no waiting. An id naming no task reports missing rather than hanging forever.

Examples:

id=$(printf '%s\n' "${ids[@]}" | vhscli poll -) && vhscli save "$id" -o "$id.png"

Fan-out loop sketch:

left=("${ids[@]}")
while [ ${#left[@]} -gt 0 ]; do
  id="$(printf '%s\n' "${left[@]}" | vhscli poll -)"
  vhscli save "$id" -o "$id.png"
  left=($(printf '%s\n' "${left[@]}" | grep -vFx "$id"))
done

vhscli save — write a finished task's output

vhscli save <task_id> -o <path>

Writes an already-finished task's output to -o and exits. It never waits: a task still running is an error here (poll is what waits). Safe to re-run — the outcome is read back off the task, not regenerated.

Prefer save over fetching a result URL yourself: the result envelope differs per model, the download lands atomically, and the file is converted when the provider's format differs from your -o extension. An errored task fails with the task's error.

Examples:

vhscli save 7d3c1b2a-... -o cat.png

Understanding local images, video, and pdfs

Do NOT use the Read tool, or any built-in file-reading capability, to "look at" images, video, or pdfs. That path either fails or gives you a garbled snippet. The only correct way to understand local visual or document content is vhscli chat with -i / -v / -f.

vhscli chat "what's happening?" -i photo.jpg
vhscli chat "transcribe the speech" -v clip.mp4 --fps 2
vhscli chat "summarize this paper" -f paper.pdf

Prompt patterns for visual / document understanding

vhscli chat understands images, pdfs, and video frames, but not audio inside videos. Ask for structured JSON output when you'll parse the answer, and name every field you want. Be explicit about formats (timestamp style, units, language).

Image — describe / classify:

vhscli chat "describe the scene. return json {objects:[{label,bbox?}], setting, mood, dominant_colors:[]}." -i photo.jpg
vhscli chat "classify the image into one of: cat, dog, bird, other. return json {label, confidence_0_1, reasoning}." -i pic.jpg

Image — OCR / text extraction:

vhscli chat "transcribe all visible text verbatim, preserving line breaks and reading order. do not paraphrase." -i receipt.jpg
vhscli chat "extract the receipt as json {merchant, date_iso, items:[{name, qty, unit_price, line_total}], subtotal, tax, total, currency}." -i receipt.jpg

Image — comparison (number them in the prompt):

vhscli chat "compare image 1 and image 2. return json {same_subject:bool, differences:[], which_is_better, why}." -i a.jpg -i b.jpg
vhscli chat "image 1 is the original, image 2 is an edit. list every visible change as json {changes:[{region, before, after}]}." -i orig.png -i edit.png

PDF — summarize / outline (always ask for page anchors):

vhscli chat "summarize this paper in 5 bullets. each bullet must include the source page as {page:int, point:string}. return json {bullets:[...]}." -f paper.pdf
vhscli chat "extract the outline as json [{page, heading_level, heading, bullets:[]}]." -f doc.pdf

PDF — QA / extraction:

vhscli chat "answer using only this document. question: what is the experimental setup? return json {answer, citations:[{page, quote}]}." -f paper.pdf
vhscli chat "extract every table as json [{page, title?, headers:[], rows:[[...]]}]." -f report.pdf

Video — events / timeline (state the timestamp format):

vhscli chat "list key events. return json [{start_time, end_time, event}]. use HH:mm:ss." -v clip.mp4 --fps 2
vhscli chat "describe the movement sequence and any safety risks. return json [{start_time, end_time, event, danger:'none'|'low'|'med'|'high'}]. HH:mm:ss." -v clip.mp4 --fps 3

Video — temporal QA / counting:

vhscli chat "at what timestamp does the referee first appear? return json {timestamp_hms, evidence}." -v match.mp4 --fps 2
vhscli chat "count how many distinct people appear. return json {count, per_person:[{first_seen_hms, description}]}." -v scene.mp4 --fps 3

Choosing --fps for video (default 1, range 0.2–5):

  • 3–5 — counting actions, sports, fast cuts, dense motion.
  • 1 — general description, dialogue scenes.
  • 0.2–0.5 — long static footage, headcount, slow surveillance.

Higher fps = more detail but more tokens and slower. Lower fps = cheaper but may miss brief events.

Tips

  • Always quote prompts.
  • -o is required for vhscli generate / vhscli submit / vhscli resume / vhscli save. It's relative to your cwd; output format follows the extension and the CLI converts if needed. The CLI writes exactly there and will overwrite, so pick a free name yourself if that matters.
  • Short options accept no-space form: -ofoo.jpg. Long options accept =: --size=1920x1080.
  • Use -- to pass a prompt starting with a dash: vhscli generate seedream-5 -o x.jpg -- "-weird prompt".
  • Reference images (-i, --first-frame, --last-frame) can be any common format; non-JPEG/PNG inputs (e.g. HEIC, WebP, TIFF, BMP) are converted to JPEG before upload.
  • Reference files are uploaded to temporary cloud storage for the provider to fetch, and expire there on their own. Each run re-uploads them, so passing a large reference repeatedly costs an upload each time.
  • Unknown command? vhscli will suggest the closest match.

--json (scripting)

Add --json to generate, submit, resume, save, poll, whoami, or account to get one JSON object per line on stdout while logs go to stderr:

{"event":"submitted","task_id":"..."}
{"event":"progress","task_id":"...","elapsed_s":42}
{"event":"task","task_id":"...","state":"done"|"error"|"missing","message":"..."}
{"event":"done","task_id":"...","path":"/abs/out.mp4","hash":"sha256:...","size":123}
{"event":"whoami","user_id":"...","email":"...","name":"..."}
{"event":"account","user_id":"...","email":"...","name":"...","balance_usd":14.04}
{"event":"error","message":"...","code":"auth"|"credit"|"network"}
  • submitted fires as soon as the backend accepts the job — persist the task_id and you can vhscli resume <task_id> -o <path> --json later, even after a crash, from any machine you are logged in on. The id is the whole handle; nothing local is needed.
  • task is what poll emits when a watched id reaches an outcome (done / error / missing). It does not write a file — follow with save.
  • done.path is the file written (always the -o you asked for); hash is sha256 of its content. Emitted by commands that write (generate, resume, save).
  • code on error appears only for actionable failures: auth (log in), credit (top up), network (retry may work). No code means the message is all there is. A failing command emits error and exits 1.