Back to blog
Engineering13 min read

How to Give an Agent the Right Files (And Only Those)

Agent file context: how to scope an AI coding agent to the right files without exposing .env secrets, private keys, git history, or customer data it was never meant to read.

NH
Nafiul Hasan
Founder, Prompt Architects

TL;DR: Giving an agent your whole repo isn't just wasteful, it's a real exfiltration risk: .env files, private keys, .git history, and credentials in config all get swept up. Ignore-file behavior differs by tool and a repo's own .gitignore doesn't automatically protect you. Scope explicitly, verify per tool, and give the agent only what the task needs.

Why Is "Just Read the Whole Repo" the Wrong Instruction?

Because it risks something worse than slow responses and a bigger bill: exfiltration. An agent that can read a file can act on it, paste it into a commit message, quote it back to you in an explanation, or summarize .git history that still contains a secret you thought you'd removed. "Give the agent the right files, and only those" is a security instruction wearing the clothes of a performance tip, and most advice online only covers the performance half.

The noise problem (slower responses, a bigger bill, a context window full of irrelevant files) is real, but it's the minor cost. It follows directly from how these tools are built: a file the agent can read is a file whose contents can appear anywhere the agent's output goes, including a tool call, a generated file, or a pull request description. Scoping what an agent can see is the actual control surface, and it works differently on every tool, which is why the general discipline of keeping an agent's context lean, covered separately in context engineering for coding agents, isn't the same job as keeping it safe.

What Actually Gets Swept In When You Say "Look at My Codebase"?

A blanket "here's my repo, help me with X" instruction routinely pulls in:

  • .env and .env.* files — the single most common leak, because they sit at the project root where a broad read naturally lands.
  • Private keysid_rsa, .pem files, anything that looks like a keypair, often left in a project during local setup and never cleaned up.
  • .git history — not just the current working tree. A secret committed once and later "removed" in a subsequent commit is still sitting in an earlier blob, reachable by anything that can read the repository's git objects, not only the files on disk today.
  • Customer data in test fixtures — seed data, exported rows, or sample payloads that got copied from a real environment to make a test realistic, and never scrubbed.
  • Credentials embedded in config — connection strings in docker-compose.yml, secrets in a .tfvars file, an API key hardcoded into a CI pipeline definition. None of these look like "the secrets file," so they don't get manually excluded the way .env does.

This is worse when the agent's instructions aren't entirely under your control either. A malicious comment in a file, a poisoned dependency's README, or untrusted output from a tool call can nudge an agent toward reading exactly the file you meant to keep off-limits; that's the specific mechanism covered in prompt injection attacks: how to protect your AI app. Scoping is the defense that holds even if the instruction itself gets compromised.

Does Your Repo's .gitignore Already Protect You?

Assume no until you've checked your own tool. This is the most common wrong assumption here, and it doesn't generalize.

Claude Code ships a setting called respectGitignore, but read what it actually controls: it keeps gitignored files out of the @ file picker in interactive mode. That's it. It has no effect on what the Read, Grep, Glob, or Bash tools can access while the agent is working. Your .gitignore quietly protects the manual autocomplete menu and nothing else.

Cursor takes the opposite default. Its own documentation states plainly: "Cursor automatically ignores files in .gitignore and the default ignore list below" for Agent, Tab, and Inline Edit, and for @ mention references. But the same page immediately qualifies it: "The terminal and MCP server tools used by Agent cannot block access to code governed by .cursorignore." A shell command the Agent runs inside its own session is not covered by the same boundary as its direct file reads.

Codex doesn't reference .gitignore at all for this purpose. It scopes by an OS-level sandbox instead: a workspace-write boundary that limits where it can write, with .git, .agents, and .codex directories additionally protected as read-only when they exist. That's a different mechanism solving a related but distinct problem (protecting tool configuration from being overwritten, not scoping what gets read).

Verified against each vendor's own docs, 3 September 2026
FeatureClaude CodeCursorCodex CLI
Auto-honors .gitignore for the agent's own file readsNo — @ picker UI onlyYes, by defaultN/a — no gitignore link
Dedicated ignore fileNo (use permissions.deny)Yes — .cursorignoreNo (sandbox roots instead)
Explicit deny-by-path config existsPartial — write-scoping only
Covers shell/terminal commands tooBash file commands, not arbitrary subprocessesNo — explicitly excludedYes — OS-level sandbox

The last row is the one that trips people up most. Every tool here has some mechanism, but none is complete alone, and each has a documented gap the others don't share.

How Do You Actually Block These Files, Tool by Tool?

Explicitly, using each tool's own mechanism. Nothing here does this for you automatically and completely, so set it up per project rather than trusting a default.

Claude Code uses permissions.deny in settings.json, with Read rules following gitignore-style pattern syntax. This is the example from Claude Code's own settings reference, verbatim:

{
  "permissions": {
    "deny": [
      "Read(./.env)",
      "Read(./.env.*)",
      "Read(./secrets/**)",
      "Read(./config/credentials.json)",
      "Bash(curl *)"
    ]
  }
}

A Read deny rule also blocks Edit and Write on the same path, so it stops the agent from reading or overwriting a denied file, and it covers file commands recognized inside Bash like cat, head, tail, and sed. It does not cover an arbitrary script that opens the file itself (a Python or Node process reading .env directly bypasses this), which is what the sandbox exists for if you need that guarantee.

Cursor uses a .cursorignore file at the project root, in .gitignore syntax:

# Environment and credentials
**/.env
**/.env.*
**/credentials.json
**/secrets.json
**/*.key
**/*.pem
**/id_rsa

Cursor also has a global ignore list in user settings for patterns you want applied to every project without repeating this file each time, and it maintains its own default ignore list on top of your .gitignore and .cursorignore combined (lockfiles, build caches, binaries). Remember the terminal-and-MCP gap from the section above: this file is not a complete boundary if the agent shells out.

Codex doesn't take a project-level ignore file for this. Instead, choose the sandbox mode deliberately: read-only for anything you want the agent to only inspect, workspace-write (the Auto preset) once you're ready for edits, scoped to the working directory. Codex's own security docs are explicit that workspace-write still keeps .git, .agents, and .codex protected as read-only whether they're directories or files, recursively.

How Do You Scope an Agent to a Subtree Instead of a Whole Repo?

Start narrower than you think you need, and widen deliberately rather than starting broad and hoping the ignore rules catch everything.

Claude Code's own security documentation describes the default this way: "In Manual mode, Claude Code can only write to the folder where it was started and its subfolders, and can't modify files in parent directories without explicit permission. In Manual mode, Claude Code also asks you before reading paths outside this boundary with the Read, Grep, and Glob tools." Starting the session inside the specific subdirectory the task touches, rather than at the repo root, gets you that boundary for free, no configuration required. Auto mode relaxes the read-outside-boundary prompt, which is exactly why the deny rules above matter more once you're not being asked each time.

The same discipline applies as an instruction, not just a starting directory:

Work only inside src/checkout/ and its subfolders for this task. Do not
read, list, or reference anything under .env*, secrets/, .git/, or any
config file with "credentials" or "secret" in its name, even if you
believe it's relevant. If you think you need something outside
src/checkout/, stop and ask me by name for that specific path first.

This does two things a bare "focus on checkout" instruction doesn't: it names the off-limits paths explicitly, and it converts an implicit boundary into an explicit stop-and-ask, which matters because an agent that infers it "probably needs" a config file will otherwise just go read it.

Why Are Instruction Files Multiplying Behind Your Back?

Because coding agents increasingly read each other's rule files by default, not just the one you wrote for them. That's only half the file-scoping picture: instruction files are expanding across a growing set of cross-tool bridges, and a stray secret pasted into one now has more readers than it used to.

Claude Code's /init command already reads Cursor rules (.cursor/rules/ or .cursorrules) and Copilot rules (.github/copilot-instructions.md) by default, folding the relevant parts into the generated CLAUDE.md. Set the environment variable CLAUDE_CODE_NEW_INIT=1 and /init additionally reads AGENTS.md, .devin/rules/, .windsurf/rules/ or .windsurfrules, and .clinerules. That's six more file locations feeding into one project's instructions than most teams realize exist.

The Devin side of that list changed hands recently: Cognition, the company behind Devin, acquired Windsurf, and docs.windsurf.com now redirects to docs.devin.ai. Per Devin's current docs, .devin/rules/ is now the preferred location, with .windsurf/rules/ kept as a fallback, and the legacy .windsurfrules "also still read." Both carry hard character limits: 12,000 per workspace rules file, 6,000 for the single global global_rules.md.

Claude Code's own auto-generated memory is a useful contrast: MEMORY.md loads at session start, but only its "first 200 lines... or the first 25KB, whichever comes first." Past that threshold, nothing loads automatically, and Claude Code actively nudges itself to keep the file short. That's the same principle this whole post argues for: bound what loads automatically, make the overflow a deliberate lookup instead of a silent read. What belongs in the file you write yourself is covered in CLAUDE.md best practices.

Audit every one of these files the same way you'd audit a .env: a rules file can hold a pasted API key or internal hostname just as easily, and it now has more automatic readers than it did a few months ago. An over-scoped instruction file also causes a different failure once a session is running: see why Claude Code loses track mid-task for what happens when too much of it competes for the same context window.

Does Giving the Agent More Context Always Help?

No, and the two biggest vendors don't even agree on where the context you do keep should sit.

Reasoning tokens are not free, and they're not separate from your budget. OpenAI's own docs state it directly: reasoning tokens "are not visible via the API" but "still occupy space in the model's context window and are billed as output tokens." If generation hits the max_output_tokens ceiling, you get a response with status of incomplete, and per OpenAI's docs, "this might occur before any visible output tokens are produced, meaning you could incur costs for input and reasoning tokens without receiving a visible response." Anthropic's extended-thinking docs say the same thing for Claude: "the tokens Claude spends reasoning are billed as output tokens, even when the thinking text isn't returned to you, and they count toward max_tokens alongside the response text." A prompt padded with files "just in case" doesn't only cost more; on a hard cap, it can cost you the entire visible answer.

Where to put the context you do keep is a genuine, unresolved split, not a solved problem. Anthropic's prompting guide: "Place your long documents and inputs near the top of your prompt, above your query, instructions, and examples," adding that "queries at the end can improve response quality by up to 30 percent in tests, especially with complex, multidocument inputs." Google's current Gemini 3 guidance says the same thing in almost the same words: "When providing large amounts of context (e.g., documents, code), supply all the context first. Place your specific instructions or questions at the very end of the prompt." OpenAI's guide recommends the opposite structure for its own four-part template, saying context "is usually best positioned near the end of your prompt, as you may include different context for different generation requests" — instructions first, data last.

Two vendors say data-first, one says data-last, and none of them is wrong for their own model; this is a real, documented disagreement, not a case where one side just hasn't caught up. The good news is that tight file scoping mostly sidesteps needing to resolve it. A prompt carrying three relevant files instead of thirty doesn't have a "where does the giant context block go" problem, because there's no giant context block.

Free Chrome Extension

Stop rewriting prompts. Start shipping.

Works with ChatGPT, Claude, Gemini, Grok, Midjourney, Ideogram, Veo3 & Kling. 4.8★ on the Chrome Web Store.

Create An Account

A Before-You-Point-An-Agent-At-A-Repo Checklist

Run this before the first message of a new session, not after something goes wrong:

  1. Name the actual subtree the task needs, and start the session there or write it into the first instruction explicitly.
  2. Add or confirm a deny rule or ignore file for .env*, private keys, and anything named "secret" or "credential," using your tool's actual mechanism, not a borrowed assumption.
  3. Decide whether .git history needs to be in scope at all. If the task doesn't require it, don't let the agent read it.
  4. Check test fixtures and seed data. Scrub anything copied from a real environment before the agent, or anyone, can see it.
  5. Audit instruction files too: CLAUDE.md, AGENTS.md, .cursor/rules/, .devin/rules/, and anything a cross-tool bridge reads automatically.
  6. Prefer fewer, correctly chosen files over a wide net "for safety." Safety is the scoping, not the volume.

Where Prompt Architects Fits, and Where It Doesn't

Prompt Architects doesn't scope an agent's file access; that's your coding agent's job, configured the way this post describes. What we do is help you write the instruction that tells the agent what to do once it's correctly scoped: our MCP server at mcp.prompt-architects.com/mcp connects to Claude Code, Claude Desktop, Claude.ai, Cursor, Codex, and Codex CLI (setup and scopes are covered in using MCP inside Claude Code), exposing prompt tools named improve, refine, shorten, and enhance so the scoping instruction itself, like the subtree example above, gets tightened before it reaches the agent. It never reads your repository or your chat history; it works on the prompt text you send it, and nothing else.

If you're building a library of scoping instructions you reuse across projects, that's exactly what a saved prompt with Variables is for: keep one template with the subtree path and the forbidden-paths list as placeholders, and fill them in per project instead of retyping the same warning every time.

Giving an agent the right files is a discipline, not a one-time setting: verify your tool's actual ignore mechanism, treat instruction files with the same suspicion as config files, and remember "more context" has a real ceiling on cost and quality, long before it becomes a security problem too.

Frequently asked questions

Free Chrome Extension

Stop rewriting prompts. Start shipping.

Works with ChatGPT, Claude, Gemini, Grok, Midjourney, Ideogram, Veo3 & Kling. 4.8★ on the Chrome Web Store.

Create An Account