The evidence layer for AI coding agents
Your agent can change forty files in a minute. Nothing on your machine can tell it what those changes reach — so it reads, guesses, and sounds certain either way. Euthynos answers structurally, and names the boundary of every answer.
npm install -g euthynos
agentI'm about to change serialize. What reaches it?
9 transitive callers of serialize: d1 fetch — src/client/client.ts:58 d1 generateCookie — src/helper/cookie/index.ts:78 d2 hc — src/client/client.ts:137 d2 setCookie — src/helper/cookie/index.ts:99 d3 testClient — src/helper/testing/index.ts:21 d3 deleteCookie — src/helper/cookie/index.ts:141 d3 cacheLanguage — src/middleware/language/language.ts:221 d4 detectLanguage — src/middleware/language/language.ts:238 d5 languageDetector — src/middleware/language/language.ts:292 For the exact call-site lines with their source: find_references({ symbol: "serialize" }).
agentWhich tests cover it?
2 test files for src/utils/cookie.ts#serialize (route-labeled evidence, not coverage): src/utils/cookie.test.ts [import-edge] imports './cookie' resolving to src/utils/cookie.ts [name-convention] test filename matches cookie.* src/client/client.test.ts [import-edge] imports '../utils/cookie' resolving to src/utils/cookie.ts Routes: import-edge (structural), name-convention (convention), test-call (static, name-level). Dynamic/parameterized test discovery is invisible — a hit list is evidence, never a coverage claim.
agentI've made the edit. What did it touch?
Changes vs HEAD 3c8e1b7a91f2 Scope: tracked changes vs HEAD plus untracked CODE files. Not diffed: binary/oversized blobs, line-ending-only differences. Changed files (2): [modified] src/utils/cookie.ts [modified] src/helper/cookie/index.ts Changed symbols (1): [modified] serialize — src/utils/cookie.ts:261 Boundary: no deep-import violations INTRODUCED by this diff (pre-existing, unchanged: 0). Within the import-edge boundary rule only — not a claim the architecture is sound. Blast radius (static call graph — dynamic dispatch and unindexed files are invisible): 9 functions transitively call the 1 modified symbol. This is evidence, not a verdict. The decision stays with you.
Watch it run
One command, on a repository you can check.
A recording of real output, not a mock-up. Clone
hono at
26de7313, run the same command, and the numbers should match.
Health 68 is not a grade to fix — it is five measured signals with their inputs named. Leverage 36 says most exported surface has few callers. Locality 88 says related changes already land together. These 382 files scan once; the index is content-addressed, so the next scan only re-reads what changed.
The shift
Writing code stopped being the bottleneck. Verifying it started.
An agent that can produce a refactor in seconds still has no structural picture of the repository it is editing. It compensates the only way it can — by reading files until the context window fills.
Reading is not understanding
Opening forty files tells an agent what the code says, not what depends on it. Inbound edges are invisible from the file you are looking at.
Confidence is not calibrated
A model that missed a caller sounds exactly like a model that found them all. Nothing in the transcript distinguishes the two.
The reviewer is now the human
When generation is cheap, the scarce resource is a person deciding whether a change is safe to ship — with whatever evidence they were handed.
The evidence model
It refuses to tell you your change is safe.
Euthynos is a static analyser. It sees imports, declarations and call edges — not reflection, not dynamic dispatch, not production. So it never claims otherwise. Every answer carries the boundary of the method that produced it, and every empty answer says what was not examined rather than implying nothing exists.
Claims the engine will not make
These are enforced by the test suite, not by style guide. A change that lets any of them reach a user is a failing build.
Tiered, and labelled
Structural resolution establishes facts. Name conventions and text matches discover candidates. The two are never presented as the same thing.
Exclusions are stated
Files skipped for size, ignore rules or parse failure are counted and reported. A narrowed answer says it was narrowed.
Capabilities
Twenty-three read-only tools, over MCP.
Your agent calls them the way it calls any other tool. Nothing is written to your repository except a gitignored index that is safe to delete at any time.
| Question | Tool | Evidence it returns |
|---|---|---|
| What reaches this function? | callers_of | Transitive callers with depth and location |
| What does my edit touch? | check_my_changes | Changed symbols, blast radius, tests, stated scope |
| Which tests relate to this? | tests_for | Route-labelled test files — never a coverage claim |
| Does this already exist? | similar_logic_exists | Near-duplicate implementations with both spans |
| What is this repository? | repo_map | Modules, sizes, structural signals, health |
| Did I cross a boundary? | boundary_check | Deep-import violations introduced by the diff |
Plus symbol and reference lookup, callees, module dependencies and dependents, path-between, context bundles, and span-accurate source reads. 16 languages parse — call-graph resolution is strongest in TypeScript.
Measured, not asserted
We preregistered the benchmark before running it.
Answer keys frozen by commit. Sessions gated. Recall hand-graded against those keys before anyone looked at the transcripts. Here is what it showed — and what it did not.
What this does not show. Two of the seven tasks were never measured — an external session limit consumed them, and they stay unmeasured rather than being quietly dropped. One repository, one model, one permission environment. No causal isolation of individual tools. The benchmark also found a real defect in our own call graph, which the released engine ships fixed. Full preregistration, results and caveats are in the repository.
Runs where your code is
Local, read-only, and offline by default.
Your source never leaves
There is no upload path in the query loop. No account, no network dependency, no telemetry sent anywhere.
Hostile-repo hardened
Symlinks are never followed, paths are containment-checked, and git runs with hooks, filters and exotic transports disabled.
Derived state is disposable
The index verifies itself against the repository it describes. If integrity cannot be established it is rebuilt, and it says so.
Honest envelope. Validated to roughly 10,000 files, comfortable well below that. Memory is the binding constraint above it. The measured numbers, including where it stops, ship with the package.
Give your agent something better than a guess.
One command to install, one to register it with your agent. Apache 2.0 licensed.
npm install -g euthynos
claude mcp add euthynos -- euthynos mcp