cmfy is a fast, flexible command‑line tool to run ComfyUI workflows. It loads JSON workflows from your filesystem, applies template variables and targeted overrides, submits them to a running ComfyUI server, and saves the generated outputs locally.
- Simple commands:
run,batch run(JSONL mixed workflows),workflows(list/show/inspect/assign/ssh-list/ssh-import),queue,job(status/wait/cancel),server ping,config(init/path),version. - Works with local workflow JSONs; supports both raw prompt maps and
{ "prompt": { ... } }wrappers. - Configurable
workflows_dirandoutput_dirin a TOML config stored under$XDG_CONFIG_HOME/cmfy/config.toml. - Rich templating via
${KEY}placeholders across string inputs. - Precise overrides with
--set <nodeID>.inputs.<name>=<value>(coerces ints/floats/bools; quoted strings preserved). - Asset uploads for images/masks/inputs; exposes
${IMAGE},${MASK},${INPUT}and enumerated variants. - First‑class sampler/refiner flags (
--sampler,--scheduler,--steps,--cfg,--denoise, etc.). - Music prompt convenience flags:
--tagsand--lyricsmap to${TAGS}and${LYRICS}. - Standard workflow aliases (e.g.,
txt2img,img2img, …) via[standard_workflows]and path mappings via[standard_workflows_params.<alias>].
- Go 1.21+ to build.
- A running ComfyUI server (HTTP API) reachable at
server_url(defaulthttp://127.0.0.1:8188).
# From repository root
go build -o cmfy ./cmd/cmfy
./cmfy versionOr use just:
just help
just build
just test
just checkConfig is stored at $XDG_CONFIG_HOME/cmfy/config.toml. If XDG_CONFIG_HOME is empty, falls back to ~/.config/cmfy/config.toml.
By default, output_dir is ./outputs (relative to the current working directory where you run cmfy).
Initialize a default config:
./cmfy config init
./cmfy config path # prints the pathConfig keys:
"$schema" = "https://raw.githubusercontent.com/byteowlz/schemas/refs/heads/main/cmfy/cmfy.config.schema.json"
server_url = "http://127.0.0.1:8188"
output_dir = "./outputs"
workflows_dir = "workflows"
default_workflow = "" # optional fallback when -w is omitted
default_width = 768
default_height = 768
default_steps = 28
[vars]
# Global template vars available to all workflows
# MODEL = "sdxl.safetensors"
# CFG = "4.5"
[workflows.my_custom.vars]
# Per-workflow defaults (workflow name = file name without extension)
# PROMPT = "a cute cat"
# STEPS = "20"
[standard_workflows]
# Map well-known aliases to workflow names or absolute/relative JSON paths.
# If unset but a matching file exists (e.g., <workflows_dir>/txt2img.json), it is used implicitly.
txt2img = ""
img2img = ""
canny2img = ""
depth2img = ""
img2vid = ""
txt2vid = ""
txt2music = ""
txt2img_lora = ""
img2img_inpainting = ""
# Optional: precise paths for first-class flags for a given alias
[standard_workflows_params.txt2img]
# sampler_name = "12.inputs.sampler_name"
# scheduler = "12.inputs.scheduler"
# steps = "12.inputs.steps"
# cfg = "12.inputs.cfg"
# denoise = "12.inputs.denoise"
[standard_workflows_params.txt2img_lora]
# sampler_name = "28.inputs.sampler_name"
# refiner.sampler_name = "42.inputs.sampler_name"
# refiner.steps = "42.inputs.steps"
# Optional: named remote servers for SSH workflow discovery/import
[remote_servers.local_gpu]
ssh_config_host = "local-gpu"
workflows_dir = "~/ComfyUI/user/default/workflows"
# host = "192.168.1.20"
# user = "agent"
# port = 22
# key_path = "~/.ssh/id_ed25519"- Place JSON workflows under
workflows_dir(default./workflows). - Supported formats:
- Raw prompt map: a JSON object with numeric keys for nodes:
{ "0": { ... }, "1": { ... }, ... }. - Wrapper with
promptkey:{ "prompt": { "0": { ... } } }.
- Raw prompt map: a JSON object with numeric keys for nodes:
- Optional workflow metadata keys:
variables: variable defaults/descriptions.prompt_guidelines(orguidelines): authoring hints for prompt/tag writing.
Inspect a workflow to discover node IDs, class types, and inputs:
./cmfy workflows inspect txt2img
./cmfy workflows inspect txt2music --guidelines # prints optional prompting guidance
./cmfy workflows show txt2img # prints JSON with prompt map
./cmfy workflows list # lists available names in workflows_dir
# From SSH-configured remote servers in config.toml
./cmfy workflows ssh-list local_gpu
./cmfy workflows ssh-list local_gpu flux --json
./cmfy workflows ssh-import local_gpu Flux_upscale.jsonBasic run:
# By name (resolved in workflows_dir)
./cmfy run -w txt2img --prompt "a cozy cabin in the woods" --width 768 --height 768 --steps 30
# By path (absolute or relative)
./cmfy run -w ./workflows/my_flow.json --prompt "a sketch of an owl"
# Async submit (returns immediately; prints prompt ID)
./cmfy run -w txt2img --prompt "a sketch of an owl" --async
# txt2music convenience variables
./cmfy run -w txt2music --tags "lofi chill, warm keys" --lyrics "Late night city lights"Using standard and custom aliases:
# Assign (CLI writes config.toml [standard_workflows])
./cmfy workflows assign txt2img my_txt2img
./cmfy workflows assign ltx23 ~/cmfy/workflows/image_to_video_ltx2_3_i2v_with_sound.json
# Then run directly by alias
./cmfy txt2img --prompt "sunlit woodland" --steps 28 --cfg 5.5 --sampler euler
./cmfy ltx23 --image input.png --var PROMPT="cinematic close-up" --asyncYou can also define aliases manually in config:
[standard_workflows]
ltx23 = "~/cmfy/workflows/image_to_video_ltx2_3_i2v_with_sound.json"Asset uploads:
./cmfy run -w img2img \
--image input.png \
--mask mask.png \
--input controlnet_hint.png
# Exposes variables: ${IMAGE}, ${IMAGE1}, ${MASK}, ${INPUT}, etc.
# Use these variables in your workflow node string inputs.Targeted overrides:
# Set any input on any node using <nodeID>.inputs.<name>=<value>
./cmfy run -w txt2img --set 12.inputs.steps=25 --set 12.inputs.cfg=5.0
./cmfy run -w img2img --set 8.inputs.denoise=0.6 --set 9.inputs.seed=1337
# Values are coerced when possible: ints, floats, bools; quoted strings stay stringsFirst‑class sampler/refiner flags:
./cmfy txt2img \
--sampler euler --scheduler normal \
--steps 30 --cfg 5.5 --denoise 0.2
./cmfy txt2img_lora \
--sampler dpmpp_2m --refiner-sampler euler \
--refiner-steps 15 --refiner-cfg 4.0How first‑class flags are applied:
- If
[standard_workflows_params.<alias>]mappings exist, flags (e.g.,steps,cfg,sampler_name) are written to those exact paths. - Otherwise, cmfy uses heuristics:
- Base flags set the first node that has a matching input name.
refiner.*flags target the second occurrence (common in two‑stage flows).
- Use
workflows inspectto find stable node IDs and add precise mappings in config.
Outputs:
- Saved to
output_dir(default./outputs). - Override per run with
--output/-oor--output-dir(all workflow commands, including aliases likecmfy txt2img). - Filenames are taken from the server’s
/viewendpoint responses.
- Any string input in the workflow that contains
${KEY}will be replaced with the value ofKEY. - Sources (precedence):
- Global
[vars]in config [workflows.<name>.vars]in config- Convenience flags (
--prompt,--tags,--lyrics,--seed,--width,--height,--steps,--cfg) --var KEY=VALon CLI- Uploaded assets populate
${IMAGE},${MASK},${INPUT}and enumerated variants
- Global
- Numeric inputs generally require
--setunless you model them as strings in the workflow and template them.
Optional prompting guidelines in workflow JSON:
{
"prompt_guidelines": {
"summary": "Write cinematic, high-contrast prompts.",
"dos": ["Use strong nouns", "Include mood and pacing"],
"donts": ["Avoid contradictory styles"],
"examples": ["epic trailer score, dark tension, rising strings"]
}
}Show them with:
./cmfy workflows inspect txt2music --guidelinesSubmit multiple jobs from a JSONL file. Each line can target a different workflow.
# Print example JSONL to stdout
./cmfy batch run example --mode mixed-workflows
# Run batch file
./cmfy batch run --file jobs.jsonl --async
# Throttle submission rate (submission delay only, not Comfy execution concurrency)
./cmfy batch run --file jobs.jsonl --submit-delay 500ms
# Machine-readable result summary
./cmfy batch run --file jobs.jsonl --jsonJSONL line schema (core fields):
workflow(required): alias, name, or workflow pathid(optional): user tracking IDvars(optional): map for${KEY}substitutionsset(optional): map for<nodeID>.inputs.<name>=valueimage/mask/input(optional): arrays of file pathsserver(optional): override server URLasync(optional): per-line async overridetimeout(optional): per-line wait timeout (e.g.30m)
./cmfy server ping # checks connectivity to server_url
./cmfy queue # show running/pending queue
./cmfy queue --json # machine-readable queue status
./cmfy job status <id> # check a prompt by ID
./cmfy job wait <id> # wait for completion
./cmfy job cancel <id> # try to remove from queue
./cmfy version # prints CLI versionPOST /upload— uploads files (form-data) as typeinput.POST /prompt— submits a prompt graph, returnsprompt_id.GET /history/<prompt_id>— tracks prompt execution and outputs.GET /queue— queue state for running/pending prompts.POST /queue— queue manipulation (used for cancel attempts).GET /view?filename=...&subfolder=...&type=...— downloads generated assets.GET /system_stats— used forserver ping.
- If
cmfy runreports “no workflow specified”, setdefault_workflowin config or pass-w. - If an alias is not set, cmfy will still run it implicitly if
<workflows_dir>/<alias>.jsonexists. - Use
workflows inspectto discover inputs; then use--setor add mappings under[standard_workflows_params.<alias>]for stable behavior. - The bundled TOML parser supports keys/strings/ints/floats/bools/arrays and simple sections. If you need advanced TOML features, open an issue to switch to a full TOML library.
- Optional WebSocket progress and live preview support
- Built‑in templates for popular workflows
- More first‑class flags (e.g., clip_skip, scheduler params)
- Richer templating with type hints (e.g.,
${WIDTH:int})
Issues and PRs are welcome. Please keep changes focused and consistent with the existing style. If you add new flags or config keys, update this README accordingly.