gemini models
gemini models lists every model the CLI offers, as text or JSON
This change adds a models command to Gemini CLI. Run gemini models and it prints every model the CLI can use, with its context limits. Ask for JSON output and the same list comes back as an array you can pipe straight into jq. A matching flag, list models, does the same thing.
Why a models flag
Models get the list-and-exit flag that extensions already had
Closes #27847
The author wants programmatic model discovery: JSON for scripts, and readable text for people. The CLI already had a flag that lists extensions and exits. This adds the matching flag for models, with the same style of help text.
Three ways in
models, models list and --list-models print the same list
listCommand registers both names
command: ['$0', 'list']
Each takes -o json
You can reach the list three ways. Gemini models, gemini models list, or the list models flag, and all three land in the same handler. The bare command works because the list command registers as both $0 and list. Every form takes the JSON output format.
Text output
By default, a list a person can read
Models from the fixture in models/list.test.ts
With no format option you get text. A bold heading, then each model's ID and display name, its description, and its context limit in input and output tokens. These rows use the test fixture's models, auto and Gemini 2.5 Pro, each with about a million input tokens.
JSON output
With -o json, the same list is a JSON array
One ModelListing per model, limits as plain integers
[
{
"modelId": "auto",
"id": "auto",
"name": "Auto",
"displayName": "Auto",
"description": "Automatic model selection",
"contextLimits": {
"inputTokens": 1048576,
"outputTokens": 8192
}
},
…
]Ask for JSON and you get an array, one object per model. Each object carries the model ID twice, as model ID and as id. The name appears twice as well, as name and display name. Then the context limits, written as plain integers, so jq can compare them without parsing commas.
One function behind both
Both entry points end in handleList()
Here's how the pieces connect. The subcommand lives in models.ts, a yargs module whose middleware marks the run as a command and defers to the list command. The flag is parsed in config, and main, in gemini.tsx, branches on it. Both paths call handleList, which loads config with extensions skipped, maps the models, and writes the listing.
Exit before the sandbox
--list-models exits before the sandbox starts
No sandbox, no interactive UI
await runDeferredCommand(settings.merged);
if (argv.listModels) {
try {
await handleListModels({
outputFormat,
loadedSettings: settings,
argv,
});
await runExitCleanup();
process.exit(ExitCodes.SUCCESS);
} catch (error) {
process.stderr.write(…);
process.exit(ExitCodes.FATAL_INPUT_ERROR);
}
}
// hop into sandbox if … sandboxing is enabled
if (!process.env['SANDBOX'] && !argv.isCommand) {The flag's branch sits in main, right after deferred commands run. When it's set, it lists the models, cleans up, and exits with success. A failure goes to standard error and exits with the fatal input error code. All of this runs before the sandbox check, so listing models never starts a sandbox or the interactive UI.
Clean stdout for jq
JSON goes to stdout, and everything else goes to stderr
Piping into jq only works if standard output carries nothing but JSON. The listing is the only thing written there, and everything else goes to standard error. The handler sets up the CLI's output listeners right after loading config, which routes the rest to standard error. Config loads with extensions skipped, and listing bypasses the non-interactive auth refresh, so credential logs stay out of the pipe.
What review changed
Review flagged five edge cases, and each one got a fix
tokenLimit(m.modelId) ?? DEFAULT_TOKEN_LIMIT
The automated reviewer, Gemini Code Assist, flagged several edge cases. The sharpest: a model missing from the token table could get an undefined limit, and the text formatter would crash calling to locale string on it. It now falls back to the default token limit. Missing names and lists got defaults too, failures now set an exit code, and the shared default arguments are copied.
Evidence: the tests
unit tests in 2 new files
Validated on Linux with npm run
- recognizes "gemini models" as a command without falling through to a prompt query
- accepts "gemini --list-models" flag without throwing unknown argument error
- should fall back to DEFAULT_TOKEN_LIMIT for unknown or custom models
- should output models in JSON format when outputFormat is json
- should write error to stderr and still call exitCli on failure
Seventeen unit tests across two new files back this up. They check that gemini models parses as a command rather than a prompt, that the flag is accepted, that unknown models get the default limit, that JSON output parses, and that a failure still exits cleanly. The author validated on Linux with npm run.
What the listing doesn't know yet
Every model reports 8,192 output tokens
DEFAULT_TOKEN_LIMIT
input limit for models the token table doesn't know
tier?
declared on ModelListing, never filled
docs
README and docs unchanged
Two values in the listing are fixed rather than looked up. Output tokens is the constant 8,192 for every model. A model missing from the token table reports the default input limit. The tier field is declared but unused, and the docs and README are unchanged.
Try it
gemini models lists every model the CLI offers, as text or JSON
To try it from a checkout, follow the pull request's validation steps. Build, then run the models command from the built package with JSON output, piped into jq. Gemini models lists every model the CLI offers, as text or JSON.












