fullstack.rocks/skills

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

Terminal
npx skills add Fullstack-Rocks/skills --skill call-depth -a claude-code

It installs best with under-engineering and under-engineering-review, since both of them call it.

Try saying

claude · demo shop
A real Claude Code session on the demo shop, recorded as it happened. Long thinking stretches are sped up, everything else plays at its real pace.
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:

  1. A tool the project already uses, like its linter or its language server.
  2. 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_ATTR and LOOKUP_CALL, and layout.py adds FILE_LENGTH, FOLDER_SIZE and FOLDER_NAME.
  3. Grep patterns, only for a language scan.py reports as NO_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 pack
Measured by parsers, not by reading. A language without a rule pack is NO_RULES, never skipped.

But 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.

The refund chain from POST /refund to Database.insert: weighted depth 7, with two hidden calls that add 2 each, REFUND_HANDLERS[reason] and a getattr dispatch. Claude alone and Claude with call-depth both got the depth and both hidden calls right, and scan.py found both in 0.11 seconds across 58 files.
Open full size (opens in a new tab)

The other skills