← Back to devlog
Tools · May 2026

A queryable architecture map for my C# codebase

When an agentic session launches, it gathers context by grepping and reading files. That works. We can do better.

Most of my Unity code is intended to be modular. Monobehaviours and serializable classes can fit together in unexpected ways. Usually on serialized prefabs that agents have a hard time reasoning about unless explicitly told to. This is counter to the highly structured, tree like architectures most non-game codebases enforce. Agents Grep for a class and find its definition. They don’t find how it fits into the system, what it composes with, what already exists. They’ll ignore the rest of the codebase and fall back onto popular, generalized solutions. (Like UnityEvents. Ew.) Or even worse, design and implement an entire competing system to solve a problem you’ve solved already because they Just. Didn’t. Know.

It isn’t the agent’s fault. We just give them a blurb, or a ticket. We’ve already got all the context in our minds, and it’s almost as much work to write a fully fleshed out prompt as to solve the problem ourselves. So we gamble with the line where laziness meets efficiency, and sometimes we lose.

So how can we improve the odds? Can we give agents a sort of map?

If you’ve developed a knowledge layer, you know that the closer descriptions are to concrete details outside the layer, the harder it is to maintain over time as things change. And codebases change. A lot. Especially when you like to keep things clean and organized. Plus, a fully authored manual would be huge. Code is meant to be self-documenting, yet that does not help us when the agents can’t find the code to begin with.

My research says this problem has been addressed in other ways. MCP’s exposed by IDE’s like Rider, or Antigravity. I have a bone to pick with MCP’s. They’re unstable, add latency, take up ports in the background, and they eat tokens like crazy every turn. I prefer documented CLI tools.

My answer is a structural map of the custom code. Not the whole thing dumped into context. A queryable map. Start at the highest level. Just the namespaces and assembly definitions. Then drill as deep as you want. All the classes inside a namespace? Done. All the fields and methods inside a class? Here you go. All the comments on those fields? Cool, only because you asked. Given directly to the agent, in milliseconds, in compact markdown instead of awkward, token heavy JSON.

That’s Blueprint.

What it does

Blueprint runs Roslyn static analysis over the project’s custom .cs files, builds an on-disk index (namespaces, types, method and field signatures, attributes), and serves slices via three commands:

  • blueprint summary: top-level namespace and type table of contents
  • blueprint show <namespace|class>: drill into one branch
  • blueprint show <class> --members: that class’s methods, fields, and full signatures

They’re equivalent to ls, cd, cat for the codebase. Orient first, then zoom.

The design calls

CLI, not MCP. A CLI rides Bash, which is already loaded. The CLI also controls the output format. Compact Markdown that fits into context, not a JSON blob that bloats it.

Roslyn syntax trees. Roslyn reads from source without executing anything, so it works even when the project isn’t compiling. A build error mid-session doesn’t leave a stale, useless map. The downside? Signatures are rendered as written, not as resolved symbols. I was willing to make the sacrifice.

No LLM Go-Between My first idea was to have a local LLM answer questions about the codebase using blueprint in natural language, lowering the surface area of the tool for my smarter agents. I threw this out. Filtering a structural map by a query is a deterministic operation. An LLM adds latency and a hallucination risk to something that has to be exact. A code map that lies is worse than no map.

JSON quarantine. The index itself is JSON. It lives on disk and isn’t exposed to the agents. The CLI returns the queried slice as Markdown. The raw index, in part or in entirety, never enters the agent’s context.

Freshness by mtime. I didn’t want to rebuild the index for every query. We can do better. Caches are built at ~/.cache/blueprint/, keyed by repo. A stone cold build of a half done game project yielded 327 files, 422 types, 87 namespaces and runs in about half a second. Touch one file and only that file re-parses, roughly 0.1s. A cached, unchanged response serves in about 0.02s.

The --docs flag

Once the initial skeleton was working, I thought it might be a good idea to serve the inline documentation to agents who cared about it. So I added opt-in /// doc-comment summaries. Pass --docs at the show stage and Blueprint appends <summary> text. Inline per type or member, extracted from XML doc-comment trivia. Roslyn already exposes doc comments as syntax trivia when you ask it to. All we had to do is support it ourselves.

To reduce noise, this is opt in only. The compact structural map is the main event. --docs is for when you’ve already navigated to the right spot and want those juicy deets.

The limitations

There is one thread I haven’t chased down yet. One thing v1 does not answer clearly. What references this? Blueprint is a definition map. It knows that DataPipeRegistry exists and what its members are. It doesn’t know which classes hold a reference to it, because that information lives in method bodies, which v1 doesn’t index. You use Blueprint to orient and Bash for the reference queries.

Reverse cross-reference is in the backlog. It’ll come when I really think I need it. Right now I don’t.

It’s not perfect. It works though. Once integrated, I have not encountered this problem again. We can always do better.