Skip to content

Instantly share code, notes, and snippets.

@spideynolove
Last active March 11, 2026 13:42
Show Gist options
  • Select an option

  • Save spideynolove/904788d43bdec43a993fe5d5e92f8b94 to your computer and use it in GitHub Desktop.

Select an option

Save spideynolove/904788d43bdec43a993fe5d5e92f8b94 to your computer and use it in GitHub Desktop.
context-stack

Context Engineering Stack for Claude Code

Part of Anthropic Claude Code in Action

A practical context engineering stack built around three MCP tools, designed so Claude can work on large codebases efficiently across sessions without reloading everything each time.


The Problem

Every Claude Code session starts cold. To understand a codebase, Claude must re-read files or repack with repomix. For small tasks this is fine. For ongoing work on large codebases, it means constant reloading of context that hasn't changed.


Stack Overview

Layer Tool Purpose
Live Layer repomix (via mcporter) Pack and read current codebase content
Structural Layer mcp-knowledge-graph Persist architectural knowledge across sessions
Tool Access Layer mcporter Load MCP schemas on demand — not permanently
┌──────────────────────────────────────────────────────────┐
│  LIVE LAYER (this session)                                │
│  repomix.pack_codebase() → current code, compressed       │
├──────────────────────────────────────────────────────────┤
│  STRUCTURAL LAYER (persists across sessions)              │
│  knowledge-graph → entities, relations, decisions         │
├──────────────────────────────────────────────────────────┤
│  TOOL ACCESS LAYER (always available, zero overhead)      │
│  mcporter → on-demand MCP tools, no schema cost           │
└──────────────────────────────────────────────────────────┘

The Workflow

Session 1 — build the index:

  1. repomix.pack_codebase(compress: true) — get full codebase (~70% token reduction via Tree-sitter)
  2. Read and analyze output — extract components, services, modules
  3. aim_memory_store() — persist entities to knowledge graph
  4. aim_memory_link() — record typed relations (depends_on, calls, owns)

Session 2+ — query the index:

  • aim_memory_search("auth") — instant lookup, no repacking
  • aim_memory_get(["AuthService"]) — exact entity retrieval

On code change:

  • Repack only the changed module, update only affected graph entries

Why Read Source Code, Not Just READMEs

Using repomix to pack the mcp-knowledge-graph source revealed constraints the README didn't mention:

Detail Found in Source Impact
Full file rewrite on every write Large graphs degrade performance
Substring search only (no semantic) Naming consistency when storing is critical
No write locking Concurrent sessions can corrupt the graph
Project detection climbs 5 directory levels Affects which .aim/ is used

This changed how the tool gets used. README-only integration produces brittle setups.

The method: repomix --remote user/repo → analyze source → write skill based on actual behavior.


Installing an MCP ≠ Integrating It

A complete integration requires three things:

  1. Binary — installed under the correct Node version
  2. mcporter entry — wired into ~/.mcporter/mcporter.json
  3. Skill file~/.claude/skills/tool-name/SKILL.md teaching Claude when and how to use it

The skill file is the behavioral contract. Without it, Claude knows the tool exists but not when to use it, which calls to make in sequence, or how to interpret output.


Multi-Agent Pattern

Claude acts as orchestrator and reviewer, delegating execution to other CLI tools:

Task Tool Why
Long-context retrieval gemini-cli 1M token context window
Structured planning codex Strong at specification reasoning
Code execution claude-code subagent Best tool use and editing
Review main claude-code session Architecture decisions

Result handling is simple: stdout from the delegated bash call flows back to the main context automatically.


Files in This Gist

File Contents
README.md This overview
docs-context-stack.md Detailed architecture analysis
docs-multi-agent.md Subprocess delegation pattern
skill-repomix.md Claude skill file for repomix
skill-knowledge-graph.md Claude skill file for mcp-knowledge-graph
example-repomix.config.json repomix project config

Context Stack: Architecture Analysis

What mcporter alone doesn't solve

mcporter provides on-demand MCP access — tool schemas load only when called, keeping the main context clean. But mcporter has no memory. Every session starts cold.

Two dimensions of codebase context

             NOW ←─────────────────────────→ ACROSS TIME
             │                                      │
          repomix                          knowledge-graph
   (what the code looks like today)    (what we've learned about it)

repomix answers: what does the code contain right now?

  • High fidelity, full implementation detail
  • Ephemeral — session-scoped
  • Token cost scales with codebase size (offset ~70% by Tree-sitter compression)

knowledge-graph answers: what have we mapped about this system?

  • Low fidelity — entities and typed relations, not raw code
  • Persistent — survives session restarts
  • Token cost is constant per query regardless of codebase size

The combination

Session 1:
  repomix.pack_codebase()                ← one-time content scan
  → extract entities, relations
  → aim_memory_store(), aim_memory_link() ← persist the structure

Session 2+:
  aim_memory_search("auth")              ← no repack needed
  aim_memory_get(["AuthService"])        ← instant lookup

Where the combination falls short

  1. No automated bridge — Claude must manually drive extraction from repomix output to graph entries. No hook auto-populates the graph on pack.
  2. Graph staleness — knowledge-graph doesn't know when code changes. Stale entries require manual updates.
  3. Keyword search limits — if you stored "Handles JWT auth" but later search "authentication", it won't match. Consistent naming when writing is required.
  4. repomix outputId is session-scoped — MCP tool output doesn't persist between sessions; use CLI (repomix) to write to disk when persistence matters.

Future direction: automated bridge

Hook: SessionStart for known project
  → if .aim/ exists but graph is stale (mtime < git HEAD)
    → launch analysis subagent:
        1. repomix.pack_codebase(compress: true)
        2. extract changed components
        3. aim_memory_add_facts() for delta
        4. report: "graph updated for N changed modules"

Not implemented. Documented as a direction worth pursuing.

Multi-Agent Pattern: Subprocess Delegation

Core idea

Claude doesn't have to execute every task. It can delegate to specialized CLI tools and review the output.

claude-code (main)
  ├── plan and decompose
  ├── spawn subagents via Bash tool
  └── review all outputs

subagent (bash subprocess)
  ├── gemini-cli → long-context retrieval, vision tasks
  ├── codex      → structured planning and specs
  └── claude -p  → non-interactive review pass

How result handling works

When Claude runs bash("gemini -p 'task...'"), the stdout flows back automatically into Claude's context. No orchestration infrastructure needed. No manual trigger.

This is the MCP_MANAGEMENT pattern: isolate tool execution in a subagent so the main context only sees the result, not the tool schema overhead.

Multiple Claude instances — safe?

Each claude -p "..." call is stateless. No shared memory between instances.

Conflicts only arise if two instances write to the same file simultaneously. Avoid with:

  • separate tmp directories per agent
  • git worktrees for parallel feature work

Architecture for complex pipelines

ORCHESTRATOR (main claude-code session)
  receives goal → decomposes → routes → reviews

SPECIALIZED AGENTS (subprocesses)
  Planning:    codex or claude subagent (specs, architecture)
  Frontend:    claude-code subagent (React, TypeScript)
  Backend:     gemini-cli (large context, existing codebases)
  Tests:       bash + test runner
  Review:      claude -p (non-interactive, stateless)

SHARED STATE (filesystem)
  git commits, tmp files, .aim/knowledge-graph

Sophisticated orchestration (state machines, retry queues) is only needed for true parallelism with shared state. Sequential pipelines need nothing beyond Claude deciding which agent to call next.

{
"output": {
"filePath": "repomix-output/repo.xml",
"style": "xml",
"compress": false,
"fileSummary": true,
"directoryStructure": true
},
"ignore": {
"useGitignore": true,
"customPatterns": [
"repomix-output/**",
"dist/**",
"node_modules/**",
"*.lock",
".next/**"
]
},
"tokenCount": {
"encoding": "o200k_base"
}
}

mcp-knowledge-graph — Persistent Codebase Knowledge

When to invoke

Use this skill when:

  • Starting work on a codebase you want to remember across sessions
  • Extracting and persisting architectural facts (components, dependencies, decisions)
  • Building a project knowledge graph from a repomix-packed codebase
  • Querying previously stored architectural context instead of re-reading files

Do not use for: general conversation memory (use episodic-memory), temporary session notes, or storing raw code (store facts about code, not the code itself).


The combo workflow: repomix → analyze → knowledge-graph

1. repomix.pack_codebase()           ← get full codebase content
2. grep/read the output              ← extract architectural facts
3. aim_memory_store()                ← persist entities (components, modules, services)
4. aim_memory_link()                 ← persist relations (depends_on, calls, owns)
5. aim_memory_add_facts()            ← add observations to existing entities

In subsequent sessions: skip steps 1-2, query directly:

6. aim_memory_search("auth")         ← find relevant entities by keyword
7. aim_memory_get(["AuthService"])   ← exact lookup of known entity

This saves repacking the entire codebase every session — the graph acts as a persistent index.


All 10 tools via mcporter

Tool Purpose
aim_memory_store Create new entities (components, people, concepts)
aim_memory_link Create typed relations between entities
aim_memory_add_facts Append observations to existing entities
aim_memory_forget Delete entities (cascades to their relations)
aim_memory_remove_facts Delete specific observations from an entity
aim_memory_unlink Delete specific relations
aim_memory_read_all Dump entire knowledge graph for a context
aim_memory_search Substring search across names, types, observations
aim_memory_get Exact lookup by entity name(s)
aim_memory_list_stores List all databases (project-local + global)

Example: build a codebase graph

# Create entities
mcporter call knowledge-graph.aim_memory_store(
  context: "my-project",
  location: "project",
  entities: [
    {"name": "AuthService", "entityType": "service", "observations": ["Handles JWT auth", "Depends on UserRepository"]},
    {"name": "UserRepository", "entityType": "repository", "observations": ["PostgreSQL", "Owns user table"]}
  ]
)

# Link them
mcporter call knowledge-graph.aim_memory_link(
  context: "my-project",
  location: "project",
  relations: [
    {"from": "AuthService", "to": "UserRepository", "relationType": "depends_on"}
  ]
)

# Query later
mcporter call knowledge-graph.aim_memory_search(query: "auth", context: "my-project")
mcporter call knowledge-graph.aim_memory_get(names: ["AuthService"], context: "my-project")

Storage mechanics (important for correct usage)

  • File location: project-local → .aim/memory-{context}.jsonl in project root; global → ~/.aim/memory-{context}.jsonl
  • Auto-detection: if .aim/ directory exists in project root, project-local is used automatically
  • Full rewrite: every write operation rewrites the entire file — keep graphs focused, not massive
  • No concurrency: do not run two sessions writing to the same graph simultaneously
  • Safety marker: every file starts with {"type":"_aim","source":"mcp-knowledge-graph"} — do not edit files manually unless you preserve this

Entity and relation design

Entity: {name, entityType, observations[]}
- name: unique identifier (e.g. "AuthService", "UserController")
- entityType: semantic category (service, repository, module, concept, decision, person)
- observations: string[] of facts — append via aim_memory_add_facts

Relation: {from, to, relationType}
- relationType: active-voice verb (depends_on, calls, owns, extends, implements, manages)
- directional: from→to and to→from are different

Naming conventions:

  • Entity names: PascalCase for code components, snake_case for concepts
  • Relation types: snake_case verbs
  • Context names: short, consistent (work, my-project, infra) — not my-work-stuff

Search behavior (know the limits)

  • aim_memory_searchsubstring match, case-insensitive, across name + type + observations
  • aim_memory_getexact name match only, case-sensitive
  • No semantic/vector search — if you stored "AuthService handles JWT", searching "authentication" will NOT find it; "JWT" will
  • Implication: use consistent, predictable observation wording when storing facts

Initializing a project graph

To enable project-local storage, create .aim/ in the project root first:

mkdir .aim

Then all calls with location: "project" (or auto-detection) will store in .aim/memory.jsonl.

Add .aim/*.jsonl to .gitignore unless you want to commit the graph (committing is valid for team-shared context).


Limitations

  • Large graphs degrade performance (full in-memory load per operation)
  • No append mode — each write rewrites the whole file
  • No fuzzy or semantic search — keyword precision matters
  • No versioning or rollback built-in
  • Project detection searches up 5 directory levels for .git, package.json, .aim, etc.

Repomix — Broad Codebase Context

When to invoke this skill

Invoke repomix before any task that requires whole-repository understanding:

  • Writing specs or architecture docs that reference multiple directories
  • 04-uiux planning stage (before IntentSpec, LayoutSpec, TokenSpec)
  • Reviewing consistency across many files
  • Answering "how does X work across the codebase" questions
  • Onboarding to an unfamiliar codebase

Do not use for single-file edits, simple bug fixes, or tasks where you already have the relevant files open.


MCP tools (preferred — no file I/O required)

All tools are available via repomix.* through mcporter.

Tool Use case
pack_codebase Pack a local directory into a single context blob
pack_remote_repository Pack a GitHub repo by URL or user/repo shorthand
read_repomix_output Read a specific line range from a previously packed output
grep_repomix_output Search packed output without re-reading everything
file_system_read_file Read a single file via repomix's filesystem access
file_system_read_directory List directory contents via repomix

Typical MCP invocation pattern

# Pack this repo (compress=true saves ~70% tokens, preserves signatures)
mcporter call repomix.pack_codebase(directory: "/home/hung/Public/SPIDEY/claude-code-in-action", compress: true)

# Search the packed output for a pattern
mcporter call repomix.grep_repomix_output(outputId: "<id from above>", pattern: "IntentSpec")

# Read a specific section
mcporter call repomix.read_repomix_output(outputId: "<id>", startLine: 100, endLine: 200)

Remote repo invocation

mcporter call repomix.pack_remote_repository(remote: "yamadashy/repomix", compress: true)
mcporter call repomix.pack_remote_repository(remote: "https://github.com/user/repo", compress: false)

CLI usage (when you need to write output files)

# Pack current directory to XML (AI-optimized, default)
repomix

# Pack to markdown (human-readable)
repomix --style markdown -o repomix-output/summary.md

# Pack with compression (Tree-sitter, ~70% fewer tokens)
repomix --compress

# Pack remote repo
repomix --remote user/repo
repomix --remote user/repo --remote-branch main -o output.xml

# Use a config file
repomix --config repomix.config.json

Config file format

Create repomix.config.json in the project root:

{
  "output": {
    "filePath": "repomix-output/repo.xml",
    "style": "xml",
    "compress": false,
    "removeComments": false,
    "showLineNumbers": false,
    "fileSummary": true,
    "directoryStructure": true
  },
  "ignore": {
    "useGitignore": true,
    "useDotIgnore": true,
    "customPatterns": ["repomix-output/**", "*.lock", "dist/**", "node_modules/**"]
  },
  "tokenCount": {
    "encoding": "o200k_base"
  }
}

Output style guidance:

  • xml — structured format, best for AI parsing (default)
  • markdown — human-readable summaries, good for docs
  • json — machine-parseable, good for programmatic use
  • plain — simple text, minimal overhead

Compression (compress: true): Uses Tree-sitter to extract function signatures and structure, discarding implementation bodies. Reduces tokens ~70%. Use for large repos or when full code isn't needed.


04-uiux workflow integration

In the 04-uiux workflow, run repomix at the start of whole-repo tasks (before IntentSpec when working across multiple components or doing a full redesign).

cd /home/hung/Public/SPIDEY/claude-code-in-action/04-uiux
repomix  # uses .repomixrc / repomix.config.json automatically

Output files land in repomix-output/. Reference them in subsequent spec stages:

  • repomix-output/repo.xml — full context for AI consumption
  • repomix-output/summary.md — human-readable summary

When using MCP tools during 04-uiux tasks, prefer pack_codebase with compress: true for initial survey, then grep_repomix_output for targeted lookups.


Token limits

repomix uses o200k_base encoding by default (matches GPT-4o / Claude tokenization closely). Check token count in the output header. If output exceeds ~100k tokens, enable compression or narrow with includePatterns.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment