Multi-File Editing & Large Codebases
Context window size and file-loading syntax are the dominant variables when working with real codebases. This module stress-tests all three tools on a 50-file Node.js project — renaming a model, generating a new middleware file, and handling dangerous deletions.
Context Loading: How Each Tool Reads Your Files
BEFORE: You have a 50-file Node.js project. You want the AI to rename a model from User to Account. If the AI only sees one file at a time, it will rename the model in that file but miss every import, type annotation, test reference, and API route that also uses the name User.
PAIN: A partial rename is worse than no rename — your TypeScript compiler suddenly sees Account in one file and User in every other, breaking the entire type system. You now have to manually hunt down every usage.
MAPPING: All three tools have a mechanism for loading files into context — but they work completely differently. The difference between Claude Code's selective precision, Gemini CLI's bulk loading, and Copilot CLI's lack of a direct equivalent defines what each tool can accomplish on multi-file tasks.
Selectively adds files or directories to the active context. Claude reads them on demand and tracks which files it has seen. You can add individual files, glob patterns, or directories.
/add src/**/*.ts
→ Claude reads & tracks all
The @ prefix in your prompt inlines a file or directory's content directly. With a 1M context window, Gemini can ingest the entire project in one shot. You can also use @ inside GEMINI.md for always-loaded context.
@package.json tells me we use...
→ Gemini reads entire tree
Copilot CLI operates on your shell session context only. It cannot read project files, load a directory tree, or maintain multi-file awareness. For file-aware operations, you must use an agentic tool.
→ suggests: find/sed command
→ YOU run it across files
50-File Node.js Project — How Each Tool Handles It
Test project: a Node.js Express API with 50 TypeScript files totalling ~35,000 tokens. Well within any tool's context window individually — but all three handle it very differently.
Claude Code reads the directory structure first, identifies the most relevant files for the task (typically 10-15), and loads those. It then expands to adjacent files if it discovers cross-references. Result: precise understanding of the exact files involved, with minimal token waste.
Trade-off: may miss an obscure file that also uses the renamed model if it wasn't surfaced in the initial directory scan.
With @src/, Gemini ingests the entire src/ directory at once. All 50 files are in context simultaneously. It can cross-reference any file without a second pass. For this 35K token project, Gemini uses only 3.5% of its 1M window.
Trade-off: token cost is higher on very large projects; Gemini Flash is rate-limited at 1,500 req/day on the free tier.
Copilot CLI cannot read project files in its CLI mode. It can suggest grep or sed commands to find and replace strings, but it has no awareness of TypeScript types, imports, or logical groupings. A sed rename will also rename string literals, comments, and package names containing "User".
For projects under 150K tokens (~110K lines of code), both Claude Code and Gemini CLI can handle the full codebase. Above that threshold, Claude Code must work in chunks while Gemini CLI (with its 1M window) can still load everything. This is the most common reason teams with large monorepos adopt Gemini CLI as their primary refactoring tool.
Multi-File Rename: User → Account
Renaming a model across a codebase is a canonical multi-file operation. It requires: finding every import, every type annotation, every test reference, every database column name, and every API route — and updating them all consistently while not renaming unrelated strings like "username" or "UserAgent".
# ─── Claude Code ──────────────────────────────────────────────────────────────
claude
> /add src/
> Rename the User model to Account across all files.
> - Update imports, type annotations, test references, and API routes.
> - Do NOT rename 'username' fields or 'UserAgent' strings.
> - Show me a diff before writing.
# Claude reads directory → finds User usages in 14 files → shows diff → asks confirm
# ─── Gemini CLI ───────────────────────────────────────────────────────────────
gemini
> Rename the User model to Account in @src/
> - Update all TypeScript imports, type annotations, and test files.
> - Preserve 'username', 'UserAgent', 'createUser' function names.
# Gemini loads entire src/ → rewrites 14 files in one pass → reports changes
# ─── Copilot CLI ──────────────────────────────────────────────────────────────
gh copilot suggest "rename User model to Account across all TypeScript files"
# Copilot suggests:
# find src/ -name "*.ts" | xargs sed -i 's/\bUser\b/Account/g'
# Problem: \bUser\b regex also renames 'UserService', 'createUser', etc.
# No TypeScript type awareness — cannot distinguish 'User' the model from 'user' the variable.
# ─── Claude Code (PowerShell) ─────────────────────────────────────────────────
claude
# /add src/
# Prompt: Rename User model to Account across all files.
# Do NOT rename 'username' or 'UserAgent'. Show diff first.
# → Claude shows a colour diff, waits for confirmation
# ─── Gemini CLI (PowerShell) ──────────────────────────────────────────────────
gemini
# Prompt: Rename the User model to Account in @src/
# → Gemini bulk-loads all .ts files, rewrites in one pass
# ─── Copilot CLI (PowerShell) ─────────────────────────────────────────────────
gh copilot suggest "rename User model to Account in all TypeScript files"
# Suggested command:
Get-ChildItem -Recurse -Path src -Filter "*.ts" |
ForEach-Object { (Get-Content $_.FullName) -replace '\bUser\b','Account' |
Set-Content $_.FullName }
# Same caveat: regex has no TypeScript type context — may replace wrong occurrences.
A regex like \bUser\b cannot distinguish between User the TypeScript interface, createUser() the function, user the variable, and "User" in a JSON response field name. Claude Code and Gemini CLI understand the TypeScript AST semantics and rename at the correct granularity. Copilot CLI's suggested sed/Get-ChildItem command will likely produce a broken build that requires manual cleanup.
New File Generation: Context-Aware Middleware
The second test: generate a new rate-limiting middleware file that is consistent with the existing middleware already in src/middleware/. This requires reading existing conventions — function signatures, error response format, export style — before generating the new file.
# Prompt (identical for all tools):
# "Create a middleware file for rate limiting, consistent with
# the existing middleware in src/middleware/"
# ─── Claude Code result ───────────────────────────────────────────────────────
# Claude: reads src/middleware/ → sees auth.ts, cors.ts, logger.ts
# → matches their export pattern (export const X: RequestHandler)
# → matches error response shape ({ error: string, retryAfter: number })
# → generates src/middleware/rateLimit.ts, fully consistent
# ─── Gemini CLI result ────────────────────────────────────────────────────────
# Gemini: @src/middleware/ inlines all 3 existing middleware files
# → same analysis, generates consistent file
# → also adds Redis integration (detects redis dep in package.json via @package.json)
# ─── Copilot CLI result ───────────────────────────────────────────────────────
# gh copilot suggest "create a rate limiting middleware file for express"
# → suggests: npm install express-rate-limit
# + copy-paste snippet from express-rate-limit README
# → no awareness of YOUR existing middleware conventions
# → you must manually adjust to match your project's patterns
The key distinction here is context-aware generation versus generic snippet retrieval. Claude Code and Gemini CLI read your actual code to understand your conventions before generating. Copilot CLI in the terminal retrieves a generic pattern from its training data.
File Deletion Safety: How Each Tool Handles Danger
All three tools can suggest or execute file deletions. What happens when you ask them to clean up files — and what are the safety guarantees?
Before deleting any file, Claude Code displays the full path, explains why it's being deleted, and waits for your explicit yes confirmation. It will not batch-delete multiple files in a single unconfirmed step.
The CLAUDE.md ## Allowed operations section can expand or restrict defaults. Permission gates can also be configured in .claude/settings.json.
Gemini CLI's Plan Mode shows all planned changes — including deletions — before executing. Without Plan Mode, Gemini may proceed with deletions; with it enabled, every destructive step requires approval.
Recommendation: always use gemini --plan when performing operations that include file deletions.
Copilot CLI suggests the rm or Remove-Item command. You decide whether to run it. This is the safest model by design — no automated execution means no automated deletion. It also means no undo support — if you run rm -rf, that's on you.
# Claude Code:
> Delete all compiled .js files in src/ that have a .ts equivalent.
# Claude: scans src/, finds 12 .js/.ts pairs, lists them all
# Prompt: "Delete these 12 files? [y/N]"
# On 'y': deletes. On 'N': aborts cleanly.
# Gemini CLI (with --plan):
gemini --plan
> Delete all .js files in @src/ that have a matching .ts file.
# Plan: "I will delete: src/auth.js, src/routes.js, ... (12 files)"
# Approve? [y/N] → On 'y': executes. On 'N': discards plan.
# Copilot CLI:
gh copilot suggest "delete js files in src/ that have a typescript equivalent"
# Suggested:
find src/ -name "*.js" -exec sh -c \
'test -f "${1%.js}.ts" && echo "Would delete: $1"' _ {} \;
# → You review the list, then run again with -delete instead of echo
# → Full manual control; Copilot only helps you write the find command
# Claude Code:
# > Delete all compiled .js files in src/ that have a .ts equivalent.
# → Lists 12 files, requires y/N confirmation
# Gemini CLI:
# gemini --plan
# → Shows plan including all deletions, requires approval
# Copilot CLI:
gh copilot suggest "delete .js files in src that have a matching .ts file"
# Suggested:
Get-ChildItem -Path src -Filter "*.js" -Recurse |
Where-Object { Test-Path ($_.FullName -replace '\.js$', '.ts') } |
Select-Object FullName # review first, then pipe to Remove-Item to execute
Full Comparison Table
| Capability | Claude Code | Gemini CLI | Copilot CLI |
|---|---|---|---|
| Max context | 200K tokens (~150K LOC) | 1M tokens (~750K LOC) ★ | Shell session only |
| File loading syntax | /add src/ or /add **/*.ts |
@src/ inline in prompt |
No file context |
| Multi-file edit | Type-aware, selective, diffs shown | Bulk load + type-aware | Regex suggest only (no type awareness) |
| New file generation | Reads existing conventions, matches patterns | Same + detects deps via @package.json | Generic snippet, not context-aware |
| Undo support | Git-aware, shows diffs before write | Plan Mode checkpoint before execute | N/A — you run the command, you own undo |
| Deletion safety | Explicit y/N gate per deletion batch | Plan Mode gate (recommended, not default) | You run the command — inherently safe |
You now understand the file operation capabilities of each tool at a concrete level:
- For projects under 150K tokens: Claude Code and Gemini CLI are equally capable on multi-file tasks.
- For projects over 150K tokens: Gemini CLI's 1M context window becomes the decisive advantage.
- For TypeScript-aware renaming: both agentic tools are far safer than Copilot CLI's sed/regex suggestion.
- Copilot CLI's you-run-it model is the safest for destructive ops — but puts all reasoning responsibility on you.
Next: M06 covers agentic workflows — the biggest architectural difference between the tools. Subagents, Plan Mode, and what "agentic" actually means in practice.
Knowledge Check
Five questions on multi-file editing, context loading, and file operation safety.
sed -i 's/\bUser\b/Account/g' to rename a TypeScript model, as Copilot CLI might suggest?@package.json in your prompt. Why does this make its new file generation better?