npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@callidescope/cli

v0.0.30

Published

Command-line tool for tracing call stacks across TypeScript monorepos and gating call depth

Downloads

1,968

Readme

🔭 Callidescope

npm

Traces call stacks across a TypeScript monorepo and flags the ones that got too deep.

Callidescope builds one call graph for the whole workspace, measures how deep a stack can get below each entry point, and reports the paths that exceed a limit you set — with every frame's file and line, so the next step is opening one.

It follows calls through NestJS injected dependencies. When a command calls this.someService.load(), the hop into SomeService.load is the whole point: that edge is invisible to anything reading one file at a time, and it is where most of this kind of codebase's control flow actually lives.

npm install --save-dev @callidescope/cli
callidescope --config projects/configuration/callidescope.config.ts
Stack #1 | 🚨 [DEPTH ≥ 10 > 6] (decorated-method)
🚀 CodometerCommand.run(_passedParameters: string[], options: CodometerCommandOptions): Promise<void> [.../codometer.command.ts:220]
   ↳ Measure the repository and write every configured output.
  └─> CodometerService.measure(args: MeasureArguments): CodeStatisticsResult [.../codometer.service.ts:115]
     ↳ Measure aggregated repository statistics for the provided directory.
    └─> LanguagesService.analyze(args: AnalyzeLanguagesArguments): LanguageResults [.../languages.service.ts:54]
       ↳ Analyze every language present in the discovered files.
      └─> JsonService.countArrayNode(node: unknown[], stats: JsonResult, depth: number): void (cycle) [.../json.service.ts:89]
         ↳ Count array nodes and their child values.

Why

A deep call stack is not automatically wrong, but it is always worth looking at. Ten frames between a command and the work it does usually means a layer that exists only to forward arguments, and the tools that would tell you are the ones that read a file at a time — so they see the forwarding and never the depth.

Two questions this answers that reading code does not:

  • How deep does this actually get? Following an injected dependency by hand means opening the module, finding the provider, and opening that — for every hop. The tool does it with the type checker, which is what makes it exact.
  • How much does this one callable reach? Breadth counts the distinct callables a function calls directly, which no file-at-a-time reader can resolve through an injected dependency or a structurally satisfied interface.

Usage

Tracing the workspace is the default command, so the flags below sit directly on callidescope:

npx callidescope --config projects/configuration/callidescope.config.ts --check depth

callidescope callidescope --config projects/configuration/callidescope.config.ts --check depth is the same run written out, which is what a task runner that always names a command spells. depth, breadth, and limits are matched by name before anything falls through to the default, so they are unaffected — and a word that names none of them is refused rather than quietly traced.

| Flag | Meaning | | ---- | ------- | | --config | Required. Path to the workspace callidescope.config.ts. Nothing is searched for, and a run without it is refused naming the flag | | -d, --directories | Comma-separated project directories to trace, each holding its own tsconfig.json. The projects their imports reach are traced too. Every such directory under the working directory when omitted | | --entry-point-addresses | Comma-separated path#name roots, overriding entryPoints.addresses | | --entry-point-decorators | Comma-separated decorator names, overriding entryPoints.decorators | | --exclude | Comma-separated globs, overriding an authored exclude. The tool's own default globs are kept, exactly as they are under a configured exclude | | --exclude-callees | Comma-separated callee patterns, overriding excludeCallees | | -f, --format | markdown, mermaid, or json, for what it prints. Markdown by default. Anything else is refused rather than rewritten | | --include-exported-functions | true or false, overriding entryPoints.includeExportedFunctions. The bare flag means true | | --include-orphans | true or false, overriding entryPoints.includeOrphans. The bare flag means true | | --include-tests | true or false, overriding entryPoints.includeTests. The bare flag means true | | --json | Where the machine-readable report goes. Overrides the path of a declared write.json, and nothing else about it. Needs --write or --check reports | | -m, --markdown | Where the markdown block goes. Overrides the path of a declared write.markdown, and nothing else about it. Needs --write or --check reports | | --maximum-breadth | A number, overriding limits.maximumBreadth wherever a project declared one. Refused against a configuration declaring none, because breadth has no default to override | | --maximum-depth | A number, overriding limits.maximumDepth wherever a project declared one | | --mermaid | Where the diagram goes. Overrides the path of a declared write.mermaid, and nothing else about it. Needs --write or --check reports | | --check | Fail on a comma-separated set drawn from breadth, depth, and reports | | --write | Write every configured destination |

Every flag but --check, --write, --format, and --config (which is required) is an override: it may change a value the configuration already declares and may not supply one it does not, so no flag can make an under-configured run legal. excludeFrom is the one configured field with no flag — it names the ignore files a run reads, which is what the run is rather than a value it judges by. A list flag written empty reads as absent, so a configured list cannot be cleared from the command line.

Three findings, named separately

A stack that runs deeper than the configured limit, a callable that calls more others directly than its project allows, and a report that no longer matches the code are separate findings, and --check names them separately. depth is callidescope's own word for the magnitude it measures — limit belongs to codometer, and blurring the two makes the messages unreadable.

| --check value | What fails the run | | --------------- | ------------------ | | breadth | A callable calling more callables directly than limits.maximumBreadth | | depth | A call stack deeper than limits.maximumDepth | | reports | A configured destination no longer holding what a fresh run would write |

--check refuses a value it does not recognize, and refuses a flag carrying no value at all. A set with nothing in it looks exactly like the flag having been left off, so it is a mistake rather than a shorthand: read as "gate nothing" it would be a gate that cannot fail. Every traced project's own configuration is complete, so limits.maximumBreadth is always there to judge --check breadth against — there is no project left to leave it undeclared, and so nothing left for --check breadth to be refused over.

depth and reports are separate because they belong on opposite sides of a pull request. Depth is the gate — a stack got longer in this change, and this change is what fixes it. In this workspace neither flag is invoked directly for that: the callidescope Nx plugin infers a gate target onto every project it does not exclude, judging depth always and breadth wherever a project declares limits.maximumBreadth — the same rules --check depth and --check breadth apply, reached through the plugin's own executor rather than a subprocess — scoped to just the projects a change touched:

nx affected --target=gate

Staleness is not, because a report goes stale whenever the call graph moves anywhere, which is nearly every change. The report is published on the default branch instead, where nothing else is competing to rewrite the same block:

nx run configuration:callidescope:write

Nothing writes unless it is asked to. A run given neither --write nor --check reports reads no destination and rewrites none, so a gate on a pull request leaves every committed report exactly as it found it. --write --check reports is refused outright: a report cannot be stale in the run that just wrote it.

Those two configurations are the whole target — there is no third one for the release, because nothing forwards a configuration to it. lint-code does not depend on callidescope: Nx forwards an explicit configuration down dependsOn, so if it did, lint-code --configuration=write would publish the report from a branch. configuration:codometer sits outside that list for the same reason. The depth gate is therefore named directly, alongside lint-code and inside the same nx affected invocation, in both places that gate: the 🧑‍🔧 Lint Codebase workflow, so it runs on every pull request, and projects/configuration/lint-staged.config.ts, so it runs on every commit. Depth reads source and needs no build, which is what keeps it in the commit path where codometer's limits — read from compiled output — cannot go.

Two failures no flag turns off

--check chooses which findings about the code fail the run. Two failures are not findings about the code at all, and neither waits to be asked:

| Failure | What it means | | ------- | ------------- | | 🔭 Rejected a project it could not read | A project's tsconfig.json could not be parsed, or a named directory holds none | | 🔭 Traced nothing | The run collected no callables at all |

Both exist because the alternative is a green gate over a workspace nobody looked at. A run reports what it found, so a run that found nothing reports nothing — which reads exactly like a clean workspace.

A tsconfig.json that will not parse ends the run where it happens, before anything is printed and before any destination is touched. It is tempting to step over the project and trace the rest, and that is wrong here: destinations are written before findings are weighed, so a partial graph would publish depths measured through a workspace that was missing a project, and only then fail. On the default branch that means committing wrong numbers into every project README, which exiting non-zero afterwards does not take back. Ending the trace leaves the checkout exactly as the run found it.

A directory named on --directories that holds no tsconfig.json at all ends the run the same way, and for the same reason. Naming a directory is the caller saying it should be traced, so quietly tracing one fewer project than was asked for reports depths for a workspace nobody described — and a typo in the list passes every gate for having looked at less. The whole-workspace walk cannot reach this, since it only ever yields directories a tsconfig.json was found in.

A project that should not be read at all is a different question, and exclusions answer it. They are applied to the tsconfig.json itself, before it is opened — excluding a project's files is too late, because opening its configuration is the step that fails. This repository's own .callidescopeignore names one such project: a fixture in codependix-examples written not to parse.

Where the report goes

Printing and writing are separate. --format decides what reaches the terminal; the destinations under output in the config decide what reaches a file, and both can be on at once.

Markdown is the console default because it is the one rendering that reads in a terminal, pastes into an issue, and is already what the files hold. --format json is for a machine reading standard output, and --format mermaid prints diagram source to paste somewhere that draws it.

Three destinations, each independent:

| write key | What it writes | | ----------- | -------------- | | json | The whole run as JSON, at one path | | markdown | The whole run, spliced between anchors in one file | | mermaid | The same report with its stacks drawn instead of printed |

A project's own section is not among them. Each project declares its own write.markdown in its own callidescope.config.ts, so where a section lands is that project's to say — see Project Configuration.

The diagram

mermaid is its own destination rather than a mode on markdown, so a repository can publish both. They answer different questions: the tree says what each frame takes, returns, and documents; the diagram says what shape they make together.

Both anchored destinations render the same report, so a diagram draws exactly what the markdown one prints: the run's call stacks over the depth limit. The committed example of one is projects/ic-suite/callidescope/callidescope-examples/output/diagram.md, where five stacks over that package's limit are drawn as 38 callables and 34 arrows. This repository publishes no diagram at all — its deepest stack sits exactly at the limit, so there is nothing over it to draw, and a --format mermaid run here prints None. under that heading.

All the stacks are drawn as one flowchart, not one apiece. A single stack is a straight line, and a straight line is a list with extra steps. Drawn together the shared tails converge — every command reaching the same repository, every resolver ending in the same service — and that convergence is the thing a picture shows and an indented tree cannot.

Entry points are drawn as stadiums and everything below them as boxes. Shape rather than color, because the diagram is read in whichever theme the reader has and only one of those is the one it was authored in. Labels carry the callable's name alone: a diagram trying to also carry signatures is unreadable at any size, and the tree already has room for them.

A diagram stops at 300 callables — GitHub refuses a mermaid block past 50,000 characters. Whole stacks are dropped rather than trimmed, so the diagram never contains an edge into something it did not draw, and it says how many it left out. The cap is generous against one project's worth of stacks and tight against a workspace's: this repository's widest project, caelundas, has 96 call stacks spanning 263 distinct callables, 27 of them called from more than one place, so a set that size fits whole; the 554 stacks its 53 projects hold between them would reach the 300 and leave 451 out. Neither set is drawn here — nothing in this workspace is over the depth limit — but they are the scale the cap is set against.

The ## 🔭 Callidescope section at the bottom of every package in this repository is that package's own declaration rather than anything the run fans out. Each project's file names the document its section lands in, and the run publishes what it finds declared:

write: { markdown: { heading: "## 🔭 Callidescope", path: "README.md" } }

Each section carries that project's stacks and findings rather than the workspace's, the first three stacks openly and the rest behind a disclosure — previewCount on the destination is what decides how many, because it is a fact about the document rather than about the run.

Under --check reports a stale section fails and names every file that drifted, rather than stopping at the first. This repository does not run that on a pull request: nx run configuration:callidescope:write writes the sections on the default branch, and the release commits them.

Narrowing with --directories is the difference between a whole-workspace analysis and a check that finishes in seconds, because each project needs its own TypeScript program. What such a run measures — and what it deliberately leaves out — is its own section below.

Depth and breadth for named callables

callidescope reports the whole workspace; depth and breadth answer a narrower question about callables you name, addressed the way a Python traceback or an ESLint rule id points at a symbol — the file path and the qualified name callidescope already prints in every stack, joined by #:

callidescope depth --config projects/configuration/callidescope.config.ts --addresses src/foo.service.ts#FooService.bar
callidescope breadth --config projects/configuration/callidescope.config.ts --addresses src/foo.service.ts#FooService.bar

--addresses is comma-separated, so one run can report on several callables — which is what a rename spanning a handful of them actually needs:

callidescope depth --config projects/configuration/callidescope.config.ts --addresses src/foo.service.ts#FooService.bar,src/bar.service.ts#BarService.baz

Every address has to resolve, or the run prints nothing. A report covering the addresses that were understood, under an exit code claiming success, is worse than no report; a run naming an address it cannot match reports each failure and exits non-zero.

A file holding more than one declaration under the same qualified name — two overloads, two callbacks bound to the same property — is disambiguated with a trailing :<line>, and both commands print every candidate's line when they cannot tell which one was meant.

--addresses is prompted for when omitted. At a terminal, both commands offer every callable the trace found as an autocompleting multiselect, so the flag is something to skip rather than something to look up. With no terminal there is nobody to ask, so the run is refused by name and exits non-zero rather than drawing a menu nothing can answer.

Both accept every graph-shaping flag callidescope itself does, since resolving an address still means tracing the workspace first: --directories, --entry-point-addresses, --entry-point-decorators, --exclude, --exclude-callees, --include-exported-functions, --include-orphans, and --include-tests, plus the required --config and --format. Each means exactly what it means above.

Neither takes --check, --write, --json, --markdown, --mermaid, --maximum-breadth, or --maximum-depth: a lookup gates nothing and writes nothing, so a limit or a destination has no value here to override. It only ever prints, to whichever format --format names.

Under --format json both print an array, whatever the address count, so one run is one document JSON.parse accepts without first counting how many addresses were asked about.

depth prints every path above the callable and every path below it — every caller chain up to a root, every callee chain down to a leaf — rather than folding each direction into the single deepest one callidescope's own report keeps. A callable reached from a dozen places, or reaching a dozen leaves, is exactly the shape this is asked to show in full, capped at 200 paths per direction so a widely-called utility cannot make the walk run away; a capped run says so.

breadth prints the callable's direct callees and direct callers side by side — what it calls, and what calls it — the two questions a refactor or a rename needs answered together before either one is safe.

What a scoped run measures

--directories narrows a run to the project directories it names. The trace still follows calls out of them: a scoped run builds a TypeScript program for each named project and for every project those projects' imports transitively reach — the named projects' dependency closure.

That closure is what makes a scoped depth a measurement rather than an approximation. A project's own tsconfig.json never lists the packages it imports, so without it a call leaving the named directory landed in code no traced project owned — and a call into unowned code is treated as external, a leaf, exactly like a call into an installed package. The stack ended at the package boundary and reported a plain number, with nothing in the report saying it had stopped early.

Scoped to projects/ic-suite/codometer/codometer-changes, this repository traces 3 projects, 33 files, and 102 callables, in about 1.7 seconds. The same command before the closure traced 1 project, 8 files, and 31 callables.

A closure is derived from what the compiler really read — every file in a project's program — rather than from the dependency list a package.json declares. A manifest says what a package may import; the program says what it did. So a type-only import widens a closure, and a declared dependency nothing imports does not.

What a closure does not reach

Dependents, deliberately. The walk runs downward only: a project that imports a scoped one is not built, and its stacks do not appear. A call stack runs downward too, so a dependent contributes no frames below anything the run measures — and leaving it out is what stops an edit in a dependent from moving the scoped project's numbers.

A project root holding no package.json. A manifest is what makes a directory something another project can depend on. A root holding only a tsconfig.json is where a repository keeps shared settings, and shared settings are read by every project rather than depended on by any. Without this rule one such directory drags the whole workspace in: each package's tsconfig.json includes its own tooling configuration files, each of those imports out of the shared directory, and that directory's program then reaches every toolchain the repository configures.

The workspace root. A project whose root contains every other project cannot be a meaningful dependency of any of them, whatever else it holds.

Both rules refuse a destination only. Naming a directory is the caller saying it should be traced, and a run naming no directory names every project — so either kind is still traced in full when asked for directly, and a whole-workspace run's findings are exactly what they were. What the rules cost is that a call into a refused directory resolves to no frame, the way every call out of a package did before closures existed.

Numbers that do not move with the scope

A file is owned by the deepest project root containing it, whichever program pulled it in — not by whichever program happened to read it first. That is what makes two runs agree: the same callable measures the same depth whether the run was scoped to its own project, scoped to something that depends on it, or scoped to nothing at all — provided each of those runs builds the project declaring it, which a refused destination's is not.

Both findings survive a downward-only scope, which is worth saying because it is not obvious: depth and breadth both fold over a callable's callees, which run downward — precisely what a closure holds in full. Neither needs the dependents a scoped run leaves out.

Publishing does not widen with measurement. A run publishes a section only for the projects it was scoped to, so a scoped run never rewrites a section in a dependency it merely measured — and a whole-workspace run still publishes every project's, because a run naming no directory has every project as a scoped one.

dependency-closure works all of this through on a real call that leaves its package, with the projects it reaches and why listed one by one.

Scoping by Nx project name

--directories takes paths, because callidescope has no idea what workspace tool you use — a directory holding a tsconfig.json is the whole contract, and it holds in a monorepo, a single package, or neither.

An Nx workspace can hand the selecting to Nx instead, through @callidescope/nx — a plugin that infers a trace target onto every project:

nx run-many -t trace --projects=tag:type:package
nx affected -t trace
nx run callidescope-graph:depth --addresses="src/foo.service.ts#FooService.bar"

It infers a trace, a depth, and a breadth target onto every project, so callidescope, depth, and breadth all become tasks — with Nx's own project selection, caching, and affected-detection for free, none of which a flag here could offer. It also resolves each project's Nx dependencies into directories of their own, which makes them projects the run is scoped to rather than ones it merely measures — so their sections are published too, and the selection comes from the declared graph rather than from whatever the compiler happened to read. A stack is not truncated at a package boundary either way: the dependency closure above is what stops that, and it needs no Nx graph.

It is a separate package rather than a flag here on purpose: this CLI depends on nothing Nx-shaped, and a flag that only worked when an optional package happened to be installed would advertise in --help something that silently did nothing without it.

What it reports

Deep call stacks. The single deepest path below each entry point, when it exceeds maximumDepth. Only one path per entry point is ever built — the deepest — so a wide graph costs no more than a narrow one.

Wide callables. A callable calling more distinct callables directly than maximumBreadth allows, reported per project the same way depth is.

A depth printed as ≥ 10 is a floor rather than a measurement: something on that path could not be followed — a callback invoked through a parameter, a computed member name — and the run says so rather than quietly under-reporting. Narrowing a run is never what causes one: a scoped run builds its dependencies' programs too, so a stack leaving the named package keeps going and reports a plain number like any other.

What a frame carries

Every frame is annotated from the type checker, because a stack of bare names is a list of places to go look rather than something you can read:

  • The signature — parameter names and types, and the return type. On this repository that is all 1,338 reported frames.
  • The documentation summary — the JSDoc prose, collapsed to one line. 801 of those 1,338 have one.
  • Tags, including @deprecated, which marks the frame inline.

Both come from the checker rather than the comment trivia, which is what makes them right on the shapes this repository writes. An overload's documentation lives on the signature rather than the implementation the graph points at; an arrow-typed property's lives on the property rather than the arrow; a destructured parameter has no name at all in the syntax. The checker resolves all three.

A signature longer than 80 characters collapses to (…): ReturnType. That is almost always a NestJS constructor taking a dozen injected services — which services those are is noise at the point where you are reading a stack, and a 440-character line destroys the indentation that makes the stack legible.

A summary longer than 120 characters prints only its opening sentence. Comments here state what a callable does and then explain why, and the first half is the half that orients someone reading a stack. It prints unmarked, because a whole sentence is a complete thought rather than an elision and the frame's file:line already points at the rest. Only a single sentence with no boundary to find is cut on a word and marked …, which across this repository is 51 of those 801 summaries.

Shortening applies to the printed tree only. The JSON report carries every comment in full, because a machine reading it has no line width to respect — the longest in the workspace runs 1,507 characters.

Annotations are read only for the frames a report actually prints, not for all 4,727 callables. Rendering a type is the one genuinely costly thing the checker does, and reports touch a few hundred frames.

How it follows a call

The TypeScript type checker resolves each call site. A call on a constructor-injected property needs no special handling: the parameter property carries the service's type, and the checker follows it.

| Written as | Resolved by | | ---------- | ----------- | | helper() | The symbol at the callee, unwrapped through import aliases | | this.service.load() | The symbol at the member name — the injected-dependency case | | provider.ingest() | Every class structurally satisfying the interface, capped by the implementation-candidate cap | | super.run() | The base declaration the checker resolves to | | new Thing() | The constructor, when it has a body | | list.map(callback) | The callback, as its own frame — map itself is external | | target[key]() | Nothing. Recorded as unfollowable rather than guessed |

Calls into installed dependencies are leaves. Whether Array.prototype.map is deeply implemented says nothing about whether your layering is too deep, and counting it would make every number move on an unrelated upgrade. A call into another project of the same workspace is not one of these: it resolves to a real frame, because a scoped run builds that project's program too — see what a scoped run measures.

Structural matching is not optional: classes here routinely satisfy an interface without writing implements, and its members are usually arrow-typed properties rather than method signatures. A nominal-only index finds none of them.

Recursion

Cycles are collapsed before depth is measured, so a mutually recursive cluster of three contributes three frames once — an honest floor on a stack that has no ceiling. The alternative, detecting a repeat visit mid-walk, makes the answer depend on which path arrived first, so the same function reports different depths from different entry points and between runs. A linter whose numbers move on their own is not usable as a gate.

Entry points

Depth is only meaningful relative to a root, and most code here is called by a framework rather than by the repository. Roots are therefore configurable: decorated methods, lifecycle hooks, main.ts bootstraps, and every src/index.ts export.

Anything left with no caller is promoted to an orphan root. That is the safety net: without it, a missing rule silently removes a whole subtree from every measurement instead of showing up.

Agent skills

Agent skills for coding agents working with callidescope are published in @callidescope/agents:

| Skill | Description | | ----- | ----------- | | callidescope-trace | Trace the workspace or inspect depth and breadth for named callables | | callidescope-configure | Configure callidescope flags, rules, and callidescope.config.ts | | callidescope-triage | Triage and resolve depth limit breaches and gate failures |

Examples

Every rule, finding, and output on this page has a worked example in @callidescope/examples — a small codebase written to be traced, with its rendered reports committed. Its AGENTS.md is a "callidescope reported X → open this example" table, so a failing gate has somewhere to go.

Non-goals

Control flow graphs. Intra-procedural branching is already capped by this repository's ESLint rules (complexity, max-depth, max-statements), and a CFG is not what call-stack depth needs. Building one would duplicate ESLint at much higher cost.

Clone detection. Repeated call-stack shapes are not a useful signal in a dependency-injected codebase — the shared suffix is the service layer, so every resolver ends the same way. jscpd already covers real duplication.

Packages

| Package | Role | | ------- | ---- | | @callidescope/cli | Orchestrates a run: traces the workspace, plans what to check, and reports | | @callidescope/configuration | Reads callidescope.config.ts and resolves the limits | | @callidescope/graph | Builds the call graph from traced source and measures depth and breadth | | @callidescope/nx | Nx plugin: per-project trace/depth/breadth targets, scoped through the Nx dependency graph | | @callidescope/output | Renders findings into markdown, mermaid, and JSON | | @callidescope/examples | A traced fixture codebase carrying one worked example of everything above |

Start

nx run callidescope-cli:start

Test

nx run callidescope-cli:vitest

Contributing

nx run callidescope-cli:lint-code --configuration=check

License

MIT — see LICENSE.

👔 Conformetry

This project was generated from the nestjs-command-project conformetry template.

🔭 Callidescope

Call stacks traced through projects/ic-suite/callidescope/callidescope-cli, deepest first. Each frame shows what it takes, what it returns, and what its documentation says.

| Measure | Value | | --- | --- | | Callables | 113 | | Files | 34 | | Calls traced | 153 | | Call stacks | 42 | | Deepest stack | 15 | | Stacks through recursion | 0 | | Unfollowable calls | 5 |

Limits

What this project is judged against, as declared in its own callidescope.config.ts.

| Limit | Value | | --- | --- | | maximumDepth | 15 | | maximumBreadth | 11 |

Call stacks (depth)

1. CallidescopeCommand.run — depth ≥ 15 · decorated-method

🚀 CallidescopeCommand.run(passedParameters: string[], options: CallidescopeCommandOptions): Promise<void> [projects/ic-suite/callidescope/callidescope-cli/src/modules/callidescope/callidescope.command.ts:474]
   ↳ Traces the workspace, reports, and sets the exit code.
  └─> CallidescopeCommand.traceWorkspace(options: CallidescopeCommandOptions): Promise<void> [projects/ic-suite/callidescope/callidescope-cli/src/modules/callidescope/callidescope.command.ts:174]
     ↳ Traces the workspace, reports, and sets the exit code.
    └─> CallidescopeService.trace(args: TraceArguments): Promise<TraceOutcome> [projects/ic-suite/callidescope/callidescope-cli/src/modules/callidescope/callidescope.service.ts:408]
       ↳ Traces a workspace and returns everything the run found.
      └─> CallidescopeService.analyze(…): AnalyzeOutcome [projects/ic-suite/callidescope/callidescope-cli/src/modules/callidescope/callidescope.service.ts:295]
         ↳ Derives every finding from the collected callables.
        └─> GraphAssemblyService.assemble(args: AssembleGraphArguments): AssembledGraph [projects/ic-suite/callidescope/callidescope-graph/src/modules/graph/graph-assembly.service.ts:43]
           ↳ Builds the call graph and everything derived from it.
          └─> EdgesService.build(args: BuildEdgesArguments): EdgeCollection [projects/ic-suite/callidescope/callidescope-graph/src/modules/edges/edges.service.ts:251]
             ↳ Builds every edge in the graph, and records the calls it could not.
            └─> EdgesService.buildSiteEdges(…): { edges: CallEdge[]; unresolved: UnresolvedCall[]; } [projects/ic-suite/callidescope/callidescope-graph/src/modules/edges/edges.service.ts:59]
               ↳ Turns one call site into the edges and non-resolutions it produced.
              └─> EdgesService.resolveSite(…): ResolvedCallSite | undefined [projects/ic-suite/callidescope/callidescope-graph/src/modules/edges/edges.service.ts:226]
                 ↳ Resolves one call site, choosing the right strategy for its shape.
                └─> SymbolResolutionService.resolve(…): ResolvedCallSite [projects/ic-suite/callidescope/callidescope-graph/src/modules/edges/symbol-resolution.service.ts:267]
                   ↳ Resolves a call expression to every declaration it can reach.
                  └─> SymbolResolutionService.resolveSymbol(…): ResolvedCallSite [projects/ic-suite/callidescope/callidescope-graph/src/modules/edges/symbol-resolution.service.ts:165]
                     ↳ Resolves an already-identified callee symbol to its declarations.
                    └─> SymbolResolutionService.resolveThroughHierarchy(…): ResolvedCallSite [projects/ic-suite/callidescope/callidescope-graph/src/modules/edges/symbol-resolution.service.ts:217]
                       ↳ Expands an interface or abstract member to its implementations.
                      └─> ClassesService.resolveImplementations(…): ImplementationLookup [projects/ic-suite/callidescope/callidescope-graph/src/modules/classes/classes.service.ts:204]
                         ↳ Finds the concrete declarations one interface member resolves to.
                        └─> ClassesService.flatMap(…)(this: undefined, candidate: ts.ClassDeclaration): ts.Declaration[] [projects/ic-suite/callidescope/callidescope-graph/src/modules/classes/classes.service.ts:232]
                          └─> ClassesService.readMemberDeclarations(…): Declaration[] [projects/ic-suite/callidescope/callidescope-graph/src/modules/classes/classes.service.ts:148]
                             ↳ Reads one member's concrete declarations off a candidate class.
                            └─> ClassesService.filter(…)(member: ts.PropertyDeclaration | ts.MethodDeclaration): boolean [projects/ic-suite/callidescope/callidescope-graph/src/modules/classes/classes.service.ts:162]

2. BreadthCommand.run — depth ≥ 15 · decorated-method

🚀 BreadthCommand.run(_passedParameters: string[], options: AddressCommandOptions): Promise<void> [projects/ic-suite/callidescope/callidescope-cli/src/modules/breadth/breadth.command.ts:320]
   ↳ Prints each named callable's direct callers and callees.
  └─> BreadthCommand.printBreadth(options: AddressCommandOptions): Promise<void> [projects/ic-suite/callidescope/callidescope-cli/src/modules/breadth/breadth.command.ts:127]
     ↳ Resolves the addresses and prints their direct callers and callees.
    └─> AddressLookupService.locate(options: AddressCommandOptions): Promise<LocatedWorkspace> [projects/ic-suite/callidescope/callidescope-cli/src/modules/address-lookup/address-lookup.service.ts:87]
       ↳ Loads the configuration and traces the workspace, matching nothing yet.
      └─> CallidescopeService.locate(args: TraceArguments): Promise<LocateOutcome> [projects/ic-suite/callidescope/callidescope-cli/src/modules/callidescope/callidescope.service.ts:394]
         ↳ Collects every callable and assembles the graph over them, without running the analysis a full trace does.
        └─> GraphAssemblyService.assemble(args: AssembleGraphArguments): AssembledGraph [projects/ic-suite/callidescope/callidescope-graph/src/modules/graph/graph-assembly.service.ts:43]
           ↳ Builds the call graph and everything derived from it.
          └─> EdgesService.build(args: BuildEdgesArguments): EdgeCollection [projects/ic-suite/callidescope/callidescope-graph/src/modules/edges/edges.service.ts:251]
             ↳ Builds every edge in the graph, and records the calls it could not.
            └─> EdgesService.buildSiteEdges(…): { edges: CallEdge[]; unresolved: UnresolvedCall[]; } [projects/ic-suite/callidescope/callidescope-graph/src/modules/edges/edges.service.ts:59]
               ↳ Turns one call site into the edges and non-resolutions it produced.
              └─> EdgesService.resolveSite(…): ResolvedCallSite | undefined [projects/ic-suite/callidescope/callidescope-graph/src/modules/edges/edges.service.ts:226]
                 ↳ Resolves one call site, choosing the right strategy for its shape.
                └─> SymbolResolutionService.resolve(…): ResolvedCallSite [projects/ic-suite/callidescope/callidescope-graph/src/modules/edges/symbol-resolution.service.ts:267]
                   ↳ Resolves a call expression to every declaration it can reach.
                  └─> SymbolResolutionService.resolveSymbol(…): ResolvedCallSite [projects/ic-suite/callidescope/callidescope-graph/src/modules/edges/symbol-resolution.service.ts:165]
                     ↳ Resolves an already-identified callee symbol to its declarations.
                    └─> SymbolResolutionService.resolveThroughHierarchy(…): ResolvedCallSite [projects/ic-suite/callidescope/callidescope-graph/src/modules/edges/symbol-resolution.service.ts:217]
                       ↳ Expands an interface or abstract member to its implementations.
                      └─> ClassesService.resolveImplementations(…): ImplementationLookup [projects/ic-suite/callidescope/callidescope-graph/src/modules/classes/classes.service.ts:204]
                         ↳ Finds the concrete declarations one interface member resolves to.
                        └─> ClassesService.flatMap(…)(this: undefined, candidate: ts.ClassDeclaration): ts.Declaration[] [projects/ic-suite/callidescope/callidescope-graph/src/modules/classes/classes.service.ts:232]
                          └─> ClassesService.readMemberDeclarations(…): Declaration[] [projects/ic-suite/callidescope/callidescope-graph/src/modules/classes/classes.service.ts:148]
                             ↳ Reads one member's concrete declarations off a candidate class.
                            └─> ClassesService.filter(…)(member: ts.PropertyDeclaration | ts.MethodDeclaration): boolean [projects/ic-suite/callidescope/callidescope-graph/src/modules/classes/classes.service.ts:162]

3. DepthCommand.run — depth ≥ 15 · decorated-method

🚀 DepthCommand.run(_passedParameters: string[], options: AddressCommandOptions): Promise<void> [projects/ic-suite/callidescope/callidescope-cli/src/modules/depth/depth.command.ts:295]
   ↳ Resolves the addresses, traces every path above and below each, and prints them.
  └─> DepthCommand.printDepth(options: AddressCommandOptions): Promise<void> [projects/ic-suite/callidescope/callidescope-cli/src/modules/depth/depth.command.ts:89]
     ↳ Traces every path above and below each resolved address, and prints them.
    └─> AddressLookupService.locate(options: AddressCommandOptions): Promise<LocatedWorkspace> [projects/ic-suite/callidescope/callidescope-cli/src/modules/address-lookup/address-lookup.service.ts:87]
       ↳ Loads the configuration and traces the workspace, matching nothing yet.
      └─> CallidescopeService.locate(args: TraceArguments): Promise<LocateOutcome> [projects/ic-suite/callidescope/callidescope-cli/src/modules/callidescope/callidescope.service.ts:394]
         ↳ Collects every callable and assembles the graph over them, without running the analysis a full trace does.
        └─> GraphAssemblyService.assemble(args: AssembleGraphArguments): AssembledGraph [projects/ic-suite/callidescope/callidescope-graph/src/modules/graph/graph-assembly.service.ts:43]
           ↳ Builds the call graph and everything derived from it.
          └─> EdgesService.build(args: BuildEdgesArguments): EdgeCollection [projects/ic-suite/callidescope/callidescope-graph/src/modules/edges/edges.service.ts:251]
             ↳ Builds every edge in the graph, and records the calls it could not.
            └─> EdgesService.buildSiteEdges(…): { edges: CallEdge[]; unresolved: UnresolvedCall[]; } [projects/ic-suite/callidescope/callidescope-graph/src/modules/edges/edges.service.ts:59]
               ↳ Turns one call site into the edges and non-resolutions it produced.
              └─> EdgesService.resolveSite(…): ResolvedCallSite | undefined [projects/ic-suite/callidescope/callidescope-graph/src/modules/edges/edges.service.ts:226]
                 ↳ Resolves one call site, choosing the right strategy for its shape.
                └─> SymbolResolutionService.resolve(…): ResolvedCallSite [projects/ic-suite/callidescope/callidescope-graph/src/modules/edges/symbol-resolution.service.ts:267]
                   ↳ Resolves a call expression to every declaration it can reach.
                  └─> SymbolResolutionService.resolveSymbol(…): ResolvedCallSite [projects/ic-suite/callidescope/callidescope-graph/src/modules/edges/symbol-resolution.service.ts:165]
                     ↳ Resolves an already-identified callee symbol to its declarations.
                    └─> SymbolResolutionService.resolveThroughHierarchy(…): ResolvedCallSite [projects/ic-suite/callidescope/callidescope-graph/src/modules/edges/symbol-resolution.service.ts:217]
                       ↳ Expands an interface or abstract member to its implementations.
                      └─> ClassesService.resolveImplementations(…): ImplementationLookup [projects/ic-suite/callidescope/callidescope-graph/src/modules/classes/classes.service.ts:204]
                         ↳ Finds the concrete declarations one interface member resolves to.
                        └─> ClassesService.flatMap(…)(this: undefined, candidate: ts.ClassDeclaration): ts.Declaration[] [projects/ic-suite/callidescope/callidescope-graph/src/modules/classes/classes.service.ts:232]
                          └─> ClassesService.readMemberDeclarations(…): Declaration[] [projects/ic-suite/callidescope/callidescope-graph/src/modules/classes/classes.service.ts:148]
                             ↳ Reads one member's concrete declarations off a candidate class.
                            └─> ClassesService.filter(…)(member: ts.PropertyDeclaration | ts.MethodDeclaration): boolean [projects/ic-suite/callidescope/callidescope-graph/src/modules/classes/classes.service.ts:162]

4. LimitsCommand.run — depth ≥ 7 · decorated-method

🚀 LimitsCommand.run(_passedParameters: string[], options: LimitsCommandOptions): Promise<void> [projects/ic-suite/callidescope/callidescope-cli/src/modules/limits/limits.command.ts:90]
   ↳ Prints every project's resolved limits.
  └─> LimitsService.list(options: LimitsCommandOptions): Promise<ProjectLimitRow[]> [projects/ic-suite/callidescope/callidescope-cli/src/modules/limits/limits.service.ts:150]
     ↳ Resolves every project's limits, workspace default first.
    └─> ConfigurationService.loadConfigurationFile(args: LoadConfigurationArguments): Promise<LoadedCallidescopeConfiguration> [projects/ic-suite/callidescope/callidescope-configuration/src/modules/configuration/configuration.service.ts:80]
       ↳ Loads a configuration, and says what the file itself declared and which file answered.
      └─> ConfigurationFileService.loadConfigurationFile(args: LoadConfigurationArguments): Promise<LoadedCallidescopeConfiguration> [projects/ic-suite/callidescope/callidescope-configuration/src/modules/configuration/configuration-file.service.ts:290]
         ↳ Loads a configuration, and says what the file itself declared and which file answered.
        └─> ConfigurationFileService.resolveConfigurationPath(configurationPath: string): string [projects/ic-suite/callidescope/callidescope-configuration/src/modules/configuration/configuration-file.service.ts:130]
           ↳ Resolves a configuration path against the cwd, then the repository root.
          └─> ConfigurationFileService.findRepositoryRoot(): string | undefined [projects/ic-suite/callidescope/callidescope-configuration/src/modules/configuration/configuration-file.service.ts:69]
             ↳ Walks upward from the process cwd looking for the repository root.
            └─> ConfigurationFileService.some(…)(marker: ".git" | "pnpm-workspace.yaml"): boolean [projects/ic-suite/callidescope/callidescope-configuration/src/modules/configuration/configuration-file.service.ts:74]

5. CallidescopeCommand.parseConfig — depth ≥ 4 · decorated-method

🚀 CallidescopeCommand.parseConfig(value: string | undefined): string [projects/ic-suite/callidescope/callidescope-cli/src/modules/callidescope/callidescope.command.ts:283]
   ↳ Parses `--config`, which every command that loads a configuration requires.
  └─> ConfigurationService.requireConfigurationPath(path: string | undefined, surface: string): string [projects/ic-suite/callidescope/callidescope-configuration/src/modules/configuration/configuration.service.ts:139]
     ↳ Reads the configuration path, refusing an absent or blank one by surface.
    └─> InputService.requireConfigurationPath(path: string | undefined, surface: string): string [projects/ic-suite/callidescope/callidescope-configuration/src/modules/input/input.service.ts:217]
       ↳ Trims the configuration path, refusing an absent or blank one.
      └─> InputService.parseOptionalOption(value: string | undefined): string | undefined [projects/ic-suite/callidescope/callidescope-configuration/src/modules/input/input.service.ts:112]
         ↳ Trims an optional string option, treating blank as absent.

6. CallidescopeCommand.parseDirectories — depth 4 · decorated-method

🚀 CallidescopeCommand.parseDirectories(value: string | undefined): string[] [projects/ic-suite/callidescope/callidescope-cli/src/modules/callidescope/callidescope.command.ts:297]
   ↳ Parses `--directories`, a comma-separated list of project directories.
  └─> ConfigurationService.parseCommaDelimitedOption(value: string | undefined): string[] [projects/ic-suite/callidescope/callidescope-configuration/src/modules/configuration/configuration.service.ts:97]
     ↳ Splits a comma-separated flag value into its parts.
    └─> InputService.parseCommaDelimitedOption(value: string | undefined): string[] [projects/ic-suite/callidescope/callidescope-configuration/src/modules/input/input.service.ts:102]
       ↳ Splits `--directories`, a comma-separated list of project directories.
      └─> InputService.map(…)(entry: string): string [projects/ic-suite/callidescope/callidescope-configuration/src/modules/input/input.service.ts:107]

7. CallidescopeCommand.parseEntryPointAddresses — depth 4 · decorated-method

🚀 CallidescopeCommand.parseEntryPointAddresses(value: string | undefined): string[] [projects/ic-suite/callidescope/callidescope-cli/src/modules/callidescope/callidescope.command.ts:306]
   ↳ Parses `--entry-point-addresses`, overriding `entryPoints.addresses`.
  └─> ConfigurationService.parseCommaDelimitedOption(value: string | undefined): string[] [projects/ic-suite/callidescope/callidescope-configuration/src/modules/configuration/configuration.service.ts:97]
     ↳ Splits a comma-separated flag value into its parts.
    └─> InputService.parseCommaDelimitedOption(value: string | undefined): string[] [projects/ic-suite/callidescope/callidescope-configuration/src/modules/input/input.service.ts:102]
       ↳ Splits `--directories`, a comma-separated list of project directories.
      └─> InputService.map(…)(entry: string): string [projects/ic-suite/callidescope/callidescope-configuration/src/modules/input/input.service.ts:107]

8. CallidescopeCommand.parseEntryPointDecorators — depth 4 · decorated-method

🚀 CallidescopeCommand.parseEntryPointDecorators(value: string | undefined): string[] [projects/ic-suite/callidescope/callidescope-cli/src/modules/callidescope/callidescope.command.ts:315]
   ↳ Parses `--entry-point-decorators`, overriding `entryPoints.decorators`.
  └─> ConfigurationService.parseCommaDelimitedOption(value: string | undefined): string[] [projects/ic-suite/callidescope/callidescope-configuration/src/modules/configuration/configuration.service.ts:97]
     ↳ Splits a comma-separated flag value into its parts.
    └─> InputService.parseCommaDelimitedOption(value: string | undefined): string[] [projects/ic-suite/callidescope/callidescope-configuration/src/modules/input/input.service.ts:102]
       ↳ Splits `--directories`, a comma-separated list of project directories.
      └─> InputService.map(…)(entry: string): string [projects/ic-suite/callidescope/callidescope-configuration/src/modules/input/input.service.ts:107]

9. CallidescopeCommand.parseExclude — depth 4 · decorated-method

🚀 CallidescopeCommand.parseExclude(value: string | undefined): string[] [projects/ic-suite/callidescope/callidescope-cli/src/modules/callidescope/callidescope.command.ts:324]
   ↳ Parses `--exclude`, overriding `exclude`.
  └─> ConfigurationService.parseCommaDelimitedOption(value: string | undefined): string[] [projects/ic-suite/callidescope/callidescope-configuration/src/modules/configuration/configuration.service.ts:97]
     ↳ Splits a comma-separated flag value into its parts.
    └─> InputService.parseCommaDelimitedOption(value: string | undefined): string[] [projects/ic-suite/callidescope/callidescope-configuration/src/modules/input/input.service.ts:102]
       ↳ Splits `--directories`, a comma-separated list of project directories.
      └─> InputService.map(…)(entry: string): string [projects/ic-suite/callidescope/callidescope-configuration/src/modules/input/input.service.ts:107]

10. CallidescopeCommand.parseExcludeCallees — depth 4 · decorated-method

🚀 CallidescopeCommand.parseExcludeCallees(value: string | undefined): string[] [projects/ic-suite/callidescope/callidescope-cli/src/modules/callidescope/callidescope.command.ts:333]
   ↳ Parses `--exclude-callees`, overriding `excludeCallees`.
  └─> ConfigurationService.parseCommaDelimitedOption(value: string | undefined): string[] [projects/ic-suite/callidescope/callidescope-configuration/src/modules/configuration/configuration.service.ts:97]
     ↳ Splits a comma-separated flag value into its parts.
    └─> InputService.parseCommaDelimitedOption(value: string | undefined): string[] [projects/ic-suite/callidescope/callidescope-configuration/src/modules/input/input.service.ts:102]
       ↳ Splits `--directories`, a comma-separated list of project directories.
      └─> InputService.map(…)(entry: string): string [projects/ic-suite/callidescope/callidescope-configuration/src/modules/input/input.service.ts:107]

11. BreadthCommand.parseAddresses — depth 4 · decorated-method

🚀 BreadthCommand.parseAddresses(value: string | undefined): string[] [projects/ic-suite/callidescope/callidescope-cli/src/modules/breadth/breadth.command.ts:194]
   ↳ Parses `--addresses`, a comma-separated list of callable addresses.
  └─> ConfigurationService.parseCommaDelimitedOption(value: string | undefined): string[] [projects/ic-suite/callidescope/callidescope-configuration/src/modules/configuration/configuration.service.ts:97]
     ↳ Splits a comma-separated flag value into its parts.
    └─> InputService.parseCommaDelimitedOption(value: string | undefined): string[] [projects/ic-suite/callidescope/callidescope-configuration/src/modules/input/input.service.ts:102]
       ↳ Splits `--directories`, a comma-separated list of project directories.
      └─> InputService.map(…)(entry: string): string [projects/ic-suite/callidescope/callidescope-configuration/src/modules/input/input.service.ts:107]

12. BreadthCommand.parseConfig — depth ≥ 4 · decorated-method

🚀 BreadthCommand.parseConfig(value: string | undefined): string [projects/ic-suite/callidescope/callidescope-cli/src/modules/breadth/breadth.command.ts:204]
   ↳ Parses `--config`, which every command that loads a configuration requires.
  └─> ConfigurationService.requireConfigurationPath(path: string | undefined, surface: string): string [projects/ic-suite/callidescope/callidescope-configuration/src/modules/configuration/configuration.service.ts:139]
     ↳ Reads the configuration path, refusing an absent or blank one by surface.
    └─> InputService.requireConfigurationPath(path: string | undefined, surface: string): string [projects/ic-suite/callidescope/callidescope-configuration/src/modules/input/input.service.ts:217]
       ↳ Trims the configuration path, refusing an absent or blank one.
      └─> InputService.parseOptionalOption(value: string | undefined): string | undefined [projects/ic-suite/callidescope/callidescope-configuration/src/modules/input/input.service.ts:112]
         ↳ Trims an optional string option, treating blank as absent.

13. BreadthCommand.parseDirectories — depth 4 · decorated-method

🚀 BreadthCommand.parseDirectories(value: string | undefined): string[] [projects/ic-suite/callidescope/callidescope-cli/src/modules/breadth/breadth.command.ts:218]
   ↳ Parses `--directories`, a comma-separated list of project directories.
  └─> ConfigurationService.parseCommaDelimitedOption(value: string | undefined): string[] [projects/ic-suite/callidescope/callidescope-configuration/src/modules/configuration/configuration.service.ts:97]
     ↳ Splits a comma-separated flag value into its parts.
    └─> InputService.parseCommaDelimitedOption(value: string | undefined): string[] [projects/ic-suite/callidescope/callidescope-configuration/src/modules/input/input.service.ts:102]
       ↳ Splits `--directories`, a comma-separated list of project directories.
      └─> InputService.map(…)(entry: string): string [projects/ic-suite/callidescope/callidescope-configuration/src/modules/input/input.service.ts:107]

14. BreadthCommand.parseEntryPointAddresses — depth 4 · decorated-method

🚀 BreadthCommand.parseEntryPointAddresses(value: string | undefined): string[] [projects/ic-suite/callidescope/callidescope-cli/src/modules/breadth/breadth.command.ts:227]
   ↳ Parses `--entry-point-addresses`, overriding `entryPoints.addresses`.
  └─> ConfigurationService.parseCommaDelimitedOption(value: string | undefined): string[] [projects/ic-suite/callidescope/callidescope-configuration/src/modules/configuration/configuration.service.ts:97]
     ↳ Splits a comma-separated flag value into its parts.
    └─> InputService.parseCommaDelimitedOption(value: string | undefined): string[] [projects/ic-suite/callidescope/callidescope-configuration/src/modules/input/input.service.ts:102]
       ↳ Splits `--directories`, a comma-separated list of project directories.
      └─> InputService.map(…)(entry: string): string [projects/ic-suite/callidescope/callidescope-configuration/src/modules/input/input.service.ts:107]

15. BreadthCommand.parseEntryPointDecorators — depth 4 · decorated-method

🚀 BreadthCommand.parseEntryPointDecorators(value: string | undefined): string[] [projects/ic-suite/callidescope/callidescope-cli/src/modules/breadth/breadth.command.ts:236]
   ↳ Parses `--entry-point-decorators`, overriding `entryPoints.decorators`.
  └─> ConfigurationService.parseCommaDelimitedOption(value: string | undefined): string[] [projects/ic-suite/callidescope/callidescope-configuration/src/modules/configuration/configuration.service.ts:97]
     ↳ Splits a comma-separated flag value into its parts.
    └─> InputService.parseCommaDelimitedOption(value: string | undefined): string[] [projects/ic-suite/callidescope/callidescope-configuration/src/modules/input/input.service.ts:102]
       ↳ Splits `--directories`, a comma-separated list of project directories.
      └─> InputService.map(…)(entry: string): string [projects/ic-suite/callidescope/callidescope-configuration/src/modules/input/input.service.ts:107]

16. BreadthCommand.parseExclude — depth 4 · decorated-method

🚀 BreadthCommand.parseExclude(value: string | undefined): string[] [projects/ic-suite/callidescope/callidescope-cli/src/modules/breadth/breadth.command.ts:245]
   ↳ Parses `--exclude`, overriding `exclude`.
  └─> ConfigurationService.parseCommaDelimitedOption(value: string | undefined): string[] [projects/ic-suite/callidescope/callidescope-configuration/src/modules/configuration/configuration.service.ts:97]
     ↳ Splits a comma-separated flag value into its parts.
    └─> InputService.parseCommaDelimitedOption(value: string | undefined): string[] [projects/ic-suite/callidescope/callidescope-configuration/src/modules/input/input.service.ts:102]
       ↳ Splits `--directories`, a comma-separated list of project directories.
      └─> InputService.map(…)(entry: string): string [projects/ic-suite/callidescope/callidescope-configuration/src/modules/input/input.service.ts:107]

17. BreadthCommand.parseExcludeCallees — depth 4 · decorated-method

🚀 BreadthCommand.parseExcludeCallees(value: string | undefined): string[] [projects/ic-suite/callidescope/callidescope-cli/src/modules/breadth/breadth.command.ts:254]
   ↳ Parses `--exclude-callees`, overriding `excludeCallees`.
  └─> ConfigurationService.parseCommaDelimitedOption(value: string | undefined): string[] [projects/ic-suite/callidescope/callidescope-configuration/src/modules/configuration/configuration.service.ts:97]
     ↳ Splits a comma-separated flag value into its parts.
    └─> InputService.parseCommaDelimitedOption(value: string | undefined): string[] [projects/ic-suite/callidescope/callidescope-configuration/src/modules/input/input.service.ts:102]
       ↳ Splits `--directories`, a comma-separated list of project directories.
      └─> InputService.map(…)(entry: string): string [projects/ic-suite/callidescope/callidescope-configuration/src/modules/input/input.service.ts:107]

18. DepthCommand.parseAddresses — depth 4 · decorated-method

🚀 DepthCommand.parseAddresses(value: string | undefined): string[] [projects/ic-suite/callidescope/callidescope-cli/src/modules/depth/depth.command.ts:168]
   ↳ Parses `--addresses`, a comma-separated list of callable addresses.
  └─> ConfigurationService.parseCommaDelimitedOption(value: string | undefined): string[] [projects/ic-suite/callidescope/callidescope-configuration/src/modules/configuration/configuration.service.ts:97]
     ↳ Splits a comma-separated flag value into its parts.
    └─> InputService.parseCommaDelimitedOption(value: string | undefined): string[] [projects/ic-suite/callidescope/callidescope-configuration/src/modules/input/input.service.ts:102]
       ↳ Splits `--directories`, a comma-separated list of project directories.
      └─> InputService.map(…)(entry: string): string [projects/ic-suite/callidescope/callidescope-configuration/src/modules/input/input.service.ts:107]

19. DepthCommand.parseConfig — depth ≥ 4 · decorated-method

🚀 DepthCommand.parseConfig(value: string | undefined): string [projects/ic-suite/callidescope/callidescope-cli/src/modules/depth/depth.command.ts:178]
   ↳ Parses `--config`, which every command that loads a configuration requires.
  └─> ConfigurationService.requireConfigurationPath(path: string | undefined, surface: string): string [projects/ic-suite/callidescope/callidescope-configuration/src/modules/configuration/configuration.service.ts:139]
     ↳ Reads the configuration path, refusing an absent or blank one by surface.
    └─> InputService.requireConfigurationPath(path: string | undefined, surface: string): string [projects/ic-suite/callidescope/callidescope-configuration/src/modules/input/input.service.ts:217]
       ↳ Trims the configuration path, refusing an absent or blank one.
      └─> InputService.parseOptionalOption(value: string | undefined): string | undefined [projects/ic-suite/callidescope/callidescope-configuration/src/modules/input/input.service.ts:112]
         ↳ Trims an optional string option, treating blank as absent.

20. DepthCommand.parseDirectories — depth 4 · decorated-method

🚀 DepthCommand.parseDirectories(value: string | undefined): string[] [projects/ic-suite/callidescope/callidescope-cli/src/modules/depth/depth.command.ts:192]
   ↳ Parses `--directories`, a comma-separated list of project directories.
  └─> ConfigurationService.parseCommaDelimitedOption(value: string | undefined): string[] [projects/ic-suite/callidescope/callidescope-configuration/src/modules/configuration/configuration.service.ts:97]
     ↳ Splits a comma-separated flag value into its parts.
    └─> InputService.parseCommaDelimitedOption(value: string | undefined): string[] [projects/ic-suite/callidescope/callidescope-configuration/src/modules/input/input.service.ts:102]
       ↳ Splits `--directories`, a comma-separated list of project directories.
      └─> InputService.map(…)(entry: string): string [projects/ic-suite/callidescope/callidescope-configuration/src/modules/input/input.service.ts:107]

21. DepthCommand.parseEntryPointAddresses — depth 4 · decorated-method

🚀 DepthCommand.parseEntryPointAddresses(value: string | undefined): string[] [projects/ic-suite/callidescope/callidescope-cli/src/modules/depth/depth.command.ts:201]
   ↳ Parses `--entry-point-addresses`, overriding `entryPoints.addresses`.
  └─> ConfigurationService.parseCommaDelimitedOption(value: string | undefined): string[] [projects/ic-suite/callidescope/callidescope-configuration/src/modules/configuration/configuration.service.ts:97]
     ↳ Splits a comma-separated flag value into its parts.
    └─> InputService.parseCommaDelimitedOption(value: string