DEV Community

Cover image for How I Manage Claude Code Context Across 20+ Repositories
Glenn Gray
Glenn Gray

Posted on • Originally published at graycloudarch.com

How I Manage Claude Code Context Across 20+ Repositories

This post was originally published on graycloudarch.com.


Three months ago I was re-explaining my Terragrunt state backend to
Claude for the third time in a week. Different session, same repo, same
repo I'd worked in the session before. Claude had no idea I was even in
the same project.

I run Claude Code daily across a 6-account AWS platform monorepo, a
personal consulting site, homelab infrastructure, and a handful of side
projects. Every session started with the same five minutes of "here's
the project, here are the conventions, here's the Jira workflow" --- and
still ended with Claude suggesting patterns that didn't fit the
environment, because I'd inevitably forgotten to mention something.

After three months of broken symlinks and abandoned experiments, I
landed on a three-tier context hierarchy that loads the right context
automatically depending on which directory I'm working in --- and I manage
all of it from a single dotfiles repo.

The Problem with Single-File Context

Claude Code loads CLAUDE.md from the current directory
(and parent directories, walking up to
~/.claude/CLAUDE.md). Most teams start with one file and
put everything in it.

That breaks down quickly:

  • Global preferences get mixed with project specifics. Your "use snake_case for variable names" preference shouldn't live next to your Terraform state bucket configuration.
  • Credentials and account IDs end up in files you accidentally commit. Put AWS account IDs in a shared CLAUDE.md, and someone will eventually push it.
  • You can't share patterns across repos without duplication. Every new repo gets a fresh copy of the same conventions, and updates never propagate.
  • Multi-employer context creates conflicts. Your consulting client's Jira workflow shouldn't contaminate your personal project sessions.

My first attempt at fixing this was a shared scripts directory. The
three-tier hierarchy came later, after I figured out what was actually
wrong with the simpler approach.

My First Attempt: A Shared Scripts Directory

Before landing on the three-tier system, I built something more
obvious: a ~/shared-claude-infra/ directory containing a
setup-project.sh script that initialized
.claude/ context for each new repo.

The script created the directory structure and symlinked a
rules/shared/ folder back to the shared repo:

::: {#cb1 .sourceCode}

mkdir -p "$PROJECT_DIR/.claude/rules"
ln -s ~/shared-claude-infra/rules "$PROJECT_DIR/.claude/rules/shared"
Enter fullscreen mode Exit fullscreen mode

:::

This worked for the first two repos I configured. Then the problems
compounded:

  • Manual per-project setup. Every new repo required running the script. Miss one, and that repo has no shared context.
  • Two repos to maintain. The shared infrastructure lived in its own git repo, separate from dotfiles. Two places to update when conventions changed, and they drifted.
  • Nested symlinks instead of directory-level symlinks. The rules/shared symlink lived deep inside the project's .claude/ tree. When the target moved, every project that had run the script got a broken symlink --- silently.
  • Hardcoded paths that drifted. The script referenced workspace paths from three months earlier. My actual directory layout had changed; the script still pointed at the old locations.

When I eventually deleted the shared directory, a quick
find confirmed broken symlinks scattered across every repo
that had run the setup script. The approach was inherently fragile
because it depended on every machine, every repo, and every workspace
path staying synchronized manually.

The fix isn't a smarter script. It's inverting the relationship:
instead of a script that runs once per project, use dotfiles that wire
context automatically based on what directories exist.

**Working through something similar?** I advise platform teams on AWS infrastructure â€" multi-account architecture, Transit Gateway, EKS, and Terraform IaC. [Let's talk.](https://graycloudarch.com/#contact)

The Three-Tier Hierarchy

~/.claude/CLAUDE.md              ← Global: preferences, style, git workflow
~/work/{employer}/.claude/       ← Org: team structure, AWS accounts, Jira workflow
~/work/{employer}/{repo}/.claude/ ← Project: repo architecture, active tickets
Enter fullscreen mode Exit fullscreen mode

Claude Code walks up the directory tree loading
CLAUDE.md files at each level. Each tier handles a specific
scope:

Global tier (~/.claude/): Everything
that applies across all work --- communication style, git commit format,
PR description templates, universal infrastructure patterns. No
credentials, no account IDs, nothing employer-specific.

Org tier (~/work/{employer}/.claude/):
Team structure, Jira project keys, AWS account layout, CI/CD pipeline
conventions. Sensitive patterns (account IDs, VPC IDs, state bucket
names) go in gitignored files within this directory. Reusable patterns
(CI/CD templates, AWS patterns without specifics) go in committed
files.

Project tier
(~/work/{employer}/{repo}/.claude/): Architecture decisions
for this specific repo, active tickets, ongoing work state. Always
gitignored --- this is ephemeral working context that changes
frequently.

Implementation: Symlinks from Dotfiles

The hierarchy only works if it's consistent across machines. I manage
all context files from a dotfiles repo using symlinks:

dotfiles/claude/
├── global/          → symlinked to ~/.claude/
├── {employer}/      → symlinked to ~/work/{employer}/.claude/
└── {personal}/      → symlinked to ~/personal/.claude/
Enter fullscreen mode Exit fullscreen mode

install.sh wires these automatically:

::: {#cb4 .sourceCode}

# Global context
ln -sf "$DOTFILES/claude/global" "$HOME/.claude"

# Per-employer context
for employer in "${EMPLOYERS[@]}"; do
  WORK_DIR="$HOME/work/$employer"
  if [ -d "$WORK_DIR" ]; then
    ln -sf "$DOTFILES/claude/$employer" "$WORK_DIR/.claude"
  fi
done
Enter fullscreen mode Exit fullscreen mode

:::

Any machine that runs install.sh gets the same context
hierarchy. Changes committed to dotfiles propagate immediately.

What Each Level Contains

Global (~/.claude/)

~/.claude/
├── CLAUDE.md          # Preferences, active work summary
└── rules/
    ├── git-workflow.md
    ├── pr-patterns.md
    ├── infrastructure.md
    └── context-management.md
Enter fullscreen mode Exit fullscreen mode

CLAUDE.md is short --- preferences and a pointer to where
active work lives. The heavy lifting goes in rules/ files
that Claude loads as supplementary context.

::: {#cb6 .sourceCode}

## Communication Style
- Be direct and technical — I understand infrastructure concepts
- Explain the "why" behind decisions
- Provide specific file paths and line numbers

## Git Workflow
- Branch format: feat/TICKET-123-description
- Commit format: [TICKET-123] Brief summary\n\nWhy this change...
- Never add Co-Authored-By trailers
Enter fullscreen mode Exit fullscreen mode

:::

Org Level (~/work/{employer}/.claude/)

~/work/{employer}/.claude/
├── {EMPLOYER}.md              # Team structure, Jira workflow — committed
└── rules/
    ├── cicd-patterns.md       # CI/CD conventions — committed
    ├── aws-patterns.md        # Account IDs, VPC IDs — GITIGNORED
    └── terraform-patterns.md  # State config, module paths — GITIGNORED
Enter fullscreen mode Exit fullscreen mode

The privacy split matters. cicd-patterns.md contains
reusable GitHub Actions patterns --- fine to commit.
aws-patterns.md contains actual account IDs --- stays
local.

A typical employer context file:

::: {#cb8 .sourceCode}

## Team Structure
- Platform team: 5 engineers, all in Jira project IN
- AWS accounts: dev, nonprod, prod (+ 3 infra accounts network, security, management)
- Monorepo: ~/work/{employer}/iac — Terragrunt, 78 components

## Jira Workflow
- IN project (infrastructure): transition IDs 3=In Dev, 8=Needs Review, 9=Done
- Prefix all commits: [IT-XXX]
- API: REST v2 only — v3 silently returns empty responses

## CI/CD
- GitHub Actions with OIDC to AWS (no long-lived credentials)
- PR requires: terraform plan output posted as comment
- Merge to main triggers auto-deploy to nonprod
Enter fullscreen mode Exit fullscreen mode

:::

The gitignored aws-patterns.md contains account IDs and
specific ARNs that Claude needs for generating Terraform configurations
accurately but shouldn't be committed anywhere.

Project Level (~/work/{employer}/{repo}/.claude/)

Project context is ephemeral and always gitignored. It's the working
memory for an ongoing effort:

::: {#cb9 .sourceCode}

## Current State
- Branch: feat/IT-89-my-app-dev-ecr
- Active ticket: IT-89 — ECR in dev
- Next: IT-90 — ECS task definition

## Architecture Decisions
- ECR in dev only; cross-account pull policies for nonprod and prod
- Mutable tags in dev, immutable in nonprod/prod
- KMS key per environment, not per repository

## Blockers
- Waiting on network DNS zone creation before cutover can proceed
Enter fullscreen mode Exit fullscreen mode

:::

I update this file at the end of each session with current state so
the next session loads instantly without re-explaining where things
are.

The Privacy Model

The critical insight is that context files need two categories:
committed (shareable) and local-only (sensitive).

Content Location Committed?


Personal preferences ~/.claude/CLAUDE.md
Git workflow rules ~/.claude/rules/git-workflow.md
Team structure {employer}/.claude/{EMPLOYER}.md ✅ sanitized
CI/CD patterns {employer}/.claude/rules/cicd-patterns.md
AWS account IDs {employer}/.claude/rules/aws-patterns.md ❌ gitignored
VPC IDs, state config {employer}/.claude/rules/terraform-patterns.md ❌ gitignored
Active ticket state {repo}/.claude/OVERRIDES.md ❌ gitignored

The .gitignore at the dotfiles level handles this
automatically by ignoring **/aws-patterns.md and
**/terraform-patterns.md across all employer
directories.

Custom Commands

Beyond context files, Claude Code supports custom
/commands --- reusable prompts stored as markdown files:

~/.claude/commands/
├── checkpoint.md       # Create context snapshot
├── sync-work.md        # Update active work status
└── pr-ready.md         # Generate PR description
Enter fullscreen mode Exit fullscreen mode

A command file is just the prompt Claude should execute:

::: {#cb11 .sourceCode}

# checkpoint.md
Create a context checkpoint. Read the current git status across active repos,
summarize open PRs and their status, list active tickets with their current
state, and write a structured summary to ~/.claude/local.md. Include any
blocking issues and the next planned action.
Enter fullscreen mode Exit fullscreen mode

:::

Commands at the global level are available everywhere. Org-level
commands handle employer-specific workflows like Jira transitions.

What This Solves in Practice

Before this system: every new Claude session started with "here's the
project, here are the conventions, here's where things are." Five
minutes of ramp-up, inconsistent outputs because I'd forget to mention
something.

After: I cd into a repo and Claude already knows the
Jira workflow, the AWS account structure, the naming conventions, and
where the active work stands. When I start a session mid-ticket, the
project-level context tells Claude exactly what was in progress.

The bigger payoff is consistency. When Claude generates Terraform, it
generates it with the correct state backend. When it writes commit
messages, they follow the format reviewers expect. When it suggests
architecture, it fits the actual account model rather than a generic AWS
example.

Starting Point

If you're starting from scratch, work tier by tier:

  1. Create ~/.claude/CLAUDE.md with your communication preferences and git conventions.
  2. Add a rules/ directory with patterns you want loaded consistently.
  3. Create an org-level directory when you start working with a specific employer or major project.
  4. Add project-level context when you start a multi-session effort.

Don't try to build the whole system at once. The global tier alone
eliminates most of the per-session ramp-up. The org and project tiers
pay off as work gets more complex.

The thing that surprised me most wasn't the time saved on ramp-up. It
was how much the output quality improved. When Claude knows the actual
state backend, the actual account IDs, the actual PR format your
reviewers expect --- the suggestions it makes fit your environment. That
gap between "technically correct" and "actually usable" is where most of
the friction in AI-assisted infrastructure work lives. The context
hierarchy is mostly just closing that gap.

If you're setting up Claude Code for a platform team and want to talk
through the context design, I do advisory
engagements
for teams getting serious about AI tooling in their
infrastructure workflow.

Top comments (0)