Version 0.12.7 was built on Thursday, May 28, 2026 at GMT-07:00
1779997868547from hash58e5792.
issue_tree scans one or more GitHub repositories into a local JSON cache, builds a
dependency graph from blocking-issue links, and renders that graph in a variety of
formats — including a self-contained interactive HTML page.
issue_tree exposes four subcommands that implement a scan → graph → render pipeline:
issue_tree scan [--repo <owner/repo>] ... [--cache <file>] [--token <token>] [--wait] [--force]
issue_tree render [--cache <file>] [--format <fmt>] [--out <file>] [--scope <scope>]
[--repo <owner/repo>] ... [--label <label>] ... [--milestone <title>]
[--state <state>] [--child-of-gates] [--edge-style <style>] [--rankdir <dir>]
issue_tree all (accepts all scan and render flags)
issue_tree auto [<path>] (accepts all scan and render flags)
scanFetches issues from one or more GitHub repositories (and any external repositories they block against) and writes a resumable JSON cache. Key flags:
--repo <owner/repo> — one or more target repositories (repeatable).--cache <file> — cache file path (default issue-tree-cache.json).--token <token> — GitHub personal access token; falls back to GITHUB_TOKEN env var,
then gh auth token.--wait — on hitting the rate limit, sleep until it resets and continue.--force — discard any existing cache before scanning.--quiet / -q — suppress per-page and per-issue progress output. By default scan
writes a progress line to stderr for every phase boundary, every page of issues fetched,
every external blocker resolved, and every issue whose comments were fetched. Use
--quiet to silence all of that (the final scan complete line on stdout still prints).renderReads an existing cache and renders the dependency graph. A missing cache is a hard
error — render never scans. Key flags:
--cache <file> — cache to read (default issue-tree-cache.json).--format <fmt> — one of json, dot, svg, png, jpeg, html (default: inferred
from --out extension, otherwise svg).--out <file> — write output to a file. For text formats (json/dot/svg/html),
omitting --out writes to stdout. For binary formats (png/jpeg), omitting --out
writes to a generated default file in the current directory named
owner_repo_YYYY-MM-DD_HH-MM-SS.ext, with _and_N_more appended after the first
repo when multiple repos contribute to the render.--scope <scope> — open (default), connected, or all.--label <label> — keep only issues with this label (repeatable; OR semantics across multiple uses).--milestone <title> — keep only issues in this milestone.--state <state> — open, closed, or all (default all).--edge-style <style> — color (default) or color+shape.--rankdir <dir> — lr (default, left-to-right) or tb (top-to-bottom).--engine <name> — Graphviz layout engine: dot (default, hierarchical),
neato/fdp/sfdp (force-directed, more compact for sparse graphs),
twopi (radial), or circo (circular).--splines <kind> — edge routing: curved (default), line (straight),
ortho (right-angle), polyline, spline, or none.--nodesep <inches> — gap between nodes in the same rank. Graphviz default 0.25.--ranksep <inches> — gap between ranks. Graphviz default 0.5. Halving these
roughly halves the corresponding axis of the output.--concentrate — merge parallel edges with shared endpoints.--packmode <mode> — how disconnected components (multiple small trees) are
arranged in 2D. node (default, shelf packing — fills space tightly without
a grid), array (row-major grid), clust (cluster-by-cluster), graph
(whole-graph cell), or off (Graphviz default — stack along the
rank-orthogonal axis). For repos with many independent issue chains, node
produces a far more readable view than the vertically-stacked default.--graph-pad <inches> — outer pad around the whole graph (~0.055 default).--graph-margin <inches> — outer page margin (0.5 default).--node-margin <x[,y]> — per-node text padding inside boxes (Graphviz default 0.11,0.055).--edge-minlen <N> — minimum rank distance per edge (default 1).--issue-shape <shape> and --pr-shape <shape> — Graphviz shape names for
issue and PR nodes (defaults box and ellipse). Accepts box, polygon,
ellipse, oval, circle, egg, triangle, diamond, trapezium,
parallelogram, house, pentagon, hexagon, septagon, octagon,
doublecircle, doubleoctagon, tripleoctagon, Mdiamond, Msquare,
Mcircle, square, star, note, tab, folder, box3d, component,
cylinder, and rectangle.--raster-max <px> — for png/jpeg output only, the maximum pixel dimension
on the longer axis. When the laid-out SVG would exceed this on either axis, the
output is downscaled by a single zoom factor that brings the longer axis down to
this value, preserving aspect ratio. Defaults to 8192. A one-line notice is
written to stderr when capping fires. This guard exists because long
dependency chains can produce SVGs with extreme aspect ratios — a 1597-node
graph routinely lays out at ~3800 × 81600 px, whose 1:1 RGBA buffer alone is
1.2 GB before any JPEG encoding overhead.allRuns scan then render in a single invocation, accepting every flag from both.
autoWalks a project tree, extracts the GitHub repository URL from every project
manifest it finds, forwards the deduped list to scan, and then renders the
resulting cache — auto is a scan + render combo, exactly like all, except
the repo list comes from filesystem discovery instead of --repo flags.
Useful when you are sitting inside a polyglot monorepo or a directory
containing several checked-out projects and want a one-shot graph across all
of them.
issue_tree auto # discovers + scans + writes auto-named PNG to cwd
issue_tree auto ./projects --cache big.json # rooted at ./projects, custom cache
issue_tree auto --format svg --out g.svg # render to SVG instead of PNG
The positional argument is the directory to scan (defaults to .). Every
scan flag (--cache, --token, --wait, --force, --quiet) and every
render flag (--format, --out, --scope, --label, --milestone,
--state, --child-of-gates, --edge-style, --rankdir, --engine,
--splines, --nodesep, --ranksep, --concentrate, --packmode,
--graph-pad, --graph-margin, --node-margin, --edge-minlen,
--issue-shape, --pr-shape, --raster-max) is accepted and forwarded.
The auto default --format is png (unlike render/all, which
default to svg). The reasoning: the natural endpoint of issue_tree auto
is a finished image, so binary is the best implicit choice. Override with
--format <fmt> or by passing --out with a recognized extension (e.g.
--out g.svg picks svg).
Detection logs go to stdout (one detected: <file> → <owner/name> line per
repo) and are suppressed by --quiet. If no repos are found, auto exits
with code 2 and a no GitHub repos detected message and skips render. If
the scan rate-limits, auto still proceeds to render against the partial
cache (matching all's semantics).
Recognized manifest formats (one repo extracted per match; non-GitHub URLs are skipped):
| Ecosystem | File pattern | Field |
|---|---|---|
| Node/JS/TS | package.json |
.repository or .repository.url |
| Rust | Cargo.toml |
[package].repository |
| Python | pyproject.toml |
[project.urls].{Repository,Source,Homepage} or [tool.poetry].repository |
| Go | go.mod |
module github.com/... line |
| PHP | composer.json |
.support.source or .homepage |
| Dart | pubspec.yaml |
repository: or homepage: |
| Java/Maven | pom.xml |
<scm><url> or <scm><connection> |
| .NET | *.csproj / *.fsproj / *.vbproj |
<RepositoryUrl> |
| Crystal | shard.yml |
repository: or url: |
| Haskell | *.cabal |
source-repository.location: or homepage: |
| OCaml | dune-project |
(source (github ...)) or (source (uri ...)) |
| Ruby | *.gemspec |
spec.metadata['source_code_uri'] or spec.homepage |
| ObjC/Swift | *.podspec |
s.source = { :git => ... } or s.homepage |
| R | DESCRIPTION |
URL: (first GitHub entry wins) |
| Lua | *.rockspec |
source = { url = ... } or homepage |
| Elixir | mix.exs |
links: %{"GitHub" => "..."} |
| Perl | META.json / META.yml |
.resources.repository.{url,web} |
| Julia | Project.toml |
repo = "..." |
| PowerShell | *.psd1 |
ProjectUri = '...' |
| Zig | build.zig.zon |
.repository = "..." |
| D | dub.json / dub.sdl |
.sourceRepository |
| Clojure | project.clj |
:url "..." inside defproject |
| Common Lisp | *.asd |
:source-control or :homepage |
| Fortran | fpm.toml |
[package].repository |
Excluded directories. The traversal never descends into directories that
hold vendored dependencies or build artifacts: node_modules, vendor,
target, dist, build, out, bin, obj, __pycache__, .venv,
venv, env, .tox, .pytest_cache, Pods, .gradle, .idea, .vs,
.vscode, .next, .nuxt, coverage, coverage-* (e.g.
coverage-typedoc), htmlcov, .git, .svn, .hg, bower_components,
elm-stuff, deps, _build, .stack-work, .cargo, .pub-cache. This
prevents thousands of unrelated package.json files (or similar) from a
dependency tree from polluting the scan list.
| Format | Description |
|---|---|
json |
The filtered graph as JSON (nodes + edges + metadata). |
dot |
Graphviz DOT source. |
svg |
SVG rendered via viz.js. |
png |
PNG rasterized from the SVG via resvg. |
jpeg |
JPEG rasterized from the SVG via resvg. |
html |
Self-contained interactive HTML — includes the viz.js layout engine and the browser client bundle, so the file works offline with no server. |
The html format is particularly useful for sharing: the output file opens in any
browser and renders the graph interactively without a server or network access.
render never scansrender reads a pre-built cache. If the cache file does not exist, the command exits
with an error. Run scan (or all) first to produce the cache.
| Count | Statement | Branch | Func | Line | |
|---|---|---|---|---|---|
| Unit | 422 | 96.5% | {{unitbranch}}% | {{unitfunc}}% | {{unitline}}% |
| Stochastic | 18 | 96.5% | {{stochbranch}}% | {{stochfunc}}% | {{stochline}}% |
| Docblock count | 100% | |
|---|---|---|
| Docblock coverage | 262 | 100% |
![]() |
![]() |
![]() |
![]() |
master//docsTODO_TOKEN_FOR_GH_CI_CD after renaming it in ci.ymlissue_trees in this file's top block linksissue_trees in package.jsonissue_tree in verify_version_bump.jspackage.jsonsrc/html/index.htmlbase-README.mdissue_trees in rollup.config.jsbin block to package.json, orbin config from rollup.config.jsnpm install && npm run build
issue_tree's scanner fetches the issues of one or more GitHub repositories — and the
external issues that block them — into a single resumable JSON cache, via the GitHub
GraphQL API.
scanner [--cache <file>] [--token <token>] [--wait] [--force] <owner/repo> ...
<owner/repo> ... — one or more target repositories to scan.--cache <file> — cache file path (default issue-tree-cache.json).--token <token> — GitHub token (see Authentication below).--wait — on hitting the rate limit, sleep until it resets and continue, rather than stopping.--force — discard any existing cache before scanning.The scanner resolves a GitHub token from three sources, in order: the --token flag,
the GITHUB_TOKEN environment variable, then gh auth token (the GitHub CLI).
The scan runs in four phases — repository issues, body blockers, comments, comment blockers — and checkpoints the JSON cache continuously. A scan interrupted by the rate limit is resumed by simply re-running the same command, picking up at the last saved page cursor.
An already-complete cache is re-scanned incrementally, fetching only issues changed
since the last run. Before opening pagination for a previously-completed repo, a
cheap single-scalar GraphQL probe checks repository.updatedAt; when GitHub reports
the repo has not changed since the cache's lastScanCompletedAt, the repo is skipped
entirely (costing one rate-limit point instead of thousands of page fetches).
Even mid-scan, a resumed repo's since filter is set to the maximum updatedAt
across already-cached issues, so the GraphQL server only returns issues newer than
what is on disk.
MIT