call-depth
Measures call chains with parsers, in any language.
How deep is this call chain? You can ask an agent to read the code and count, but then you have to spell out the counting rules every time. call-depth measures it with programs instead.
The agent traces a call chain from its root (a route, tool, job, CLI or handler). Each project-owned call adds 1 and each memory hop adds 2. A memory hop is a call that go-to-definition can’t follow, like a registry, getattr, reflection or a callback. A weighted depth over 3 is a hit.
The scanner works in any language, and it needs no runtime from your project.
Install
npx skills add Fullstack-Rocks/skills --skill call-depth -a claude-codenpx skills add Fullstack-Rocks/skills --skill call-depth -a codexnpx skills add Fullstack-Rocks/skills --skill call-depth -a cursornpx skills add Fullstack-Rocks/skills --skill call-depthInstall the call-depth skill from https://github.com/Fullstack-Rocks/skills by running npx skills add Fullstack-Rocks/skills --skill call-depth, then use it whenever you trace a call chain or check complexity and sizes.It installs best with under-engineering and under-engineering-review, since both of them call it.
Try saying
Transcript
> How deep is the call chain from POST /orders/{id}/refund? Use the call-depth skill. Skill(call-depth) · scan.py and python_scan.py over the refund files. Weighted depth 7: the two memory hops add 4 of the 7 points. 1. REFUND_HANDLERS[reason](db, order) at refund_service.py:20 looks up the handler in a dict by a string key. 2. getattr(self, f"_write_{kind}")(entry) at ledger.py:10 builds a method name at runtime. Replacing both with direct calls would bring the deepest path down to 4. Complexity: nothing on this path is over 10. Also found: the refund route is not registered, and an unknown reason raises a KeyError.
How does it work?
For every measurement the agent uses the first of these that works, and only reads code to confirm a finding or to follow a chain:
- A tool the project already uses, like its linter or its language server.
- The bundled scan.py. Real parsers, lizard and ast-grep rule packs for Python, JS/TS, Go, Ruby, Java, C# and PHP, with no runtime needed from the target project. It reports
FUNCTION_LENGTH,COMPLEXITY,DYNAMIC_CALL,DYNAMIC_ATTRandLOOKUP_CALL, andlayout.pyaddsFILE_LENGTH,FOLDER_SIZEandFOLDER_NAME. - Grep patterns, only for a language
scan.pyreports asNO_RULES, with every result labeled as approximate.
A language without a rule pack shows up as NO_RULES instead of being skipped silently, and adding a pack is one YAML file plus test fixtures. The scripts need Python 3.10+, and the agent installs ast-grep and lizard itself, at pinned versions and user level.
Here’s what scripts/scan.py reports on the demo fixture:
DYNAMIC_CALL app/services/ledger.py:10 getattr(self, f"_write_{kind}") (calls a method chosen at runtime)
LOOKUP_CALL app/services/refund_service.py:20 REFUND_HANDLERS[reason](db, order) (call through a subscript, usually a registry)
COVERED every scanned language has a rule packBut does it work?
Benchmark
Does the trace come out right?
The test was a refund route with a weighted depth of 7 and two hidden indirect calls, a dict registry and a getattr dispatch. When the prompt spelled out the counting rules, both versions got the depth and both calls right. The difference is that with the skill nobody has to write those rules into the prompt, and scan.py finds both indirect calls across 58 files in 0.11 s, before the agent reads any code.