---
title: Use issue-graph as a library
description: Use issue-graph in your application to crawl issue graphs, collect PR status, and query dashboard models over HTTP or GitHub CLI.
url: "https://issue-graph.dev/docs/library"
docs_index: /llms.txt
lastUpdated: "2026-11-07T00:00:00.000Z"
navTitle: "Library"
canonical_url: "https://issue-graph.dev/docs/library"
---

# Use issue-graph as a library

> For an index of all documentation, see [/llms.txt](/llms.txt).

## Install

```bash
npm install issue-graph@latest
```

The package ships ESM imports with TypeScript declarations and declares Node.js 20 or later. The core imports no Node built-in modules. Collectors make requests through the transport you pass them.

| Import                         | Runtime requirements                                      |
| ------------------------------ | --------------------------------------------------------- |
| `issue-graph`                  | Core collectors, classification, ranking, and rendering   |
| `issue-graph/transport/shell`  | Node-compatible child processes and an authenticated `gh` |
| `issue-graph/transport/http`   | `fetch` and a GitHub token                                |
| `issue-graph/transport/linear` | `fetch` and a Linear API key or OAuth access token        |

The core exports exclude CLI snapshot storage and the headless agent runner. Your application handles persistence and agent execution. For the Linear reader, see [Linear](/docs/linear).

## Crawl with HTTP

Store the token in your server's environment:

```ts
import {
  classify,
  crawl,
  fileOverlaps,
  fillMentionedBy,
  makeFetchNode,
  prioritize,
} from "issue-graph";
import { httpTransport } from "issue-graph/transport/http";

const token = process.env.GITHUB_TOKEN;
if (!token) throw new Error("GITHUB_TOKEN is required");

const repo = { owner: "vercel-labs", repo: "portless" };
const transport = httpTransport({ token });
const { nodes, cappedOut } = await crawl(
  [{ ...repo, number: 427 }],
  {
    maxDepth: 1,
    maxNodes: 80,
    hubThreshold: 12,
    concurrency: 4,
    primaryRepo: repo,
  },
  makeFetchNode(transport),
);

classify(nodes);
fillMentionedBy(nodes);
const priorities = prioritize(nodes, new Date());
const overlaps = fileOverlaps(nodes);
const unfetched = [...nodes.values()].filter((node) => !node.fetched);
console.log({ priorities, overlaps, cappedOut: [...cappedOut], unfetched });
```

Report `cappedOut` and unfetched nodes with your results. The [graph crawl limits](/docs/graph) apply to library calls as well.

`httpTransport` sends requests to `api.github.com`. Its `token` option accepts a string or a function that returns the token, synchronously or as a promise. The transport retries a bounded number of times and accepts these options:

| Option         | Default                             |
| -------------- | ----------------------------------- |
| `timeoutMs`    | 20000                               |
| `maxRetries`   | 3 retries after the initial attempt |
| `concurrency`  | 5 transport requests                |
| `maxBackoffMs` | 60000                               |
| `userAgent`    | `issue-graph`                       |

Transport concurrency is separate from crawl concurrency. When retries run out, the transport throws, so handle the error and report the incomplete nodes.

## Use GitHub CLI authentication

In a Node-compatible process, replace the HTTP transport with the shell transport:

```ts
import { shellTransport } from "issue-graph/transport/shell";

const transport = shellTransport();
```

Pass this transport to `makeFetchNode`. It runs `gh api` with the process's GitHub CLI credentials, so the runtime must support child processes. `gh` handles authentication and retries.

## Collect PR status

Use either transport:

```ts
import { collectStatus } from "issue-graph";

const report = await collectStatus(transport, {
  repos: ["vercel-labs/portless"],
  authors: ["ctate", "Railly"],
  concurrency: 4,
  maxPages: 100,
});

console.log({ coverageComplete: report.coverageComplete, totals: report.totals });
```

Keep `coverageComplete` and the per-repository coverage next to the counts. A `null` count means unknown. For the report fields and comparison behavior, see [Status and history](/docs/status).

## Query a dashboard model

The core exports the same filters and scoring that the CLI and dashboard use. Continuing from the crawl example, build a normalized model and rank the matching open issues:

```ts
import { dashboardModel, dashboardQuery } from "issue-graph";

const model = dashboardModel(
  nodes,
  ["vercel-labs/portless#427"],
  "vercel-labs/portless",
  undefined,
  cappedOut.size,
);
const query = dashboardQuery.normalize(model, {
  view: "rank",
  filters: { state: "open", kind: "Issue", heatMode: "min", heatMin: 50 },
  weights: { comments: 4 },
});
const result = dashboardQuery.evaluate(model, query);

console.log({
  matches: [...result.matches],
  ranking: result.ranking.map(({ n, score }) => ({ key: n.key, score })),
});
```

`normalize` fills in defaults and rejects unsupported filters or views. `evaluate` returns the matching keys and the ranked open items. Neither function touches the network or the filesystem. Library calls use the built-in weights unless you pass overrides, and they do not read the CLI configuration.

The package also exports the types `Model`, `ProviderDescriptor`, `DashboardFilters`, `DashboardQuery`, `Weights`, and `HeatSignals`. `scoring` exposes the versioned Heat formula, and `createGraphMetrics(model)` computes the observed relationships that Impact and Swarm use. Live collectors exist for GitHub and Linear. Other providers must supply a normalized model and its capabilities.

The CLI writes static Next.js exports. The library's `renderHtml` and `renderDashboard` return HTML strings from the shared renderer and do not write Next.js assets or query history.

## Protect credentials and results

Keep GitHub tokens on the server, and check that a user has access before showing them results from private repositories. See [Permissions and privacy](/docs/security).

---

For a semantic overview of all documentation, see [/sitemap.md](/sitemap.md)

For an index of all available documentation, see [/llms.txt](/llms.txt)