Skip to content
| Marketplace
Sign in
Visual Studio Code>Visualization>Field GuideNew to Visual Studio Code? Get it now.
Field Guide

Field Guide

Mustafa Kürşad Başer

|
3 installs
| (1) | Free
A field guide to any codebase, written by the code itself: what depends on what, what changes together, and who knows it.
Installation
Launch VS Code Quick Open (Ctrl+P), paste the following command, and press enter.
Copied to clipboard
More Info

Field Guide

A field guide to any codebase, written by the code itself.
What depends on what, what changes together,
and who knows the part you are about to touch.

Marketplace version CI status MIT licence

The Field Guide view in the sidebar: a Map section listing the repository's entry points, and an Active File section showing what the open file imports, how far a change to it reaches, and who knows it best


Why I wrote it

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 seem to change together. Who do I ask about this part.

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, and 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.

So this reads them instead of asking you to maintain a document.


What it does

It tells you where to start reading

Two lists, both derived: the files nothing imports, which is where execution begins, and the files everything leans on. This is hono, a repository neither of us wrote:

hono — 385 files, 960 imports between them, 1790 commits by 326 people over 4.7 years

entry points — nothing in the repository imports these
    11  benchmarks/routers/src/bench.mts
     7  benchmarks/routers-deno/src/bench.mts

most depended on — the modules everything leans on
    72  src/hono.ts
    71  src/context.ts
    62  src/types.ts

Four hundred files, and three of them carry the whole design. That is the first thing worth knowing and the last thing anyone writes down.

It tells you what a change will reach

Not a text search for the file name — the actual import chain, followed all the way, nearest first:

Changing src/core/types.ts reaches 47 files (26 of them import it directly).
Nearest first, with the shortest import chain:

   1  src/core/bridge/adapt.ts
   1  src/core/cache/codec.ts
   1  src/core/git/parseLog.ts
   2  src/node/indexer.ts

That is this repository describing itself. In the editor the same figure sits above the file you are looking at, as one line: 26 files import this — a change reaches 47.

It finds the couplings nothing else can see

This is the half that cannot be done with static analysis. Files that are committed together over and over, whether or not they refer to each other:

changes with — how often the other file changes in the same commit, then the reverse
    71% /  58%    15x  src/adapter/deno/serve-static.ts
    62% /  43%    13x  src/adapter/cloudflare-workers/serve-static.ts

Three runtime adapters in hono implementing the same feature separately. None of them imports another, so no compiler, linter or dependency graph will ever connect them — but change one and there is a two in three chance the others need the same change. Nobody writes that down either. You learn it by breaking it.

It tells you where the knowledge has gone

Ownership is worked out from lines written, and reported only when it is actually a problem — one person wrote nearly all of a file and has stopped contributing:

knowledge at risk — one person wrote it and has stopped contributing
   91% by Kei Kamikawa, last seen 2.4 years ago  src/utils/jwt/jws.ts
  100% by watany, last seen 2.2 years ago        src/adapter/bun/ssg.ts

On a small team one person owning a file is the normal state, so flagging that would fire on everything and be read as noise. The signal is the person having gone.

It answers the same questions for a coding agent

The same analysis runs as an MCP server, so an agent can ask directly instead of grepping its way to a worse answer. Six tools: overview, describe_file, impact_of, changes_with, hotspots, reindex.

An agent that asks impact_of gets the transitive answer in one call and about two hundred tokens. The same agent grepping for a file name reads forty files, still misses the indirect dependents, and spends fifty times the context getting there.


Installing

Field Guide needs Node 20 or newer and git on the PATH. It reads a repository, so it does nothing useful outside one.

In VS Code

From the Marketplace — the search box in the Extensions view, or:

code --install-extension mkbaser.fieldguide

Or take the signed VSIX from Releases:

code --install-extension fieldguide.vsix

Or build it yourself:

git clone https://github.com/mustafakbaser/FieldGuide.git
cd FieldGuide && npm install && npm run package
code --install-extension fieldguide.vsix

The first minute. Open a repository and click the Field Guide icon in the activity bar. The first index takes a few seconds on a small repository and rather longer on a large one — reading the git log is two thirds of it — and it is written to a cache outside the working tree, so every later start is quick. Then:

  • The Map view answers "where do I start reading": the files nothing imports, the files everything leans on, the files changing most, and the files nobody owns any more.
  • The Active File view follows whatever you have open — what it imports, what imports it, how far a change to it reaches, and what tends to change alongside it.
  • A line above each file says how many others import it. fieldguide.codeLens.enabled turns it off.

If the view says there is no repository, the folder is not one, or it is not trusted yet — see the note at the end of this section. Anything that goes wrong is written to View → Output → Field Guide, at warning level, so it is visible without changing a setting.

In Claude Code

The MCP server ships as one self-contained file, so there is nothing to build:

/plugin marketplace add mustafakbaser/FieldGuide
/plugin install fieldguide@mustafakbaser-tools

Then ask about a repository in plain language. The agent has six tools — overview, describe_file, impact_of, changes_with, hotspots, reindex — and reaches for them instead of grepping.

In any other MCP client

It speaks stdio and takes the repository as its argument:

{
  "mcpServers": {
    "fieldguide": {
      "command": "node",
      "args": ["/absolute/path/to/FieldGuide/dist/fieldguide-mcp.js", "/path/to/your/repo"]
    }
  }
}

With no argument it maps whichever repository the working directory is in. --cache-dir puts the index somewhere other than the platform cache directory, which matters in CI or a sandbox.

A word about workspace trust

The extension does nothing until you trust the folder, and that is not a formality: a repository's own .git/config can name commands git executes — core.fsmonitor is run by git ls-files — so reading an untrusted repository means running its author's code.


Settings

Every feature can be switched off on its own.

Setting Default
fieldguide.activeFile.enabled true The Active File view: what the open file imports, what imports it, how far a change reaches, what changes alongside it
fieldguide.codeLens.enabled true The one line above each file saying how many others import it
fieldguide.limit 10 How many entries each section of the map lists
fieldguide.coupling.minConfidence 0.4 How strong a historical relationship has to be before it is shown. 0.4 means the other file changed in at least 40% of the commits that touched this one. Lower it to see more, at the cost of coincidences

In a multi-root workspace each repository is indexed separately, in its own process, with its own cache.


How the numbers are worked out

The import graph is resolved through the TypeScript compiler's own module resolution rather than a reimplementation of it, so paths aliases, package exports maps and monorepo workspace packages reached through node_modules symlinks all resolve the way your build resolves them. Getting this wrong shows up as a map with missing edges and nothing to indicate it, which is the worst shape a bug can take here.

Change frequency is a commit count with each commit weighted by how recent it is, halving every 90 days. A raw count ranks by age on any long-lived repository: a file rewritten heavily five years ago and untouched since would outrank the one the team is fighting with this month.

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. Pairs seen fewer than three times, or under 30% confidence, are dropped. 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.

Ownership is weighted by lines added 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.

Freshness is established rather than assumed. Before answering, it checks HEAD, the set of tracked files, and the modification time of each of them, and re-reads anything that moved. Write a file and ask about it in the next breath and you get an answer about the file you just wrote. On a repository large enough that the check itself is slow, and only then, it is spaced out in proportion to what it costs.


Privacy

No telemetry. No network requests. Not as a policy — there is no code anywhere in this project that opens a socket.

What it returns is a different question, and worth being precise about. In the editor the answers stay on your machine. Through the MCP server they become part of your agent's conversation, and that conversation goes wherever your agent already sends it — so treat them exactly as you treat the code the agent is already reading. Author names appear in ownership output; email addresses are used as an identity key and are never rendered.

Text that comes out of a repository — file paths, module specifiers, author names — is escaped before it is shown. A module specifier can carry a newline while looking entirely ordinary in the source, and printed verbatim it produces a line indistinguishable from one this program wrote. For a tool whose whole 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.


What it does not do yet

  • The import graph is TypeScript and JavaScript only. Every file git tracks is in the history, the ownership map and the coupling analysis whatever its language — so a Python service beside a TypeScript front end still has churn, owners and co-change — but only these are parsed for imports.
  • Ownership is approximated from the commit log, not from git blame. Blame is more accurate for the lines that survived and costs one process per file, which is the difference between indexing in seconds and in hours.
  • A file deleted and later recreated under the same name shares one history. Git records no rename, and telling the two apart needs content similarity.
  • A shallow clone is reported, not repaired. Every history figure covers only the commits present, so the map says so and declines to cache anything it read.
  • There is no picture. A force-directed graph of four hundred files is a hairball. A useful one is a real design problem, not a weekend.

One thing it reports that you might not expect: imports whose capitalisation differs from the file on disk. They work on macOS and Windows and fail on Linux, so they are a portability bug that a CI run would otherwise find later and more expensively.


Development

npm install
npm run check      # lint, typecheck, unit and integration tests
npm run watch      # then F5 to launch an Extension Development Host
npm run test:editor    # runs the extension inside a downloaded VS Code
npm run package        # builds fieldguide.vsix

src/core is plain TypeScript that imports nothing from Node, the MCP SDK or any editor — the parsers, the metrics, the graph and the cache invalidation rules all live there and are tested without a repository. src/node runs git and reads the filesystem. src/bridge runs the analysis as a child process, src/mcp serves agents, src/vscode is the editor adapter. A lint rule keeps the boundary honest, and the build fails if the editor bundle grows past 200KB — the extension host is shared with every extension the user has installed, and the analysis carries a compiler.

If you want to work on it, see CONTRIBUTING.md.

License

MIT © Mustafa Kürşad BAŞER

  • Contact us
  • Jobs
  • Privacy
  • Manage cookies
  • Terms of use
  • Trademarks
© 2026 Microsoft