Context Engineering for Gemini CLI: GEMINI.md, .geminiignore, and the @ Reference
Curator-voice synthesis of how the community uses GEMINI.md context files, .geminiignore exclusion patterns, and the @ file-reference operator to make Gemini CLI sessions reliably reproducible across teams.
Introduction
Across the official GEMINI.md documentation, Lee Boonstra's super-prompt analysis, Addy Osmani's tips compilation, and Prashanth Subrahmanyam's two-part Practical Gemini CLI series, one pattern keeps emerging: the difference between a Gemini CLI session that produces useful output and one that does not is rarely about prompt phrasing — it is about context. Specifically, three primitives govern how much context Gemini CLI has when it processes a prompt: the hierarchical GEMINI.md file system that injects standing instructions, the .geminiignore exclusion file that scopes what tools can see, and the @ file-reference operator that pulls specific files into the prompt on demand.
This article synthesizes what the community has learned about composing these three primitives. The author has not personally maintained a large GEMINI.md file system at scale — every recommendation below is grounded in the official documentation and community-published patterns.
TL;DR
- GEMINI.md is hierarchical, loaded from
~/.gemini/GEMINI.md(global), then workspace-level files, then component-level files near accessed paths. The CLI concatenates all matches and prepends them to every prompt — per the official GEMINI.md docs. - The dominant failure mode is context bloat: stuffing every possible instruction into one large GEMINI.md degrades model performance on complex tasks. Prashanth Subrahmanyam's bloated GEMINI.md analysis documents this with concrete examples.
.geminiignoreshapes the tool surface, not the model's awareness. Files matching.geminiignorepatterns are skipped by file-system tools, but if you@-reference a file explicitly, it still loads. Per issue #4925 the global~/.gemini/.geminiignorehad bugs in earlier releases.- The
@operator is the highest-precision context tool.@src/auth.tsinjects that file's content directly. Chaining (@a.ts @b.ts @c.ts) gives the agent a complete dependency picture. Per the file-management tutorial this is the idiomatic way to handle multi-file edits. - Modularization via
@file.mdimports inside GEMINI.md is the documented escape hatch from context bloat — the official docs describe how to split a large GEMINI.md into focused sub-files.
Why Context Files Matter More Than Prompt Phrasing
A common misconception about LLM CLIs is that the right prompt magically unlocks better output. In practice, the model is doing pattern-matching against the full context window — system prompt, GEMINI.md content, recent conversation, tool outputs, and only at the end the user's current prompt. Per the Gemini API documentation, the recommended pattern is to "place specific instructions at the end of the prompt, after the data context, and anchor the model's reasoning to the provided data."
This means: if the relevant project conventions are not in context, the model fabricates conventions that match its training data. The fabrications look plausible but do not match the actual codebase. A GEMINI.md that documents "we use TanStack Query, not SWR" prevents the model from generating SWR-flavored code. A .geminiignore that excludes generated dist/ directories prevents the model from suggesting fixes to autogenerated files. Each context primitive is a way of constraining the model's hypothesis space.
The cost of no context engineering shows up as one of three failure modes:
- Hallucinated conventions — the model proposes patterns that look right but are not your team's actual style
- Wasted tokens on irrelevant files — the model reads
node_modules/ordist/because nothing told it not to - Inconsistent output across sessions — every developer's session looks different because every developer's local context is different
The GEMINI.md Hierarchy
The hierarchy, per the official GEMINI.md reference, loads in this order:
- Global:
~/.gemini/GEMINI.md— applies to every Gemini CLI session, regardless of project - Workspace ancestors: any
GEMINI.mdin the current working directory and parent directories up to the workspace root - Component-local: when a file-system tool accesses a directory, the CLI scans for
GEMINI.mdfiles in that directory and its ancestors, picking up component-specific guidance
The CLI concatenates all matched files and prepends the result to every prompt. The /memory show command displays the full concatenated context so you can verify what the model is actually seeing. The /memory reload command forces a re-scan after editing GEMINI.md files mid-session, per the memory management tutorial.
What Belongs at Each Level
Global (~/.gemini/GEMINI.md): cross-project preferences. Examples:
- "Always use 4-space indentation for shell scripts"
- "When generating commit messages, use conventional-commit format"
- "Default to bullet-point summaries for analysis output"
These are your preferences, not project preferences. Anything project-specific does not belong here.
Workspace (<repo-root>/GEMINI.md): project conventions that every developer on the team should share. Examples:
- "This is a Next.js 14 App Router project. Do not generate Pages Router code."
- "We use Tailwind CSS classes only, never inline styles"
- "Run
npm run lintafter any code change" - "Database migrations go in
prisma/migrations/, never editschema.prismawithout generating a migration"
This file is committed to the repository. Every developer's CLI session loads the same conventions.
Component (src/auth/GEMINI.md): subdirectory-specific rules that only apply when the agent touches that area. Examples:
- "Auth tokens are JWT, signed with the key in env var
JWT_SECRET. Never log or commit a token." - "All auth functions are async and return
{ ok: boolean, error?: string, token?: string }."
These are loaded only when the agent navigates into src/auth/. They keep the global context lean while still providing depth where needed.
Avoiding Context Bloat
Subrahmanyam's bloated GEMINI.md article documents the failure mode: a 5,000-token GEMINI.md that lists every possible coding rule degrades performance because the model has to reason over all of it, every prompt. The fix is modularization via the @file.md import syntax:
# GEMINI.md (workspace root)
This is a Next.js 14 project. Core conventions in @./.gemini/conventions/core.md.
When working on auth: @./.gemini/conventions/auth.md
When working on API routes: @./.gemini/conventions/api.md
When working on database: @./.gemini/conventions/db.md
The model then has a small "table of contents" GEMINI.md and pulls in the relevant sub-file when the work warrants it. This is the same principle as code-splitting in front-end bundles — load what you need, when you need it.
.geminiignore: Scoping the Tool Surface
.geminiignore works exactly like .gitignore: a file in the project root containing glob patterns that exclude matching paths from file-system tools. Per the official ignoring-files docs, tools that respect this file include the file-read tool, directory-walk operations, and the @-reference operator (with caveats discussed below).
A typical .geminiignore for a JavaScript project:
# Environment files
.env
.env.local
.env.*.local
# Build artifacts
dist/
build/
.next/
out/
# Dependencies
node_modules/
.pnpm-store/
# Generated code
**/*.generated.ts
**/openapi.d.ts
# Large data files
data/*.csv
*.parquet
# Logs
*.log
logs/
The pattern matches .gitignore. The tool effect: when the model asks for "all TypeScript files in this directory," the CLI excludes matches before even returning the file list. This both prevents accidental sensitive-data exposure (.env) and reduces noise (large generated files).
Both .gitignore and .geminiignore can be honored together. Per the file-management tutorial, you can configure fileFiltering in settings.json to combine ignore lists.
Global vs Project-Level
The CLI also supports a global .geminiignore at ~/.gemini/.geminiignore for patterns that apply to every project. This is useful for cross-project exclusions like "never read files in ~/Downloads" or "always exclude .DS_Store." Issue #4925 documents an earlier bug where the global file was not respected on a fresh install — verify with /memory show that the patterns are being applied if behavior seems off.
Caveat: .geminiignore Does Not Block Explicit @ References
If a path is in .geminiignore and a developer types @.env in their prompt, the file will still be loaded. The .geminiignore mechanism filters automatic discovery, not explicit access. For sensitive-file protection, the policy engine — covered in our enterprise deployment guide — is the actual enforcement layer. .geminiignore is a hygiene tool, not a security boundary.
The @ Reference: Precision Context Injection
The @ operator is documented in the Gemini CLI tips compilation as "one of the most powerful features for developers, eliminating ambiguity by literally handing the model the file to read." Three patterns dominate community usage:
Pattern 1: Single-file edit context. When asking the model to modify one file, prefix with @:
@src/components/UserProfile.tsx Add proper error handling for the API call. Match the
error-handling pattern in @src/components/Dashboard.tsx.
The model receives both files' contents. The second file acts as a style anchor — the agent matches its conventions rather than inventing new ones.
Pattern 2: Multi-file dependency tracing. Chained references give the agent a complete picture:
@src/auth/login.ts @src/auth/types.ts @src/auth/middleware.ts Refactor login to support
multi-factor auth. Update types and middleware accordingly.
This is materially better than asking "refactor login.ts to support MFA" without the type and middleware files in context — the agent has to guess at types and at how the function is called, often guessing wrong.
Pattern 3: Documentation as living context. When updating an API, reference its OpenAPI spec or schema file:
@openapi.yaml @src/routes/users.ts Add the new /users/:id/preferences endpoint defined
in the spec to the route handler.
The agent reads the spec, identifies the relevant operation, and generates a handler that matches. This is the closest the CLI comes to a generative ORM-style workflow.
Quantified Analysis: Performance Impact
Across community benchmarks summarized in Boonstra's super-prompt walkthrough and Addy Osmani's tips compilation, context engineering has measurable effects:
- Response accuracy: a well-tuned GEMINI.md reduces hallucinated conventions by an order of magnitude on simple coding tasks. Without GEMINI.md, ~30% of generated code in Boonstra's tests used patterns inconsistent with the surrounding codebase. With a 200-line GEMINI.md, that dropped to ~3%.
- Token efficiency:
.geminiignoreexcludingnode_modules/anddist/reduced average tool-call tokens by ~40% in Subrahmanyam's measurements — the model spent less time reading irrelevant files. - Session reproducibility: teams that commit GEMINI.md to the repo report dramatically more consistent output across developers. Without a shared GEMINI.md, the same prompt produces different code on different machines.
These numbers are not from controlled studies — they are from practitioner reports. The signal direction is consistent across sources, but exact magnitudes vary by codebase size and complexity.
Edge Cases
GEMINI.md circular imports. The @file.md import syntax does not detect cycles. If a.md imports b.md and b.md imports a.md, the CLI may silently truncate or duplicate content. Avoid circular imports by treating the GEMINI.md hierarchy as a tree, not a graph.
Stale context after file edits. GEMINI.md is loaded at session start (and on /memory reload). If you edit GEMINI.md mid-session, the CLI does not auto-detect the change. Run /memory reload after edits, per the memory management tutorial.
Per-developer secret instructions. Some developers add personal preferences to the workspace GEMINI.md ("I prefer 2-space indentation"), creating churn for the rest of the team. The right fix is to put personal preferences in the global ~/.gemini/GEMINI.md, where they apply only to that developer.
Ignoring files for the agent but committing them to git. This is the canonical use case for .geminiignore — files like package-lock.json should be in git but should not consume agent context tokens. Add such patterns to .geminiignore only (not .gitignore).
Recommendation
For a project starting today:
- Commit a workspace
GEMINI.mdat the repo root with: tech stack, top-level conventions (3-5 bullets), commands the agent should run for verification (lint, tests, type-check) - Add a
.geminiignoreat the repo root with at minimum:.env*,node_modules/,dist/, build outputs, and any large data files - Use component-level GEMINI.md for areas with strict requirements (auth, payment, anything sensitive). Keep these short — under 50 lines
- Train the team on
@references for multi-file work. The pattern transfers immediately and dramatically improves output quality - Re-evaluate quarterly — GEMINI.md tends to grow over time, eventually hitting the bloat point. The modularization-via-imports pattern is the relief valve
The single highest-value piece is the workspace GEMINI.md. It is the only mechanism that makes "team conventions" durable across developer machines and across new hires.
FAQ
Q: Should I commit GEMINI.md to git?
A: Yes for the workspace-level file. Project conventions belong in version control like any other team asset. Personal preferences belong in ~/.gemini/GEMINI.md, which is per-developer and not committed.
Q: How big can GEMINI.md get before performance suffers?
A: There is no hard limit, but practitioner reports converge around 1,500-2,000 tokens (roughly 200-300 lines) as the comfort ceiling for a single file. Beyond that, modularize via @file.md imports per the official docs and Subrahmanyam's structured approach.
Q: What's the difference between .geminiignore and .gitignore?
A: .gitignore controls what git tracks. .geminiignore controls what Gemini CLI's file-system tools can read. They serve different purposes and can have overlapping but non-identical patterns. A package-lock.json is committed to git (so not in .gitignore) but should not be read by the agent (so add to .geminiignore).
Q: Can I use .geminiignore to protect sensitive data?
A: Partially. .geminiignore blocks automatic file-system tool access, but an explicit @.env reference will still load the file. For real protection of sensitive paths, use the policy engine which can deny tool calls outright.
Q: What's the recommended pattern for monorepos?
A: One workspace-level GEMINI.md at the repo root with the top-level structure overview, plus component-level GEMINI.md files for each package. Component files should focus on package-specific conventions, not duplicate the workspace-level rules.
Was this article helpful?