Skip to content

StoneCypher/issue_tree

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

116 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

issue_tree v0.15.0

Version 0.15.0 was built on Saturday, May 30, 2026 at GMT-07:00 1780197705658 from hash fbc3416.

issue_tree scans one or more GitHub repositories and renders a dependency graph of their blocking-issue links in a variety of formats — including a self-contained interactive HTML page. The one-shot all and auto commands scan straight to an image with nothing written to your working directory; an optional on-disk JSON cache (the scanrender two-step, or --cache) persists a scan so it can be resumed or re-rendered without re-fetching.

 

 

issue_tree CLI

issue_tree exposes five subcommands that implement a scan → graph → render pipeline:

Subcommands

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)
issue_tree update [--cache <file>] [--token <token>] [--wait] [--quiet]

Getting help

Every level of the CLI is self-documenting, and help is always free — it prints to stdout and exits 0.

issue_tree                       # bare invocation → general help
issue_tree --help                # general help (also -h, or `issue_tree help`)
issue_tree <subcommand> --help   # that subcommand's flags (e.g. render --help)
issue_tree help <subcommand>     # same, in `help` form (e.g. help render)
  • General help prints a one-line usage synopsis, the four subcommands each with a one-line description, a pointer to per-subcommand help, and the token note (a GitHub token is resolved from --token, then GITHUB_TOKEN, then gh auth token).
  • Per-subcommand help (scan/render/all/auto) prints that command's synopsis plus its own flags, each with a short description. render, all, and auto share one render-layout flag block.
  • --help and -h are recognized anywhere on the line and always win, even alongside other flags: issue_tree render --format svg --help prints render help and exits without rendering.
  • issue_tree help <unknown-topic> is lenient — it falls back to the general help rather than erroring.

scan

Fetches 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).

render

Reads 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.

all

Scans the named repos and renders, in a single invocation, accepting every flag from both scan and render. Cache-free by default: the scan runs in memory and nothing is written to your working directory — so naming a new repo never reads or modifies an unrelated cache left behind by an earlier run. Pass --cache <file> to persist the scan to disk instead (enabling resume, and later re-rendering without re-scanning).

issue_tree all --repo owner/name --out g.png            # scan in memory, write only the image
issue_tree all --repo owner/name --cache c.json --out g.png  # also persist the scan to c.json

auto

Walks a project tree, extracts the GitHub repository URL from every project manifest it finds, scans the deduped list, and renders the result — auto is a scan + render combo, exactly like all, except the repo list comes from filesystem discovery instead of --repo flags. Like all, it is cache-free by default (scan in memory, render, write nothing but the image); pass --cache <file> to persist the scan. 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                              # discover + scan in memory + write auto-named PNG (no cache file)
issue_tree auto ./projects --cache big.json  # rooted at ./projects, persisted to big.json
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.

auto sources its repos from disk, so passing --repo to auto is an error (it would otherwise be silently ignored, scanning the locally-detected repo instead of the named one). To scan a specific repo, use all: issue_tree all --repo <owner/repo>.

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 whatever was scanned so far (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.

update

Incrementally refreshes the repos already recorded in a cache — the discoverable shorthand for "re-run scan against an existing cache". The targets come from the cache's repos, so update takes no --repo (passing one is an error pointing you to scan/all).

issue_tree update                       # refresh ./issue-tree-cache.json
issue_tree update --cache fsl.json --wait

For each repo in the cache it fires a cheap repository.updatedAt probe and skips any repo unchanged since its last scan; for changed repos it passes the repo's lastScanCompletedAt as the GraphQL since filter, so only issues modified since then are re-fetched. Flags: --cache <file> (default issue-tree-cache.json), --token, --wait, --quiet/-q. A missing or repo-less cache is an error — update refreshes an existing cache, it does not create one.

Output formats

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.

Important: render never scans

render 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.

 

 

Test status

Count Statement Branch Func Line
Unit 489 96.18% {{unitbranch}}% {{unitfunc}}% {{unitline}}%
Stochastic 18 96.18% {{stochbranch}}% {{stochfunc}}% {{stochline}}%
Docblock count 40%
Docblock coverage 286 40%

star_chart

sunburst visualization treemap visualization
network visualization flamegraph visualization

 

 

How to use this template

 

Before invoking it

  1. Decide whether to
    1. Update the deps in the template recommended
    2. Update the deps post-install
    3. Let the deps be out of date

 

After invoking it

  1. Reset package version
  2. Turn Github Pages on, and point it at master//docs
  3. Set up the auth token TODO_TOKEN_FOR_GH_CI_CD after renaming it in ci.yml
  4. Change all the issue_trees in this file's top block links
  5. Change all the issue_trees in package.json
  6. Change the issue_tree in verify_version_bump.js
  7. Write or copy-paste the description in package.json
  8. Search for all remaining TODOs
  9. Update meta tags and TODOs in src/html/index.html
  10. Write a base-README.md
  11. Change all the issue_trees in rollup.config.js
  12. Decide whether to
    1. re-add a bin block to package.json, or
    2. remove the bin config from rollup.config.js
  13. npm install && npm run build
    1. Maybe update the deps?
  14. Handle the MAYBE-REMOVEs in the HTML HEAD
    1. Change src/html/index.html 's <title>
    2. Maybe replace src/html/favicon.png
  15. commit and vroom

 

 

Scanner / cache module

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.

CLI

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.

Authentication

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).

Resumable cache

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.

 

 

License

MIT

About

Issue tree CLI

Resources

License

Code of conduct

Contributing

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors