@tryflowy/cli
v0.1.2
Published
Flowy CLI — sign in and plug the Flowy MCP into your AI agent (Claude, Cursor, Codex, …).
Maintainers
Readme
flowy (Go)
A Go port of @tryflowy/cli, built on the
Charm stack. Same commands, same terminal UI, same OAuth and
MCP behavior — one static binary, no Node.js required.
Sign in to Flowy and plug the Flowy MCP into your AI agent (Claude Code, Cursor, Codex, ChatGPT, …) so it can generate images, videos, audio and 3D models in your Flowy workspace — spending your workspace credits, over OAuth 2.1.
Install
go install github.com/homingos/flowy-mcp/packages/cli-go@latestOr build from this directory:
make build # ./flowy
make install # into $GOBINRequires Go ≥ 1.25.
Quick start
flowy auth # 1. sign in (opens your browser)
flowy mcp install # 2. connect the Flowy MCP to your agent(s)Then restart your agent and ask it to "generate an image with Flowy".
Run flowy with no arguments and you get the startup screen: the Flowy mark
blown up into a dot matrix, lit by a mint-to-slate gradient, with the current
sweeping the ribbon. Press enter and it clears to a second screen listing
everything Flowy can do, pickable with ↑ ↓, filterable with /.
Running a command doesn't end the session. Its output stays on screen, and backspace takes you back to the list to run another; q or Ctrl-C quits. A command that fails is no different — the shell survives it.
Invoked directly (flowy account status) nothing changes: the command runs, and
its exit code is the process's.
Narrow or short terminals get the compact braille header with the same enter/esc affordance. Piped, there's no key to press, so the full subcommand listing prints straight out.
Interactive by default, scriptable on demand
On a terminal, commands ask for what you didn't type:
| Run this | And you get |
| --- | --- |
| flowy | the dot-matrix startup screen; enter opens the command list, esc quits |
| flowy generate image | a composer box for the prompt, then aspect ratio, resolution, wait? |
| flowy workspace set | a picker over your actual workspaces |
| flowy node create | a project picker, then type + prompt |
| flowy mcp install | a multi-select over Claude / Cursor / Codex |
| flowy brandkit delete <id> | a confirmation, since deletion is irreversible |
Supply the argument and nothing is asked — flowy generate image "a cat" --wait
runs straight through. Prompts are skipped entirely when stdout or stdin is not
a terminal, and -y / --yes forces that off explicitly:
flowy -y mcp install # all clients, no questions
flowy -y generate image "a cat" # errors if a required value is missing--json: one document per command, for agents
--json is a persistent flag on every command. With it, the frames, cards,
spinners and prompts all fall silent and the command prints a single JSON
document to stdout — nothing else — so an agent parses the result instead of
scraping box-drawing characters. It implies --yes, so a command never stops to
ask; anything it would have prompted for must be passed as a flag or argument.
flowy account status --json # {email, planId, available, …}
flowy account transactions --image --size 5 --json # {transactions:[…], scanned, filters}
flowy workspace list --json # {workspaces:[…], selected}
flowy generate image "a red bike" --wait --json # {generationId, status, outputs:[{url,kind}]}
flowy generate image "a red bike" --json # no --wait → {generationId, status:"processing"}Failures are JSON too, and carry the HTTP status when the API set one, so an
agent can tell 401 (sign in) from 402 (out of credits) without reading prose,
and the process still exits non-zero:
{ "error": "Could not fetch the account: unauthorized", "status": 401 }flowy auth --json reports login state ({signedIn, expired, issuer, scopes,
expiresAt}) rather than opening a browser — signing in needs a person, so it is
never attempted in JSON mode.
Generation prompts get a composer: a ruled text box with a caret, a cursor, and a hint row, so a long prompt is legible while you write it. Enter submits (a blank prompt won't — generating costs credits); Esc cancels.
──────────────────────────────────────────────────────────────
❯ a cat eating cookies▌
──────────────────────────────────────────────────────────────
enter to submit · esc to cancel imageIt's pinned to the foot of the terminal and spans its width, and it hands the rows back when you submit, so the transcript stays contiguous. On a narrow terminal the hint row sheds its tag and abbreviates rather than wrapping.
Esc or Ctrl-C cancels any prompt.
flowy generate --wait shows a live view — the mascot, a spinner, the elapsed
time, and a progress bar that eases toward each reported value. The mascot paces
its patch of terminal while the job runs, looking where it walks and blinking at
the end of each lap, then waves you off when it's finished.
Started from the composer, generation reads as a conversation: your prompt is
echoed back in --ink cream behind a sage ❯, the results are named after it,
and the composer returns for the next one — keeping the settings you picked.
Press c on the results to copy an asset's URL (you'll be asked which, when a
generation made several); enter for another prompt; esc to finish.
Given a prompt on the command line (flowy generate image "a cat") it runs once
and returns, so scripts stay scripts. The bar's spring is
critically damped (damping 1.0), so it settles onto its target and never
overshoots or springs back. Ctrl-C stops watching without cancelling the job,
and tells you how to pick it back up.
List commands (account transactions, workspace list, project list,
brandkit list) render as tables with sage headers, a header rule and thin
column dividers. Numeric columns are right-aligned.
Result cards (account status, auth status, workspace credits, …) are filled
panels in the brand palette: a --brand sage-tinted background, a sage frame, a
--ink cream title. A --current band — a lifted fill plus a mint frame — sweeps
across on a loop, the same current that traces the logo's ribbon.
The loop is bounded (3 sweeps, ~0.9s) because a terminal command has to exit;
FLOWY_SHIMMER_LOOPS=n changes the count and 0 disables the sweep. NO_COLOR,
FLOWY_NO_ANIM and piping all print the settled card directly.
The fill adapts to your terminal's theme, queried once via OSC 11.
FLOWY_THEME=light|dark skips the query.
How this maps to the TypeScript CLI
| TypeScript | Go | Notes |
| --- | --- | --- |
| commander | cobra + fang | Fang styles help, errors, and completions |
| @clack/prompts | lipgloss (internal/ui) | Clack's intro/outro/note/log vocabulary, reimplemented |
| Clack spinner | bubbletea + bubbles | Same ◒◐◓◑ frames |
| — (no equivalent) | bubbles/textinput | The prompt composer |
| Hand-padded columns | lipgloss/table | Header rule + column dividers, ANSI-aware widths |
| — (no equivalent) | huh | Pickers, forms and confirmations, themed to match |
| Hand-rolled ANSI logo animation | bubbletea (internal/logo) | Same braille mark and "current" flourish |
| @modelcontextprotocol/sdk | modelcontextprotocol/go-sdk | Same 20 stdio tools |
| zod schemas | Go structs + jsonschema tags | Enums / min / max preserved |
| open | pkg/browser | Loopback OAuth |
| smol-toml | go-toml/v2 | Codex config.toml |
Behavioral parity is deliberate down to the details: credit amounts are formatted
exactly the way JavaScript's toLocaleString("en-US", { maximumFractionDigits: 2 })
formats them (including its round-the-shortest-decimal, half-away-from-zero
behavior), and ~/.flowy/credentials.json is byte-compatible, so the two CLIs can
share a login.
Commands
flowy auth [--no-browser] [--issuer <url>] [--logout]
One command for the whole session. Signed out, it signs you in. Signed in, it
shows the session and offers to end it (or to sign in again, when the token has
expired and cannot refresh). --logout signs out without asking.
Signing in uses the loopback browser OAuth flow (PKCE, public client — no secret):
- Discovers the authorization server (RFC 8414).
- Starts a local
127.0.0.1callback server on an ephemeral port. - Dynamically registers a client (RFC 7591).
- Opens the authorization URL in your browser; sign in and consent.
- Exchanges the returned code for tokens and stores them at
~/.flowy/credentials.json(mode0600).
--no-browser— print the sign-in URL instead of opening a browser.--issuer <url>— override the authorization-server issuer.
flowy auth login, auth logout and auth status still work — hidden rather
than removed, so anything already scripted against them keeps running.
flowy account status (alias acc) / flowy account transactions [--size <n>]
The signed-in account — email, plan, available credits — and its recent credit transactions (newest first, default 20, max 100).
--image, --video, --audio and --3d narrow the list to those generations;
combine them to see several kinds at once. The API has no kind parameter, so a
filtered run pulls the most recent 100 transactions and sifts them here — the
output says so, since an older match would otherwise look like it doesn't exist.
flowy account transactions --image # image generations only
flowy account transactions --image --video # both
flowy account transactions --3d --size 5 # the five most recent 3D chargesflowy workspace … (alias ws)
list, set <workspaceId>, status, unset, credits [workspaceId].
set selects the workspace context: the workspace future requests act in and
bill against (validated — you need owner/editor access). Persisted at
~/.flowy/settings.json and honored by the flowy mcp stdio tools.
flowy generate image|video|audio|3d [prompt] (alias gen)
Starts a generation and prints its id. Flags: --aspect-ratio, --resolution,
--duration, --mode (audio: sfx|music), --format (3d geometry),
--input <assetId|url> (repeatable; required for 3d), and --wait to watch
it to completion and print the output URL.
Omit the prompt on a terminal and you get the guided form instead.
For 3d, asset ids are exchanged for fresh signed URLs before the job starts —
the mesh provider downloads inputs over plain HTTP.
flowy generate status <generationId>
Status, outputs, and any error for a generation.
flowy project list / flowy node create [projectId] --type … --prompt …
Browse canvas projects, or start a generation as a node on a project's canvas.
node create exchanges the signed-in OAuth token at /v1/session, then uses
the resulting backend JWT to write a frontend-shape node to that project's Yjs
canvas WebSocket. It then starts generation through
/projects/:id/canvas/:type-node using the same session JWT. The node is thus
created on the canvas data plane and the generated result arrives in every open
project canvas. --input accepts repeatable asset IDs or public media URLs.
flowy assets list (alias asset)
Lists the workspace's asset library (uploads + generated media), newest first, with each asset's id — so you can reference one in a generation:
flowy assets list --image --size 10 # ten most recent image assets
flowy generate video "she waves" --input <assetId>Flags: --size <n> (default 20, max 100) and the media-type filters
--image / --video / --audio / --3d (client-side over the paged library).
--json emits { assets: [{id, mediaType, url, source, createdAt}], filters }.
Reference assets by id rather than a signed URL — signed URLs expire.
flowy brandkit … (alias bk)
list, get <id>, create <url>, delete <id>, poll <jobId>.
flowy render status <workflowId>
Check a composition render (processing | completed | failed).
flowy flows … (alias apps)
The apps surface: the published apps in your authenticated workspace, their
schemas, and the runs you start against them. It uses the same OAuth session as
every other command; the token's apps:read and runs:* scopes determine what
you may do. When targeting genstudio-svc-refactor, request its enabled scopes
before signing in:
FLOWY_SCOPES="generations:read generations:write assets:read credits:read apps:read runs:read runs:write" flowy authSome deployments intentionally enable only the core scopes, so ordinary flowy
auth remains compatible while the refactor deploys.
flowy flows list— the active apps (TITLE · ID · PUBLISHED · RUNS).flowy flows get <appId>— an app's identity plus its input nodes (label · nodeId · type · required) and output nodes, so you can see what a run needs and what it returns.flowy flows run <appId>— start a run. On a terminal it fetches the app's schema and asks for a value per input node. Scripted, pass the nodes directly:--input <nodeId>=<value>(repeatable) and/or--inputs '<json object>'; a value that looks like JSON (a number, an array) is parsed, a URL or text stays a string.--waitpolls to completion and prints the output URL; without it you get therunIdto poll yourself.flowy flows status <runId>— poll a run and, oncecompleted, print its output media URL(s). With--jsonit's a single snapshot (poll on your own cadence); on a terminal it's a live view until the run settles.
flowy flows list --json
flowy flows get 6a514049cfcad3eb9608032a --json
flowy flows run 6a514049cfcad3eb9608032a \
--input image_7974ce312f6c4296a76224edfc8fa027=https://example.com/me.jpg --wait --json
flowy flows status <runId> --jsonThe run-start and run-status response shapes weren't observable while this was
written, so they're decoded permissively and --json also passes a urls array
pulled to the top of the document, plus the raw outputs.
flowy mcp install [--client claude|cursor|codex|all] [--url <mcpUrl>]
Wires the hosted Flowy MCP (https://mcp.tryflowy.ai/mcp) into your agent.
Idempotent — safe to re-run; existing config is preserved. With no --client, it
offers a multi-select on a terminal, and targets all otherwise:
- claude — runs
claude mcp add --transport http --scope user flowy <url>if the Claude Code CLI is on your PATH, otherwise prints the command to run. - cursor — merges into
~/.cursor/mcp.jsonundermcpServers.flowy. - codex — merges into
~/.codex/config.tomlunder[mcp_servers.flowy].
It also prints setup for ChatGPT (Settings → Apps → Developer mode) and a
generic { "mcpServers": { "flowy": { "url": … } } } snippet for any other client.
flowy mcp stdio
Runs a local stdio MCP server for stdio-only clients, exposing the same 20
tools as the hosted server as thin proxies over the Flowy /v1 API, and
authenticating with your stored token (auto-refreshing when expired). Point a
stdio MCP client at flowy mcp stdio as the command.
Tools: generate_image, generate_video, generate_audio, generate_3d,
get_generation, list_assets, get_asset, get_credits, get_account,
list_workspaces, list_transactions, list_projects, create_node,
list_brandkits, get_brandkit, create_brandkit, poll_brandkit_extract,
start_render, list_renders, get_render.
stdoutis owned by the JSON-RPC transport; all diagnostics go tostderr.
flowy skill install
Installs the Flowy agent skill via npx -y skills add flowy-ai/skills.
flowy version
Prints the CLI version and runtime info.
Environment overrides
| Variable | Default | Used by |
| -------------------- | ----------------------------------------------- | ------------- |
| FLOWY_AS_ISSUER | https://fi.development.flamapis.com/genstudio-svc-v2/api | auth login |
| FLOWY_MCP_URL | https://mcp.tryflowy.ai/mcp | mcp install |
| FLOWY_API_BASE_URL | https://fi.development.flamapis.com/genstudio-svc-v2/api | REST API |
| FLOWY_COLLAB_URL | derived as wss://<API host>/collab-svc | project canvas WebSocket |
| FLOWY_SCOPES | four core scopes | scopes requested at auth sign-in; add scopes enabled by the target service |
| FLOWY_NO_ANIM | — | skips the logo flourish |
| NO_COLOR | — | strips all color |
| FLOWY_SHIMMER_LOOPS| 3 | card sweeps; 0 disables |
| FLOWY_THEME | auto-detected | light | dark card fill |
Passing -y / --yes (or piping) turns every prompt off.
These may be exported in your shell or kept in a .env.local / .env file
in the directory you run flowy from. Precedence (highest first): exported shell
variable → .env.local → .env. auth login prints which file it loaded config
from. Note that auth login uses FLOWY_AS_ISSUER (the authorization
server), a different URL from the /v1 API base (FLOWY_API_BASE_URL) — for a
local backend, set both.
Color is negotiated per-stream: styled output is written through a
colorprofile.Writer, so piping to a file or NO_COLOR=1 yields clean text, and
the logo animation is skipped entirely when stdout is not a TTY.
Layout
main.go fang.Execute over the cobra tree
internal/cmd/ one file per command group
internal/ui/ Clack-style intro/outro/log, the brand card and its
shimmer, spinner, prompts, tables, progress view
internal/logo/ braille mark, the dot-matrix splash decoded from it,
and the "current" sweep
internal/oauth/ RFC 8414 discovery, RFC 7591 DCR, PKCE, loopback
internal/credentials/ ~/.flowy/credentials.json + auto-refresh
internal/settings/ ~/.flowy/settings.json (workspace context)
internal/apiclient/ /v1 client, { data: … } envelope, typed errors
internal/canvas/ OAuth-to-session exchange + Yjs canvas WebSocket sync
internal/agentclients/ claude / cursor / codex config injectors
internal/mcpserver/ the 20-tool stdio MCP server
internal/envfile/ .env.local / .env loader
internal/config/ endpoint + scope defaultsDevelop
make test # go test ./...
make lint # go vet + gofmt
make run # build and run the bare `flowy` home screen