Overview
Aider's most powerful system is its PageRank-based repo map — an automatic context selection mechanism that builds a directed graph of symbol definitions and references across an entire codebase, then uses personalized PageRank to rank files by relevance to the current conversation, and renders the top-ranked definitions as scope-aware elided code views that fit within a token budget.
When Hermes Agent works on code, it relies on the user or agent to manually read_file and search_files to find relevant context. There's no automatic awareness of codebase structure — the agent has to guess which files matter. Aider solves this: before every LLM call, it computes which symbols and files are most relevant to the files being edited and includes a compact structural overview in the prompt. This is fundamentally different from both regex search (#489's semantic search via embeddings) and explicit @ references (#502) — it's automatic, graph-based, zero-effort context that adapts to the conversation.
Source: Deep analysis of aider/repomap.py (781 lines), tree-sitter query files, and integration with the Coder classes.
Research Findings
How Aider's Repo Map Works (Step by Step)
The algorithm has 6 phases:
Phase 1: Symbol Extraction (tree-sitter)
For each file in the repo, Aider:
- Determines the language from file extension
- Loads a tree-sitter parser and
.scm query file (stored in aider/queries/)
- Parses the file into an AST
- Runs tag queries to extract captures:
@name.definition.X → definitions, @name.reference.X → references
- Yields
Tag(rel_fname, fname, line, name, kind) namedtuples
Fallback: If tree-sitter queries only produce definitions (e.g., C++ queries lack reference captures), Aider uses Pygments lexer tokenization to extract all Token.Name tokens as references.
Tags are cached in SQLite (via diskcache.Cache), keyed by filename with mtime-based invalidation — so re-parsing only happens for changed files.
Phase 2: Graph Building
Builds a networkx.MultiDiGraph where nodes are files and edges go from referencing file → defining file, weighted by reference importance:
# Edge weight calculation (simplified)
mul = 1.0
if ident in mentioned_idents: mul *= 10 # user mentioned this symbol
if is_meaningful_ident(ident): mul *= 10 # snake_case/camelCase, >=8 chars
if ident.startswith("_"): mul *= 0.1 # private symbol discount
if defined_in_more_than_5_files: mul *= 0.1 # too common = less relevant
# CRITICAL: 50x boost for references FROM files currently being edited
if referencer in chat_fnames: mul *= 50
# Dampen high-frequency references
num_refs = math.sqrt(num_refs)
G.add_edge(referencer, definer, weight=mul * num_refs, ident=ident)
The 50x multiplier for chat-file references is the key insight: it ensures that symbols used by the files you're editing get ranked highest.
Phase 3: Personalized PageRank
personalize = {fname: 100 / len(fnames) for fname in chat_fnames}
# Also boost files mentioned in user's message, files matching mentioned identifiers
ranked = nx.pagerank(G, weight="weight", personalization=personalization,
dangling=personalization)
This captures transitive importance — a utility file used by many important files ranks high even if not directly mentioned.
Phase 4: Distribute File Rank to Definitions
Each file's PageRank score is distributed to (file, symbol) pairs proportionally to edge weights:
for src in G.nodes:
src_rank = ranked[src]
total_weight = sum(data["weight"] for _, _, data in G.out_edges(src, data=True))
for _, dst, data in G.out_edges(src, data=True):
ranked_definitions[(dst, data["ident"])] += src_rank * data["weight"] / total_weight
Files already in the chat context are excluded (they're already fully visible).
Phase 5: Binary Search for Token Budget
Aider does binary search over the ranked definition list to find the maximum number of entries that fit the token budget (default: max(1024, min(max_input_tokens/8, 4096)) tokens):
while lower_bound <= upper_bound:
tree = self.to_tree(ranked_tags[:middle], chat_rel_fnames)
num_tokens = self.token_count(tree)
if abs(num_tokens - max_map_tokens) / max_map_tokens < 0.15: # within 15%
break
# binary search adjustment...
When NO files are in the chat, the budget increases to 8x (more map since there's no file content).
Phase 6: Scope-Aware Tree Rendering
Uses grep_ast's TreeContext to render each file with intelligent elision:
- For each ranked symbol, shows its definition line plus parent scope headers (class signature, function signature)
- Everything else is elided with
⋮ markers
- Small gaps between shown lines are filled in
Output format:
path/to/file.py:
│class MyClass:
⋮
│ def important_method(self, arg):
⋮
path/to/other.py:
│def utility_function(x, y):
⋮
How It's Sent to the LLM
Injected as a user message with this framing:
Here are summaries of some files present in my git repository.
Do not propose changes to these files, treat them as *read-only*.
If you need to edit any of these files, ask me to *add them to the chat* first.
The map is rebuilt before every LLM call, so it adapts as the conversation evolves.
Why It's Effective
- PageRank captures transitive importance — files referenced by many important files rank high even if not directly mentioned
- Personalization adapts to conversation — biases toward user's active files
- 50x multiplier for chat-file references — symbols used by files being edited get prioritized
- Scope-aware rendering — shows just signatures and structure, not full implementations
- Adaptive budget — 8x more context when no files selected, shrinks as files are added
- Smart heuristics — penalizes private symbols, boosts meaningful identifiers, dampens common symbols
Current State in Hermes Agent
What we have:
The gap: No automatic codebase structure awareness. The agent doesn't know what files exist, how they relate to each other, or which symbols are relevant to the current task — unless it manually searches. For a coding agent, this is a critical missing capability.
Implementation Plan
Classification
This should be a core codebase change — a new tool or enhancement to existing tools. It requires:
- Tree-sitter AST parsing (native library)
- Graph computation (PageRank)
- Token budget management
- Integration with the prompt assembly pipeline
What We'd Need
- Tree-sitter grammars — Python, JS/TS, Rust, Go, C/C++, Java, Ruby, etc. (Aider supports 26+ languages via
tree-sitter-language-pack)
- Tag query files (.scm) — Language-specific queries for extracting definitions and references (could adapt Aider's, they're Apache 2.0)
- Graph library — networkx for PageRank, or a lightweight custom implementation (power iteration is ~50 lines)
- Scope-aware renderer — For elided code display (could build on tree-sitter AST, or port
grep_ast's TreeContext)
- Tag cache — SQLite or diskcache for mtime-based invalidation
- Integration point — Either a new
repo_map tool, or automatic injection into system prompt when working in a git repo
Phased Rollout
Phase 1: Symbol Extraction + Flat Listing
- Tree-sitter parsing for top 5 languages (Python, JS/TS, Rust, Go, C/C++)
- Extract definitions (classes, functions, methods, constants)
- Cache in SQLite with mtime invalidation
- New
codebase_map tool that returns a flat listing of symbols, ranked by file proximity to specified files
- No PageRank yet — simple heuristic ranking (same directory → higher rank)
Phase 2: PageRank Ranking + Scope-Aware Display
- Build reference graph (definitions + references)
- Implement personalized PageRank (can use networkx or custom)
- Scope-aware rendering with parent scope headers and elision
- Token budget fitting via binary search
- Auto-inject repo map when agent is working in a git repo
Phase 3: Deep Integration
Pros & Cons
Pros
Cons / Risks
- Tree-sitter dependency — native library that needs compilation for each platform. However,
tree-sitter and tree-sitter-language-pack are available as pip packages with pre-built wheels
- Complexity cost — adds a new subsystem (graph building, PageRank, rendering) with ~600 lines of core logic
- Token budget tradeoff — repo map tokens compete with conversation context. Default of max_input/8 may be too much or too little depending on the task
- Large repo performance — PageRank on repos with >50k files may be slow (Aider has RecursionError protection for this case)
- Not useful for non-code tasks — this is a coding-specific feature, irrelevant when Hermes is used for research, creative work, etc.
Open Questions
- Should the repo map be a standalone tool (agent decides when to call it) or automatically injected into every prompt when working in a git repo?
- Should we use networkx (established, 20MB dep) or implement power iteration directly (~50 lines)?
- What token budget default makes sense? Aider uses max_input/8 — is that right for Hermes which has different context management (probing, compaction)?
- Should the map be visible to the user (shown in output) or invisible (injected into system prompt)?
- Can we reuse Aider's .scm query files directly (Apache 2.0 licensed)?
References
Overview
Aider's most powerful system is its PageRank-based repo map — an automatic context selection mechanism that builds a directed graph of symbol definitions and references across an entire codebase, then uses personalized PageRank to rank files by relevance to the current conversation, and renders the top-ranked definitions as scope-aware elided code views that fit within a token budget.
When Hermes Agent works on code, it relies on the user or agent to manually
read_fileandsearch_filesto find relevant context. There's no automatic awareness of codebase structure — the agent has to guess which files matter. Aider solves this: before every LLM call, it computes which symbols and files are most relevant to the files being edited and includes a compact structural overview in the prompt. This is fundamentally different from both regex search (#489's semantic search via embeddings) and explicit @ references (#502) — it's automatic, graph-based, zero-effort context that adapts to the conversation.Source: Deep analysis of aider/repomap.py (781 lines), tree-sitter query files, and integration with the Coder classes.
Research Findings
How Aider's Repo Map Works (Step by Step)
The algorithm has 6 phases:
Phase 1: Symbol Extraction (tree-sitter)
For each file in the repo, Aider:
.scmquery file (stored inaider/queries/)@name.definition.X→ definitions,@name.reference.X→ referencesTag(rel_fname, fname, line, name, kind)namedtuplesFallback: If tree-sitter queries only produce definitions (e.g., C++ queries lack reference captures), Aider uses Pygments lexer tokenization to extract all
Token.Nametokens as references.Tags are cached in SQLite (via
diskcache.Cache), keyed by filename with mtime-based invalidation — so re-parsing only happens for changed files.Phase 2: Graph Building
Builds a
networkx.MultiDiGraphwhere nodes are files and edges go from referencing file → defining file, weighted by reference importance:The 50x multiplier for chat-file references is the key insight: it ensures that symbols used by the files you're editing get ranked highest.
Phase 3: Personalized PageRank
This captures transitive importance — a utility file used by many important files ranks high even if not directly mentioned.
Phase 4: Distribute File Rank to Definitions
Each file's PageRank score is distributed to
(file, symbol)pairs proportionally to edge weights:Files already in the chat context are excluded (they're already fully visible).
Phase 5: Binary Search for Token Budget
Aider does binary search over the ranked definition list to find the maximum number of entries that fit the token budget (default:
max(1024, min(max_input_tokens/8, 4096))tokens):When NO files are in the chat, the budget increases to 8x (more map since there's no file content).
Phase 6: Scope-Aware Tree Rendering
Uses
grep_ast'sTreeContextto render each file with intelligent elision:⋮markersOutput format:
How It's Sent to the LLM
Injected as a user message with this framing:
The map is rebuilt before every LLM call, so it adapts as the conversation evolves.
Why It's Effective
Current State in Hermes Agent
What we have:
search_files(ripgrep-backed) — exact string/regex matching, requires knowing what to search forread_file— reads specific files, agent must know which ones matterThe gap: No automatic codebase structure awareness. The agent doesn't know what files exist, how they relate to each other, or which symbols are relevant to the current task — unless it manually searches. For a coding agent, this is a critical missing capability.
Implementation Plan
Classification
This should be a core codebase change — a new tool or enhancement to existing tools. It requires:
What We'd Need
tree-sitter-language-pack)grep_ast's TreeContext)repo_maptool, or automatic injection into system prompt when working in a git repoPhased Rollout
Phase 1: Symbol Extraction + Flat Listing
codebase_maptool that returns a flat listing of symbols, ranked by file proximity to specified filesPhase 2: PageRank Ranking + Scope-Aware Display
Phase 3: Deep Integration
Pros & Cons
Pros
Cons / Risks
tree-sitterandtree-sitter-language-packare available as pip packages with pre-built wheelsOpen Questions
References