Code Impact Radius
The structured replacement for grep -r <name> followed by reading every match.
When to invoke
Whenever you (or the user) is about to:
- Rename a function, variable, class, type, interface, or enum
- Change the shape of a type or function signature
- Move or delete a file / module / barrel export
- Audit who depends on a particular symbol (security review, deprecation, refactor planning)
- Decide whether a change is "safe to do alone" or "needs coordinated rollout"
- Answer "what calls X", "where is X used", "who imports X", "blast radius of X"
This is the right tool whenever the question is "what connects to this symbol?"
How to invoke
node ~/.claude/skills/code-impact-radius/src/cli.js <symbolName> --project <path> --json
Or — if you want pretty terminal output instead of JSON:
node ~/.claude/skills/code-impact-radius/src/cli.js <symbolName> --project <path> --pretty
JSON is the default when stdout is not a TTY (so AI agents always get JSON).
Options
| Flag |
What it does |
--project <path> |
Project root containing tsconfig.json (default: cwd) |
--depth <n> |
Transitive caller depth (default 2, max 10) |
--kind <kind> |
Filter declaration kind: variable function class method interface type enum |
--at <file>:<line> |
Disambiguate when the name has multiple declarations |
--include-tests |
Include *.test.ts / *.spec.ts in references (default: skip) |
--max-sites <n> |
Truncate references.sites to first N entries |
--json / --pretty |
Force output mode |
--mermaid |
Emit a Mermaid flowchart diagram (paste into a Markdown ```mermaid block — GitHub, GitLab, VS Code, Obsidian, Notion render natively). |
Exit codes
| Code |
Meaning |
| 0 |
Success — exactly one match, OR ambiguous list with N candidates |
| 1 |
CLI usage error / runtime error |
| 2 |
Symbol name not found |
Reading the JSON output
{
"query": // what was asked
"matched": // how many declarations matched the name
"candidates": // disambiguation list (only when matched > 1 or 0)
"symbol": {
"name": // canonical name
"kind": // function | class | variable | ...
"definition": {
"file": "...", "line": 42, "column": 17,
"snippet": "export function formatPrice..."
}
},
"references": {
"totalCount": 14,
"byKind": {
"definition": 1,
"call": 8,
"import": 3,
"reExport": 1,
"read": 1,
"instantiation": 0,
"jsx": 0,
"type": 0,
"write": 0
},
"sites": [
{ "file": "...", "line": 18, "kind": "call", "snippet": "formatPrice(price, locale)", "parentKind": "CallExpression" }
],
"truncated": 0
},
"imports": {
"exportedFrom": "src/lib/utils.ts",
"importedBy": // every file that imports this symbol
"reExportedFrom": // every barrel that re-exports it
},
"transitiveCallers": {
"depth": 2,
"callers": [
{ "depth": 1, "file": "...", "line": 18, "function": "PriceTag", "functionKind": "FunctionDeclaration" },
{ "depth": 2, "file": "...", "line": 87, "function": "CheckoutPage", "functionKind": "FunctionDeclaration" }
]
},
"warnings": []
}
Disambiguation flow
If matched > 1, the tool returns candidates and asks for a --at <file>:<line> to pick one:
$ code-impact-radius id --project ./app --json
# matched: 3, symbol: null, candidates: [{file, line, kind} × 3]
$ code-impact-radius id --project ./app --at src/ambig.ts:11 --json
# matched: 1, symbol: { ... }
Decision-flow guidance for blast-radius checks
Use this output to drive a structured impact table:
- Definition — read the file at
symbol.definition.file:line to see the contract you're about to change.
- References by kind — every
call, instantiation, jsx, read, write site is a potential breakage.
- Imports — every
imports.importedBy entry needs updating if you rename the symbol.
- Re-exports — barrels in
imports.reExportedFrom need updating too.
- Transitive callers — depth 2-3 catches most "indirect callers" that grep would miss.
Then build the impact table (file:line | usage | status: todo/done/verified-unaffected) and tick each row off as you go.
What this tool does NOT cover
- String literals — i18n keys (
t('home.title')), SQL fragments, env-var names, URL paths. Use grep for those.
- Dynamic imports —
import('./mod') calls aren't traced.
- Cross-language — TypeScript / TSX only. Python / Rust / Go / SQL stay grep territory.
For string-literal scans, run grep alongside this tool. The two together cover the full surface.
Performance
Cold-load on a ~200 TS/TSX file project: ~1.5s per query. ~700-file projects: ~5-8s. The TypeScript compiler initializes once per CLI invocation, so repeated queries in one session each pay that cost.
1---2name: code-impact-radius3description: Use BEFORE any rename, refactor, type-shape change, deletion, or "what depends on X" question in a TypeScript / TSX codebase. Returns every place a symbol is defined, read, written, called, instantiated, imported, re-exported, plus its transitive callers up to a configurable depth. Far cheaper than grep+read because it queries a typed reference index built from the TypeScript Compiler — no false positives from string matches in comments or unrelated identifiers with the same name. Triggers on phrases like "what calls X", "where is X used", "blast radius of X", "impact of renaming X", "find all references to X", "who depends on X", "what breaks if I change X".4---56# Code Impact Radius78The structured replacement for `grep -r <name>` followed by reading every match.910## When to invoke1112Whenever you (or the user) is about to:1314- Rename a function, variable, class, type, interface, or enum15- Change the shape of a type or function signature16- Move or delete a file / module / barrel export17- Audit who depends on a particular symbol (security review, deprecation, refactor planning)18- Decide whether a change is "safe to do alone" or "needs coordinated rollout"19- Answer "what calls X", "where is X used", "who imports X", "blast radius of X"2021This is the right tool whenever the question is **"what connects to this symbol?"**2223## How to invoke2425```bash26node ~/.claude/skills/code-impact-radius/src/cli.js <symbolName> --project <path> --json27```2829Or — if you want pretty terminal output instead of JSON:3031```bash32node ~/.claude/skills/code-impact-radius/src/cli.js <symbolName> --project <path> --pretty33```3435JSON is the default when stdout is not a TTY (so AI agents always get JSON).3637### Options3839| Flag | What it does |40|---|---|41| `--project <path>` | Project root containing `tsconfig.json` (default: `cwd`) |42| `--depth <n>` | Transitive caller depth (default `2`, max `10`) |43| `--kind <kind>` | Filter declaration kind: `variable` `function` `class` `method` `interface` `type` `enum` |44| `--at <file>:<line>` | Disambiguate when the name has multiple declarations |45| `--include-tests` | Include `*.test.ts` / `*.spec.ts` in references (default: skip) |46| `--max-sites <n>` | Truncate `references.sites` to first N entries |47| `--json` / `--pretty` | Force output mode |48| `--mermaid` | Emit a Mermaid flowchart diagram (paste into a Markdown ` ```mermaid ` block — GitHub, GitLab, VS Code, Obsidian, Notion render natively). |4950### Exit codes5152| Code | Meaning |53|---|---|54| 0 | Success — exactly one match, OR ambiguous list with N candidates |55| 1 | CLI usage error / runtime error |56| 2 | Symbol name not found |5758## Reading the JSON output5960```jsonc61{62 "query": // what was asked63 "matched": // how many declarations matched the name64 "candidates": // disambiguation list (only when matched > 1 or 0)65 "symbol": {66 "name": // canonical name67 "kind": // function | class | variable | ...68 "definition": {69 "file": "...", "line": 42, "column": 17,70 "snippet": "export function formatPrice..."71 }72 },73 "references": {74 "totalCount": 14,75 "byKind": {76 "definition": 1,77 "call": 8,78 "import": 3,79 "reExport": 1,80 "read": 1,81 "instantiation": 0,82 "jsx": 0,83 "type": 0,84 "write": 085 },86 "sites": [87 { "file": "...", "line": 18, "kind": "call", "snippet": "formatPrice(price, locale)", "parentKind": "CallExpression" }88 ],89 "truncated": 090 },91 "imports": {92 "exportedFrom": "src/lib/utils.ts",93 "importedBy": // every file that imports this symbol94 "reExportedFrom": // every barrel that re-exports it95 },96 "transitiveCallers": {97 "depth": 2,98 "callers": [99 { "depth": 1, "file": "...", "line": 18, "function": "PriceTag", "functionKind": "FunctionDeclaration" },100 { "depth": 2, "file": "...", "line": 87, "function": "CheckoutPage", "functionKind": "FunctionDeclaration" }101 ]102 },103 "warnings": []104}105```106107## Disambiguation flow108109If `matched > 1`, the tool returns `candidates` and asks for a `--at <file>:<line>` to pick one:110111```bash112$ code-impact-radius id --project ./app --json113# matched: 3, symbol: null, candidates: [{file, line, kind} × 3]114115$ code-impact-radius id --project ./app --at src/ambig.ts:11 --json116# matched: 1, symbol: { ... }117```118119## Decision-flow guidance for blast-radius checks120121Use this output to drive a structured impact table:1221231. **Definition** — read the file at `symbol.definition.file:line` to see the contract you're about to change.1242. **References by kind** — every `call`, `instantiation`, `jsx`, `read`, `write` site is a potential breakage.1253. **Imports** — every `imports.importedBy` entry needs updating if you rename the symbol.1264. **Re-exports** — barrels in `imports.reExportedFrom` need updating too.1275. **Transitive callers** — depth 2-3 catches most "indirect callers" that grep would miss.128129Then build the impact table (`file:line | usage | status: todo/done/verified-unaffected`) and tick each row off as you go.130131## What this tool does NOT cover132133- **String literals** — i18n keys (`t('home.title')`), SQL fragments, env-var names, URL paths. Use grep for those.134- **Dynamic imports** — `import('./mod')` calls aren't traced.135- **Cross-language** — TypeScript / TSX only. Python / Rust / Go / SQL stay grep territory.136137For string-literal scans, run grep alongside this tool. The two together cover the full surface.138139## Performance140141Cold-load on a ~200 TS/TSX file project: ~1.5s per query. ~700-file projects: ~5-8s. The TypeScript compiler initializes once per CLI invocation, so repeated queries in one session each pay that cost.