Show HN: Calldiff – a CLI to see callstack diffs in all major languages

tanishqkanc1 pts0 comments

GitHub - tanishqkancharla/calldiff: Diffs for function call stacks across git commits. 22 languages supported (AST-based, built using Tree-sitter). Great for agentic review. · GitHub

/" data-turbo-transient="true" />

Skip to content

Type / to search

Sign in<br>Sign upAppearance settings

You signed in with another tab or window. Reload to refresh your session.<br>You signed out in another tab or window. Reload to refresh your session.<br>You switched accounts on another tab or window. Reload to refresh your session.

Dismiss alert

{{ message }}

tanishqkancharla

calldiff

Public

Notifications<br>You must be signed in to change notification settings

Fork<br>13

Star<br>284

main

BranchesTags

Go to file

CodeOpen more actions menu

Folders and files<br>NameNameLast commit message<br>Last commit date<br>Latest commit

History<br>19 Commits<br>19 Commits

examples

examples

src

src

test

test

.gitignore

.gitignore

AGENTS.md

AGENTS.md

LICENSE

LICENSE

README.md

README.md

package-lock.json

package-lock.json

package.json

package.json

tsconfig.json

tsconfig.json

vitest.config.ts

vitest.config.ts

View all files

Repository files navigation

calldiff

Diff call stacks across git commits — like git diff, but for who-calls-whom.

Built for agentic code review : when an agent (or you) rewires call flow, plain line diffs bury the shape of the change. calldiff shows which callees appeared, disappeared, or moved under an entrypoint — across 22 languages .

PiService.createAgentSession(options)<br>- ├─ AuthStorage.create()<br>- ├─ new ModelRegistry<br>- ├─ createCodingTools()<br>+ ├─ PiService.getServices()<br>+ │ ├─ SettingsManager.create()<br>+ │ ├─ AuthStorage.create()<br>+ │ └─ new ModelRegistry

Prompt for agents

Paste this when you want a walkthrough of call-flow changes:

dearest clod, walk me through the code changes you made using npx calldiff@latest

Install

npx calldiff@latest<br># or<br>npm install -g calldiff

Usage

# HEAD vs working tree<br>calldiff diff

# one ref vs working tree<br>calldiff diff main

# two commits / branches<br>calldiff diff abc123 def456<br>calldiff diff --from main --to feature

# force entrypoints (functionName or ClassName.method)<br>calldiff diff main feature --entry createAgentSession<br>calldiff diff main feature -e PiService.createAgentSession -e boot

# limit to paths (trailing positionals; leading -- also accepted)<br>calldiff diff main feature src/lib

# view a call tree (no diff) — requires --entry<br>calldiff tree -e createAgentSession<br>calldiff tree HEAD -e PiService.createAgentSession<br>calldiff tree main -e boot --max-depth 8 src/lib<br>calldiff tree -e runCheckout --locs examples/checkout

# find all call paths from one symbol to another — requires --entry and --to<br>calldiff reach -e runCheckout --to sendEmail<br>calldiff reach HEAD -e runCheckout --to sendEmail examples/checkout

# agent / machine-readable output (via incur)<br>calldiff diff --format json<br>calldiff --llms<br>calldiff skills add # install agent skill files<br>calldiff mcp add # register as MCP server

diff semantics (git-diff shaped)

Invocation<br>From<br>To

calldiff diff<br>HEAD<br>working tree

calldiff diff

working tree

calldiff diff

- lines were present in from and gone in to .

+ lines are new in to .

If you omit --entry, calldiff infers exported functions whose expanded call trees changed (and may show several).

tree

Invocation<br>Tree from

calldiff tree -e<br>working tree

calldiff tree -e<br>that commit/ref

Prints a plain ASCII call tree (no +/− markers). --entry / -e is required.<br>With --locs, each node shows a source location: the root uses the definition<br>file:line, and children use the call site in the parent (file:line or<br>file:line-line) — same idea as LSP Call Hierarchy fromRanges, not Go to<br>Definition.

reach

Invocation<br>Paths from

calldiff reach -e --to<br>working tree

calldiff reach -e --to<br>that commit/ref

Prints every call path from the entrypoint to the target (including alternate if / else arms). Both --entry / -e and --to are required.

Labels

functionName — free function

ClassName.method — class method

new ClassName — constructor / new call

Component — JSX/TSX component tags (); children nest under the parent

if (cond) / else / else if (cond) — conditional arms (no continuing │ rail)

file:line — call-site (or root definition) location; enable with --locs (default off)

Supported languages

TypeScript, TSX, JavaScript, JSX, Python, Go, Rust, Java, Ruby, C, C++, C#, PHP, Kotlin, Swift, Scala, Lua, Elixir, Bash, Haskell, Zig, Solidity, OCaml.

Output

Default: colored ASCII callstack trees (TTY) / colorless ASCII when piped — same shape as before.

--format json|yaml|md|jsonl: structured result (from/to/trees or paths with nested nodes + per-entry ascii) for agents and scripts.

Built on incur: skills add, mcp add, --llms, CTAs after diffs, typed flags.

How it works

Reads source from both git trees (git show / working tree)

Detects language by file extension, loads a tree-sitter grammar (bundled or on-demand into ~/.cache/calldiff/grammars), and parses

Builds...

calldiff tree diff call from json

Related Articles