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

@smi0001/agent-pintu

v0.4.2

Published

PR call-graph tracer for TypeScript and JavaScript projects — multi-root, extracts a call graph from a PR diff (route-rooted or function-touched mode) and emits Mermaid + YAML artifacts via ts-morph static analysis.

Downloads

954

Readme

@smi0001/agent-pintu

PR call-graph tracer for TypeScript and JavaScript projects. Walks the AST from each route handler (or every touched function) in a PR diff and emits two artifacts you can paste into a PR or hand to another AI:

  • PR-<n>-<slug>.md — human view: title, combined Mermaid flowchart, per-route Mermaid + table, "touched code not reached" section. Renders as a picture in GitHub / Gitea.
  • PR-<n>-<slug>.yaml — machine view: structured entry_routes, nodes, edges, timestamps. An AI/tool can read this and skip re-walking the codebase.

Static analysis only — no LLM calls, no token cost per run.

Install

# On demand
npx @smi0001/agent-pintu init
npx @smi0001/agent-pintu trace --pr-num 42 --title "Order Checkout"

# Or globally
npm i -g @smi0001/agent-pintu
agent-pintu --help

Requires Node 18+. Works on any git repo whose server code is TypeScript and whose tsconfig.json compiles cleanly.

Configure

Drop a .pintu.json in your project root:

cd /path/to/your/project
agent-pintu init
{
  "project": ".",
  "serverRoot": "server",
  "tsConfig": "server/tsconfig.json",
  "appEntry": "server/app.ts",
  "baseBranch": "main",
  "outDir": "documents/pr-traces",
  "routesPattern": "/routes/",
  "depth": 4
}

Paths in .pintu.json resolve relative to the config file's location, so agent-pintu works no matter what dir you run it from. CLI flags (e.g. --pr-num, --title, --base) override config.

Config search walks up from the current dir to find .pintu.json — handy if you cd into a subdirectory of your project.

Usage

# Trace the current branch vs base (auto-detects main / master / develop)
agent-pintu trace --pr-num 42 --title "Order Checkout"

# Override base branch
agent-pintu trace --base develop --pr-num 42

# Run against a different repo
agent-pintu trace --project /path/to/other/repo --pr-num 7

# All flags
agent-pintu --help

What it does

  1. git diff --name-only <base>...HEAD → changed files.
  2. Loads the TS project with ts-morph (<server-root>/tsconfig.json).
  3. In every changed .ts route file (routesPattern substring match), finds:
    • router.route(path).METHOD(handler) chains
    • router.METHOD(path, handler) calls
  4. Walks the AST from each handler. For each CallExpression, resolves the called symbol's declaration via the TS type checker. Recurses up to depth hops; cycle-safe via a visited set.
  5. For each in-project symbol: marks touched_in_pr if any line of its body intersects a diff hunk; runs git log -L <start>,<end>:<file> to extract created / updated ISO timestamps.
  6. External calls (node_modules) become terminal nodes. Stdlib noise (res.json, console.log, Array.push, JSON.stringify, etc.) is filtered.
  7. Detects the mount-prefix chain from app.ts (app.use("/_", api) + api.use("/orders", router)/_/orders).

Mermaid color coding:

  • 🟧 Orange — function touched by this PR
  • 🟦 Blue — route entry handler
  • ⬜ Gray — external / library symbol

Output schema (.yaml)

pr: "42"
title: "Order Checkout"
base_branch: "develop"
generated_at: "2026-06-03T..."
tool: agent-pintu v0.1.0
entry_routes:
  - method: POST
    path: "/_/orders/start"
    handler: "createOrder"
    route_file: "server/src/routes/ordersRoutes.ts"
nodes:
  - id: "server/src/controllers/ordersController.ts:createOrder"
    name: createOrder
    file: server/src/controllers/ordersController.ts
    line: 95
    end_line: 232
    kind: entry          # entry | internal | external
    touched_in_pr: true
    routes: ["POST /_/orders/start"]
    created: "2026-05-17T04:11:07+05:30"
    updated: "2026-05-17T04:11:07+05:30"
edges:
  - from: "server/src/controllers/ordersController.ts:createOrder"
    to:   "server/src/controllers/ordersController.ts:requireAuth"
    call_sites:
      - line: 138
        args: ["payload"]

Trace modes (v0.3)

Pintu now has two ways to pick entry points, selected via --mode or the interactive prompt:

| Mode | Entry points | Best for | |---|---|---| | routes (default for backend servers) | Express route handlers detected in changed route files (router.METHOD(path, handler) or router.route(path).METHOD(handler)) | Backend Node services with HTTP routes — answers "what URLs does this PR affect?" | | touched | Every top-level function (and class method) whose body intersects a diff hunk | Any project — frontend SPAs, libraries, CLIs, monorepos without classic Express routes. Answers "what functions changed and what do they call?" |

If you don't pass --mode and don't set mode in .pintu.json, pintu prompts you interactively (or defaults to routes in non-TTY contexts like CI).

agent-pintu trace --pr-num 42 --title "..."             # interactive prompt
agent-pintu trace --mode touched --pr-num 42 --title "..."  # explicit, no prompt

For CI, pin the mode in .pintu.json or pass --mode so the run is deterministic.

Scope (v0.3)

  • TypeScript and JavaScript. Walks .ts, .tsx, .js, .jsx, .mjs, .cjs. When tsConfig is set (or tsconfig.json exists at the server or project root), pintu loads the project from it. For JS-only projects without a tsconfig, pintu synthesizes a ts-morph project with allowJs: true — just leave tsConfig unset in your .pintu.json (or omit the file).
  • Route handlers — named or inline (routes mode). Both router.get(path, namedHandler) and router.get(path, async (req, res) => {...}) are detected. Inline handlers get a synthetic label like <inline get@L22>.
  • Top-level functions and class methods (touched mode). Nested arrow functions inside other functions are skipped to avoid graph explosion.
  • Doesn't trace client-side framework routers (Durandal, Knockout, React Router, Vue Router, etc.) in routes mode. For frontend projects, use --mode touched.
  • Arg labels are source text, not type-resolved. Edge labels show the literal source of the first 1–2 arguments (capped at 60 chars).
  • Mount-prefix detection (routes mode) uses suffix-matching on import paths in appEntry and only resolves plain string / no-substitution template prefixes. Template literals with variable substitutions (e.g. app.use(\${BASE_PATH}/api`, router)`) currently fall back to router-local paths — roadmap item.

Composition with agent-binod

The pair works well together: run agent-pintu first to generate the .yaml, then point agent-binod at the PR — binod's review gets free structural context ("you touched 6 routes; here's the call graph") at near-zero token cost vs re-parsing the source.

Multi-root projects (v0.4)

When a PR spans multiple source trees — server + client + admin dashboard, or multiple bundles in a monorepo — declare each root in .pintu.json:

{
  "project": ".",
  "roots": [
    {
      "label": "server",
      "serverRoot": "server",
      "tsConfig": "server/tsconfig.json",
      "appEntry": "server/app.ts",
      "mode": "routes"
    },
    {
      "label": "client",
      "serverRoot": "client/src",
      "tsConfig": "client/tsconfig.json",
      "mode": "touched"
    },
    {
      "label": "admin",
      "serverRoot": "adminDashboard/src",
      "tsConfig": "adminDashboard/tsconfig.json",
      "mode": "touched"
    }
  ],
  "baseBranch": "upstream/main",
  "outDir": "documents/pr-traces",
  "depth": 4
}

Each root is processed independently — its own tsconfig, mode, and entry-point detector — and the resulting call graphs are merged into a single Mermaid diagram and YAML file. Each node in the YAML carries a roots array tagging which root(s) walked into it, so a shared utility called by both client and admin shows roots: ["client", "admin"].

Per-root fields:

| Field | Required | Notes | |---|---|---| | label | optional | Display name (defaults to basename of serverRoot) | | serverRoot | yes | Directory to walk, relative to config file's location | | tsConfig | optional | If omitted, pintu auto-finds <serverRoot>/tsconfig.json then synthesizes | | appEntry | optional | For routes mode mount-prefix detection | | mode | optional | routes or touched. Falls back to top-level mode, then interactive prompt | | routesPattern | optional | Defaults to /routes/ |

Single-root configs continue to work unchanged — the top-level serverRoot/tsConfig/appEntry/mode/routesPattern fields are still supported, and the output shape for single-root is identical to v0.3.

Cross-root edges aren't drawn. If a function in client/src imports from adminDashboard/src, each root only resolves symbols within its own tsconfig. The call to the cross-root function will appear as a terminal external node in the calling root. Single-tsconfig projects don't have this problem — both halves are loaded into the same ts-morph project there.

Base-branch freshness check

When baseBranch is a remote-tracking ref (e.g. upstream/release), pintu does a one-off git ls-remote to compare your local ref with the remote tip before running. If they differ, you get a warning:

[pintu] ⚠  Your local "upstream/release" is OUT OF SYNC with upstream.
[pintu]    local:  1d559d441475
[pintu]    remote: d6fc897d2df4
[pintu]    Run `git fetch upstream release` to refresh, then re-run.
[pintu]    (Pass --skip-base-check to suppress this warning.)

It's non-blocking — pintu proceeds with the local copy. Skipped automatically when:

  • baseBranch is a local branch or SHA (no remote-tracking concept)
  • You pass --skip-base-check (useful for offline / air-gapped / CI runs with pre-fetched refs)
  • The remote is unreachable (a soft notice is printed, run continues)

Known limitations / roadmap

  • Mount prefixes from template literals with variables — e.g. app.use(\${BASE_PATH}/api`, router)` currently falls back to router-local paths.
  • Cross-stack stitching — pick up axios.get('/_/...') / fetch('/_/...') calls in client code and join them into matching route entries.
  • Test coverage overlay — flag which nodes have a corresponding *.test.{ts,js} reference.
  • <out-dir>/index.yaml — one-line entry per generated trace so future agents can grep instead of scanning the directory.
  • Type-resolved arg labels.
  • Pre-push hook / Gitea webhook — auto-generate on PR open, commit the pair back to the branch.
  • Python / Go / other languages — different ASTs entirely; would ship as sibling packages with a shared core, only when there's a concrete project to point each one at.

License

MIT © Shammi Hans