Seven million input tokens on a minimal WordPress theme is what finally made me look at where Gemini CLI spends money.
The theme in question was tiny: a handful of PHP files, one stylesheet, a couple of scripts. Nothing about it justified that token count. The cause turned out to be everything around the code rather than the code itself, and once I understood the pattern the fix took a few minutes.
If you are running Gemini CLI against a real project and the bills look wrong, this is where to start.
Your Context Is Full of Things You Never Meant to Send
Gemini CLI builds context from the directory you launch it in. Without instructions it will happily read compiled assets, binaries and generated files, and every one of those becomes input tokens you pay for.
On that WordPress project the bulk came from a predictable set:
- Video and image files. By far the biggest single cost. One stray mp4 that had been dropped into a theme’s asset folder accounted for millions of tokens on its own.
- Compiled output in
/dist/, which is a byte for byte duplicate of source the model is already reading. - Font files, which are binary and carry no useful signal whatsoever.
- The theme’s compiled
style.css, a minified copy of SCSS that was also in context. - Translation
.pofiles, which are enormous and almost never relevant to the task at hand. - CodeKit configuration, a large generated JSON blob describing the build.
Notice the theme: nearly all of it is generated, binary, or duplicated. None of it helps the model reason about your code, and it is the cheapest thing in the world to exclude.
.geminiignore
The fix is a .geminiignore file in the directory where you first run the gemini command. Syntax is identical to .gitignore, but the way you should use it is fundamentally different.
A .gitignore answers “what should not be tracked in version control.” A .geminiignore answers “what does the model not need in order to help me.” Those are different questions with different answers. Compiled CSS is committed in plenty of WordPress themes, so Git tracks it, but the model gains nothing from reading it alongside the SCSS that produced it.
A reasonable starting point for a WordPress project:
# Binary and media, the biggest cost by far
*.mp4
*.mov
*.webm
*.png
*.jpg
*.jpeg
*.gif
*.webp
*.ico
*.woff
*.woff2
*.ttf
*.eot
*.pdf
*.zip
# Generated output, duplicates of source already in context
dist/
build/
node_modules/
vendor/
*.min.js
*.min.css
*.map
# Large generated or low signal files
*.po
*.mo
config.codekit3
composer.lock
package-lock.json
Testing What Your Rules Actually Match
Gemini CLI has no built in equivalent of git check-ignore, which makes it easy to write a rule that silently does nothing. Because the syntax matches Git’s, you can borrow Git’s tooling to verify.
List every file that will still be included after your rules apply:
git ls-files -c -o --exclude-from=.geminiignore
On the project above, that output dropped to exactly what I wanted the model to see:
wp-content/themes/basic-wp/functions.php
wp-content/themes/basic-wp/header.php
wp-content/themes/basic-wp/footer.php
wp-content/themes/basic-wp/index.php
wp-content/themes/basic-wp/single.php
wp-content/themes/basic-wp/page.php
wp-content/themes/basic-wp/lib/acf.class.php
wp-content/themes/basic-wp/lib/enqueue.class.php
wp-content/themes/basic-wp/lib/helpers.php
wp-content/themes/basic-wp/lib/hooks.php
wp-content/themes/basic-wp/lib/nav.php
wp-content/themes/basic-wp/static/scss/theme.scss
wp-content/themes/basic-wp/template-about.php
wp-content/themes/basic-wp/template-contact.php
To check the inverse, everything being excluded:
git status --ignored
Run the first command before and after writing your rules. The difference between the two lists is what you stopped paying for.
Context Caching
The second lever is context caching, which is off by default. Leaving it off means every turn in a long session resends the same project context and bills you for it again.
Turning it on changes the economics of exactly the workflow the CLI is built for: a long conversation against a stable codebase where the files barely change between turns. The tradeoff is that cached context has a lifetime and a storage cost of its own, so it pays off on sustained sessions and does very little for one off questions.
Pair it with the ignore rules rather than treating it as a substitute. Caching a bloated context just means paying to store the bloat.
What to Check First
- Look for media and binaries in your project tree. One video file can cost more than the entire codebase, so search for those before anything else.
- Write a
.geminiignorecovering media, fonts, compiled output, lock files and translations. - Verify with
git ls-files -c -o --exclude-from=.geminiignoreso you know the rules match what you think they match. - Enable context caching once the context is lean, for long working sessions.
The underlying principle applies to every coding agent, not just Gemini: the model should see source, and nothing that was generated from source. Keeping the context small also makes the output better, because the signal is not buried in minified CSS and base64.
References worth bookmarking: Google’s documentation on understanding and counting tokens and a tokenizer tool for checking individual files.