Use radius from an AI agent
What your agent should run after a dependency upgrade, and what to do with the result.
Why your agent needs it
When an agent upgrades a dependency, it checks two things: does the code compile, and do the tests pass. Both can be fine while the package now behaves differently: a new default, a stricter check.
Those changes are written in the release notes. The agent either skips the notes, or reads all of them and guesses which files they affect.
radius does that part for it. It returns the changes that affect your code, with the files and lines. The agent then checks those lines and runs the tests.
The steps
1. Upgrade
The agent upgrades the way the project does it: pnpm up zod, npm install react@19. No need to
commit.
2. Run radius
npx dep-radius --since HEAD --json--since HEAD compares with the last commit, so radius looks at exactly what the agent just
changed. On a branch with several commits, use --since main.
3. Check the lines
| Exit code | What the agent does |
|---|---|
0 | Nothing your code uses changed. Run the tests. |
1 | Open each line radius lists. Read the notes it couldn't place. Fix what breaks. |
2 | Same as 1, and first fix every call to what was removed. |
3 | radius failed. Report it. Don't guess. |
4. Run the tests
Run the tests for the files radius listed. If a note describes something no test covers, add a test. radius shows where to look. The tests confirm the code still works.
Paste this into CLAUDE.md or AGENTS.md
It works in CLAUDE.md (Claude Code), AGENTS.md, .cursor/rules,
.github/copilot-instructions.md, or any file your agent reads.
## Dependency upgrades
After you change a dependency version, run this before you say you're done:
npx dep-radius --since HEAD --json
Then look at the exit code:
- 0: nothing this project uses changed. Run the tests.
- 1 or 2: for each package where "verdict" is not "quiet":
1. If "verdict" is "blocked", first fix every site in `types.touched[]` where "change" is
"removed".
2. Open every site in `types.touched[].sites[]`.
3. For each note in `notes.matched[]`, find its names (`names[].name`) in `usage.sitesByName`,
open those sites, and check if the change in the note's `title` affects that code.
4. Read the titles in `notes.breakingWithoutApi[]` and `notes.changesWithoutApi[]`. radius could
not link them to the code. Search the code for what they describe.
5. Fix the code. Run the tests for the files you opened. Add a test if nothing covers the change.
6. Run radius again.
- 3: radius failed. Report the error. Don't guess.
`usage.cannotSee[]` lists usage radius could not follow, like scripts or config files. Check those
with the tests. Never call an upgrade safe because of radius alone.How I use it
I don't run radius myself. The block above is in my projects' CLAUDE.md. When Claude Code upgrades
a dependency, it runs radius, opens the lines, and runs the tests for those files. Then it tells me
the upgrade is done.
Why not just let the agent read the changelog?
| Agent reads the changelog | Agent runs radius | |
|---|---|---|
| Finds the files a change affects | Guesses, often misses some | Finds every place, even through other files |
| Same answer next time | No | Yes |
| Says what it didn't check | No | Yes |
| Tokens used | Every note of every version | Only the result |
The agent still reads. It just reads the right lines.
The JSON
Here is what an agent needs from the output. This is a real run, zod 4.4.3 → 4.5.0, shortened. The full format is on The JSON output.
{
"schemaVersion": 1,
"exitCode": 1,
"packages": [
{
"name": "zod",
"from": "4.4.3",
"to": "4.5.0",
"verdict": "review",
"reasons": [
{ "code": "notes-match", "detail": "5 release notes mention names you use (4 by name in the text)" },
{ "code": "unattributed-change", "detail": "23 release notes describe changes radius cannot tie to your code" }
],
"notes": {
"matched": [
{
"version": "4.5.0",
"title": "⚠️ String length counts code points",
"breaking": true,
"direct": true,
"names": [
{ "name": "string", "certainty": "exact", "where": "inline-code" },
{ "name": "max", "certainty": "by-name", "where": "inline-code" },
{ "name": "min", "certainty": "by-name", "where": "inline-code" }
]
}
],
"changesWithoutApi": [{ "version": "4.5.0", "title": "Zod 4.5 is now available.", "refs": [] }]
},
"usage": {
"sitesByName": {
"min": [
{
"file": "src/feedback/schema.ts",
"line": 5,
"column": 12,
"typeOnly": false,
"code": "message: z.string().min(10).max(2000),",
"via": ["src/lib/zod.ts"]
}
]
}
}
}
]
}| Field | What it is |
|---|---|
notes.matched | Notes that name something your code uses |
usage.sitesByName | Where your code uses each name: file, line, and the code itself |
types.touched | Functions and types that changed or were removed, with where they're used |
notes.changesWithoutApi | Notes radius couldn't link to your code. Skim them. |
notes.breakingWithoutApi | Breaking notes that name nothing specific |
notes.changesWithoutApi can be long. Some entries are just a release intro, like "Zod 4.5 is now
available." It's a list to skim, not a list of problems.
Handy commands
These use jq:
# every file:line a note lands on, with the note
npx dep-radius --since HEAD --json | jq -r '.packages[] as $p | $p.notes.matched[] as $n | $n.names[].name as $name | ($p.usage.sitesByName[$name] // [])[] | "\(.file):\(.line) \($n.title)"' | sort -u
# the notes radius couldn't link to your code
npx dep-radius --since HEAD --json | jq -r '.packages[] | .name as $pkg | (.notes.breakingWithoutApi + .notes.changesWithoutApi)[] | "\($pkg) \(.version) \(.title)"'
# every use of a changed or removed function
npx dep-radius --since HEAD --json | jq -r '.packages[].types.touched[]?.sites[] | "\(.file):\(.line)"'
# only the packages that need a look
npx dep-radius --json | jq -c '.packages[] | select(.verdict != "quiet") | {name, from, to, verdict}'Tips
- Give it a GitHub token. Set
GITHUB_TOKEN, or log in withgh. Without it, GitHub allows 60 requests an hour and some notes may be skipped. radius says so when it happens. - The output is only JSON. With
--json, nothing else is printed to stdout. - Running it again is fast. Downloads are cached.
--offlineuses only the cache. - Monorepos. Point it at one package:
npx dep-radius ./packages/api --since HEAD --json. - The JSON won't break. Version 1 only adds fields. It never removes or renames one.
Docs for agents
This site can be read by agents:
| URL | What |
|---|---|
/llms.txt | A list of every page |
/llms-full.txt | Every page in one Markdown file |
Any page URL + .md | That page as Markdown, like /docs/agents.md |
Accept: text/markdown | Any page answers in Markdown when asked |