Files
orcs-code/src/context/repoMap/renderer.ts
gnanam1990 81896618a1 feat: add Codebase Intelligence — repo map with PageRank-ranked structural summaries
Add a new module that builds a structural map of the repository by parsing
source files with tree-sitter, building a cross-file reference graph
weighted by IDF, ranking files with PageRank, and rendering a
token-budgeted summary of the most important files and their signatures.

Stage 1 — Core module (src/context/repoMap/):
  Symbol extraction via web-tree-sitter WASM, IDF-weighted reference graph
  via graphology, PageRank ranking, token-budgeted rendering via js-tiktoken
  cl100k_base, disk cache with mtime invalidation. Supports TypeScript,
  JavaScript, and Python. 10 tests.

Stage 2 — RepoMap tool (src/tools/RepoMapTool/):
  buildTool wrapper registered in src/tools.ts. Read-only, concurrency-safe.
  Supports focus_files, focus_symbols, and max_tokens parameters. 9 tests.

Stage 3 — Integration:
  Auto-injection into session context behind REPO_MAP feature flag (off by
  default). /repomap slash command with --tokens, --focus, --stats, and
  --invalidate flags. User-facing docs in docs/repo-map.md. 13 tests.

With the flag off, the system context is byte-identical to previous behavior.

Dependencies: web-tree-sitter, tree-sitter-wasms, graphology,
graphology-pagerank, graphology-operators, js-tiktoken

Tests: 32 new, 621 total passing, 0 failures.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-28 11:57:16 +05:30

73 lines
1.8 KiB
TypeScript

import type { FileTags, Tag } from './types.js'
import type { RankedFile } from './pagerank.js'
import { countTokens } from './tokenize.js'
/**
* Render a token-budgeted repo map from ranked files and their tags.
*
* Format per file:
* path/to/file.ts:
* ⋮
* signature line for def 1
* ⋮
* signature line for def 2
* ⋮
*
* Files that don't fit within the budget are dropped entirely.
*/
export function renderMap(
rankedFiles: RankedFile[],
fileTagsMap: Map<string, FileTags>,
maxTokens: number,
): { map: string; tokenCount: number; fileCount: number } {
const sections: string[] = []
let currentTokens = 0
let fileCount = 0
for (const { path } of rankedFiles) {
const ft = fileTagsMap.get(path)
if (!ft) continue
// Only include definitions in the rendered output
const defs = ft.tags
.filter(t => t.kind === 'def')
.sort((a, b) => a.line - b.line)
if (defs.length === 0) continue
const section = renderFileSection(path, defs)
const sectionTokens = countTokens(section)
// Would this section bust the budget?
if (currentTokens + sectionTokens > maxTokens) {
// Don't include partial files — drop entirely
break
}
sections.push(section)
currentTokens += sectionTokens
fileCount++
}
const map = sections.join('\n')
return { map, tokenCount: currentTokens, fileCount }
}
function renderFileSection(path: string, defs: Tag[]): string {
const lines: string[] = [`${path}:`]
let lastLine = 0
for (const def of defs) {
// Add elision marker if there's a gap
if (def.line > lastLine + 1) {
lines.push('⋮')
}
lines.push(` ${def.signature}`)
lastLine = def.line
}
// Trailing elision marker
lines.push('⋮')
return lines.join('\n')
}