Skip to content

Installation

cants ships as a self-contained native binary — no Bun or Node at runtime. Install the prebuilt binary for your platform with pip, Homebrew, or a one-line shell script; building from source is only needed if you’re hacking on the analyzer itself.

The wheel bundles the prebuilt binary for your platform:

Terminal window
pip install codeanalyzer-typescript
cants --help

This is also the package CLDK’s Python SDK depends on to locate the backend — it exposes codeanalyzer_typescript.bin_path() and schema_path() so the SDK can find the binary and the schema contract.

Running a prebuilt cants requires nothing else — the runtime is bundled, and the Jelly flow analyzer and the neo4j-driver used for the live Bolt push are already inside the binary. There is no extra pip or npm step to push to a graph.

cants --emit neo4j projects the analysis into a Neo4j property graph — either a self-contained graph.cypher snapshot, or an incremental push to a live database over Bolt. The snapshot path needs no running database; the live push needs a Neo4j you can reach over Bolt.

A throwaway single instance for local work:

Terminal window
docker run --rm \
-p 7474:7474 -p 7687:7687 \
-e NEO4J_AUTH=neo4j/secret \
neo4j:5

Bolt is on 7687; the browser UI is on 7474.

cants reads the standard Neo4j connection variables when the matching flag is omitted (an explicit flag wins). Prefer the password env var — a flag value is visible in shell history and the process list.

Terminal window
export NEO4J_URI=bolt://localhost:7687
export NEO4J_USERNAME=neo4j
export NEO4J_PASSWORD=secret
export NEO4J_DATABASE=neo4j # optional; omit to use the server's default DB
cants --input ./my-ts-project --emit neo4j --app-name my-ts-app

The --app-name you push with is the scoping key for the whole graph: it names the :TSApplication anchor node, the snapshot’s scoped wipe matches on it, and the CLDK Python SDK reads the graph back by it. Keep it stable across runs of the same application — and identical to the application_name the SDK reads with. See the Neo4j output guide for the full producer/consumer story.

The producer and consumer scale separately. Run cants --emit neo4j out of band — as a CI step or a Kubernetes Job/CronJob that pushes app-scoped subgraphs into a shared, managed Neo4j over Bolt. Consumers (agents, the CLDK SDK, dashboards) are lightweight read-only clients that fan out from the same cluster and never run the analyzer.

In that setup, supply the connection details as environment variables — from a Secret for the credentials — so nothing lands in shell history or the pod spec:

Terminal window
export NEO4J_URI="$NEO4J_URI" # from the cluster
export NEO4J_USERNAME="$NEO4J_USERNAME"
export NEO4J_PASSWORD="$NEO4J_PASSWORD" # from a Secret
cants --input "$WORKSPACE" --emit neo4j --app-name "$APP_NAME"

Each application is anchored at its own :TSApplication node, so many analyzer jobs can write into one database without clobbering each other. The graph is stamped with a schema_version (currently 2.0.0) on that node; publish the matching contract with cants --emit schema (it needs no project) so consumers can detect a producer/consumer mismatch before they query.

To resolve types and call targets the way the project actually compiles, cants drives the TypeScript compiler against the project’s own node_modules. By default it materializes those dependencies before parsing — running the project’s package manager with --ignore-scripts so packages’ .d.ts/JS files are present without compiling native addons.

This means the project you analyze should be a normal Node/TypeScript project (a package.json, ideally a tsconfig.json). A few notes:

  • node_modules is installed in place. Node’s module resolution requires it to live in the project tree, so the analyzer installs there and reuses it on later runs.
  • Already have node_modules? Pass --no-build to skip materialization and reuse the prepared tree.
  • --eager reinstalls dependencies from scratch, mirroring a clean rebuild.
  • It degrades, never crashes. If the install fails (offline, a broken dependency), the analyzer logs a warning and continues with partial type information rather than aborting.

Level-2 analysis (--analysis-level 2) is designed to enrich the call graph with CodeQL. You do not need to install anything for it today — it is an experimental, not-yet-implemented stub that currently falls back to the level-1 graph.

You only need this to develop the analyzer; for normal use, prefer a prebuilt install above. Building from source requires Bun 1.0+.

Terminal window
curl -fsSL https://bun.sh/install | bash

Clone, install dependencies, and compile the binary:

Terminal window
git clone https://github.com/codellm-devkit/codeanalyzer-typescript
cd codeanalyzer-typescript
bun install
bun run build # -> dist/cants (standalone native binary)
./dist/cants --help

bun run build produces a standalone native binary at dist/cants — it bundles the runtime, so neither Bun nor Node is needed to run it afterward.

You can also run the analyzer straight from source without compiling:

Terminal window
bun install
bun run start -- --input /path/to/typescript/project # run from source
bun run typecheck # tsc --noEmit

The -- separates Bun’s arguments from the analyzer’s.