Skip to main content
Developer ToolCompleted

Field Guide: A Living Architecture Map for Any Codebase

A VS Code extension and MCP server that map a git repository from its own import graph and commit history: where to start reading, what a change reaches, which files change together, and who knows the part you are about to touch.

DeveloperMustafa Kürşad Başer
Started: Jun 2026Completed: Aug 2026
Field Guide: A Living Architecture Map for Any Codebase

The first week on an unfamiliar codebase is spent asking questions nobody has written down. Where does this start. What breaks if I touch this. Why do these two files always change together. The usual answer is documentation, and documentation has one fatal property: it starts going stale the moment it is written. The architecture diagram in the wiki describes a system that existed eighteen months ago, everybody knows it, so nobody trusts it, so nobody updates it.

The answers are already in the repository. The import graph knows what depends on what. The commit history knows what changes together, how often each file is touched, and who has touched it. Neither can go stale, because neither is written down: both are derived, every time you ask. The audience is concrete, developers in their first weeks on a codebase somebody else built, maintainers who inherited a system they did not write, and anyone working with a coding agent.

Where to start reading: Two lists, both derived: the files nothing imports, which is where execution begins, and the files everything leans on. Pointed at hono, a repository I did not write, it reports 385 files with 960 imports between them and names the three modules that carry the whole design. Four hundred files, and the three that matter are the first thing worth knowing and the last thing anyone writes down.

What a change will reach: Not a text search for a file name, but the actual import chain followed all the way, with the shortest chain to each file it arrives at. The same figure sits above the open file in the editor as one line, so the reach of what you are editing is visible without asking for it.

The couplings nothing else can see: Files committed together over and over, whether or not they refer to each other. This is the half that static analysis cannot do.

  • Coupling is a conditional probability, of the commits that touched A what share also touched B, reported in both directions because the relationship is usually lopsided
  • In hono it surfaces three runtime adapters implementing the same feature separately. None of them imports another, so no compiler, linter or dependency graph will ever connect them, and yet changing one leaves a two in three chance the others need the same change
  • Pairs seen fewer than three times, or under 30% confidence, are dropped, and commits touching more than 25 files are excluded outright. A formatter run or a dependency bump couples everything it touches to everything else it touches, and all of it is spurious

Where the knowledge has gone: Ownership is weighted by lines written rather than by commits, because someone who wrote a file in two large commits knows it better than someone who adjusted an import in ten small ones. Bus factor is the fewest people who between them account for half the file.

  • It is reported as a risk only when one person wrote nearly all of a file and has also stopped contributing. On a small team one person owning a file is the normal state, so flagging that alone would fire on everything and be read as noise. The signal is the person having gone
  • Files with fewer than three commits are exempt, since a file with one commit has one author by definition
  • Identities are resolved through git's own mailmap, so one person committing under two addresses is one person

The same answers for a coding agent: The same engine runs as an MCP server, so an agent asks instead of grepping. Six tools: overview, describe_file, impact_of, changes_with, hotspots and reindex. It installs into Claude Code as a plugin and speaks stdio to any other MCP client. An agent that asks for impact gets the transitive answer in one call; grepping for the file name reads dozens of files, still misses the indirect dependents, and spends far more context getting there.

Engineering:

  • One rule drives the layout: the core layer imports nothing from Node, the MCP SDK or any editor. The parsers, the metrics, the graph and the cache invalidation rules are plain TypeScript tested without a repository, and an ESLint rule enforces the boundary in both directions
  • The import graph resolves through the TypeScript compiler's own module resolution rather than a reimplementation of it, so path aliases, package exports maps and monorepo packages reached through symlinks resolve the way the build resolves them. Getting this wrong produces a map with missing edges and nothing to indicate it, which is the worst shape a bug can take here
  • The analysis runs in a child process behind a JSON protocol, because the extension host is shared with every other extension the user has installed and this one carries a compiler. The editor bundle is under 20KB and the build fails if it passes 200KB
  • Change frequency is weighted by recency with a 90 day half life, so the ranking reflects current activity rather than repository age. Freshness is established rather than assumed: HEAD, the tracked file set and each file's timestamp are checked before an answer is given, and the index is cached in segments so both reading and appending stream
  • 204 tests, 162 unit and 42 integration driving real git repositories, plus a suite that runs the extension inside a downloaded VS Code. CI covers three operating systems on two Node versions, with two further jobs guarding what vsce does not check: that the committed bundle still matches its sources, and that the packaged VSIX actually contains the analysis engine rather than installing and then hanging on every feature

Security: There is no code anywhere in the project that opens a socket, so no telemetry and no network requests are a property of the source rather than a policy.

  • The extension stays inactive until the folder is trusted, and that is not a formality. A repository's own .git/config can name core.fsmonitor, which git runs as a subprocess for ls-files, so reading an untrusted repository means running its author's code. It is pinned off
  • Text that comes out of a repository is escaped before it is rendered. A module specifier can carry a newline while looking ordinary in the source, and printed verbatim it produces a line indistinguishable from one this program wrote. Bidirectional overrides, the Trojan Source problem, go the same way. For a tool whose purpose is being pointed at code you have not read, that is worth closing
  • The index is cached outside the working tree, so nothing appears in git status and there is nothing to add to .gitignore

The limits are stated rather than hidden. The import graph is TypeScript and JavaScript only, though every tracked file still gets churn, ownership and co-change whatever its language. Ownership is approximated from the commit log rather than from git blame, which is the difference between indexing in seconds and in hours. And there is no picture, because a force directed graph of four hundred files is a hairball.

The result is a document that cannot go stale, because it was never written. Every answer is derived from the repository at the moment it is asked, and the parts that are estimates say so.

Tech Stack

TypeScriptVS Code Extension APIModel Context ProtocolNode.jsTypeScript Compiler APIgitesbuildESLintnode:testMochaGitHub ActionsClaude Code Plugin