{"id":49375857,"url":"https://github.com/stacktower-io/stacktower","last_synced_at":"2026-05-02T19:00:58.871Z","repository":{"id":327522850,"uuid":"1039749901","full_name":"stacktower-io/stacktower","owner":"stacktower-io","description":"Visualize package dependencies as XKCD-style tower diagrams. Supports Python, Rust, JavaScript, Ruby, PHP, Java, and Go.","archived":false,"fork":false,"pushed_at":"2026-04-20T03:21:15.000Z","size":3802,"stargazers_count":507,"open_issues_count":13,"forks_count":12,"subscribers_count":3,"default_branch":"main","last_synced_at":"2026-04-25T13:38:45.153Z","etag":null,"topics":["dependencies","golang","graphs"],"latest_commit_sha":null,"homepage":"https://app.stacktower.io/","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/stacktower-io.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":".github/FUNDING.yml","license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":".github/CODEOWNERS","security":"SECURITY.md","support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null},"funding":{"github":"matzehuels"}},"created_at":"2025-08-17T22:50:34.000Z","updated_at":"2026-04-20T03:19:01.000Z","dependencies_parsed_at":"2026-01-13T02:00:55.718Z","dependency_job_id":null,"html_url":"https://github.com/stacktower-io/stacktower","commit_stats":null,"previous_names":["matzehuels/stacktower","stacktower-io/stacktower"],"tags_count":9,"template":false,"template_full_name":null,"purl":"pkg:github/stacktower-io/stacktower","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/stacktower-io%2Fstacktower","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/stacktower-io%2Fstacktower/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/stacktower-io%2Fstacktower/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/stacktower-io%2Fstacktower/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/stacktower-io","download_url":"https://codeload.github.com/stacktower-io/stacktower/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/stacktower-io%2Fstacktower/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":32545870,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-02T12:25:33.646Z","status":"ssl_error","status_checked_at":"2026-05-02T12:24:51.733Z","response_time":132,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["dependencies","golang","graphs"],"created_at":"2026-04-28T02:00:34.657Z","updated_at":"2026-05-02T19:00:58.856Z","avatar_url":"https://github.com/stacktower-io.png","language":"Go","funding_links":["https://github.com/sponsors/matzehuels"],"categories":["Utilities"],"sub_categories":["Utility/Miscellaneous"],"readme":"# Stacktower\n\n[![CI](https://github.com/stacktower-io/stacktower/actions/workflows/ci.yml/badge.svg)](https://github.com/stacktower-io/stacktower/actions/workflows/ci.yml)\n[![codecov](https://codecov.io/gh/stacktower-io/stacktower/graph/badge.svg)](https://codecov.io/gh/stacktower-io/stacktower)\n[![Go Report Card](https://goreportcard.com/badge/github.com/stacktower-io/stacktower)](https://goreportcard.com/report/github.com/stacktower-io/stacktower)\n[![Go Reference](https://pkg.go.dev/badge/github.com/stacktower-io/stacktower.svg)](https://pkg.go.dev/github.com/stacktower-io/stacktower)\n[![Release](https://img.shields.io/github/v/release/stacktower-io/stacktower)](https://github.com/stacktower-io/stacktower/releases)\n[![License](https://img.shields.io/badge/License-Apache_2.0-blue.svg)](https://opensource.org/licenses/Apache-2.0)\n\nInspired by [XKCD #2347](https://xkcd.com/2347/), Stacktower renders dependency graphs as **physical towers** where blocks rest on what they depend on. Your application sits at the top, supported by libraries below—all the way down to that one critical package maintained by _some dude in Nebraska_.\n\n\u003cp align=\"center\"\u003e\n  \u003cimg src=\"https://www.stacktower.io/plots/showcase/python/fastapi.svg\" alt=\"FastAPI dependency tower\" width=\"600\"\u003e\n\u003c/p\u003e\n\n---\n\n\u003cp align=\"center\"\u003e\n  \u003ca href=\"https://app.stacktower.io\"\u003e\u003cstrong\u003e🚀 Try the Web App\u003c/strong\u003e\u003c/a\u003e · \u003ca href=\"https://www.stacktower.io\"\u003e\u003cstrong\u003e📖 Read the Story\u003c/strong\u003e\u003c/a\u003e\n\u003c/p\u003e\n\n---\n\n## Installation\n\n### Homebrew (macOS/Linux)\n\n```bash\nbrew tap stacktower-io/tap\nbrew install stacktower\n```\n\n### Go\n\n```bash\ngo install github.com/stacktower-io/stacktower/cmd/stacktower@latest\n```\n\n### From Source\n\n```bash\ngit clone https://github.com/stacktower-io/stacktower.git\ncd stacktower\ngo build -o bin/stacktower ./cmd/stacktower\n```\n\n## Quick Start\n\n```bash\n# Render the included Flask example (XKCD-style tower is the default)\nstacktower render examples/real/flask.json -o flask.svg\n\n# Parse a package from a registry, then render it\nstacktower parse python fastapi -o fastapi.json\nstacktower render fastapi.json -o fastapi.svg\n\n# Or pipe parse straight into render\nstacktower parse python flask | stacktower render - -o flask.svg\n```\n\n## Global Options\n\nThese flags apply to all commands:\n\n| Flag              | Description                                                |\n| ----------------- | ---------------------------------------------------------- |\n| `-v`, `--verbose` | Enable debug logging (search space info, timing details)   |\n| `-q`, `--quiet`   | Suppress non-essential output (success messages, stats)    |\n| `-h`, `--help`    | Show help for any command                                  |\n| `--version`       | Show version information                                   |\n\n---\n\n## `stacktower parse`\n\nParse dependency graphs from package registries or local manifest files.\n\n```bash\nstacktower parse \u003clanguage\u003e \u003cpackage-or-file\u003e [flags]\nstacktower parse \u003cmanifest-file\u003e                       # Auto-detect language from filename\nstacktower parse github [owner/repo]                   # Parse from a GitHub repository\n```\n\n**Supported languages:** `python`, `rust`, `javascript`, `ruby`, `php`, `java`, `go`\n\n### Parse Options\n\n| Flag                    | Description                                                                          |\n| ----------------------- | ------------------------------------------------------------------------------------ |\n| `-o`, `--output`        | Output file (stdout if empty)                                                        |\n| `-n`, `--name`          | Project name for manifest parsing (auto-detected if not set)                         |\n| `--max-depth N`         | Maximum dependency depth (default: 10, max: 100)                                     |\n| `--max-nodes N`         | Maximum packages to fetch (default: 5000, max: 50000)                                |\n| `--workers N`           | Concurrent fetch workers (default: 20)                                               |\n| `--enrich`              | Enrich with GitHub metadata — stars, maintainers (default: true)                     |\n| `--contributors`        | Fetch GitHub contributors for Nebraska rankings (slower API calls)                   |\n| `--security-scan`       | Best-effort scan for known vulnerabilities via OSV.dev                               |\n| `--dependency-scope`    | Dependency scope: `prod_only` (default) or `all` (includes dev dependencies)         |\n| `--include-prerelease`  | Include prerelease versions (alpha/beta/rc/dev) in resolution                        |\n| `--runtime-version`     | Target runtime version for marker evaluation (e.g., `3.11` for Python)               |\n| `--no-cache`            | Disable caching                                                                      |\n\n### From Package Registries\n\n```bash\nstacktower parse python fastapi -o fastapi.json                  # PyPI\nstacktower parse rust serde -o serde.json                        # crates.io\nstacktower parse javascript yargs -o yargs.json                  # npm\nstacktower parse ruby rails -o rails.json                        # RubyGems\nstacktower parse php monolog/monolog -o monolog.json             # Packagist\nstacktower parse java com.google.guava:guava -o guava.json       # Maven Central\nstacktower parse go github.com/gin-gonic/gin -o gin.json         # Go Module Proxy\n```\n\nUse `package@version` to pin a specific version. Without it, the latest version is resolved:\n\n```bash\nstacktower parse python fastapi@0.104.1 -o fastapi.json\nstacktower parse rust serde@1.0.195 -o serde.json\nstacktower parse javascript @angular/core@17.0.0 -o angular.json  # scoped packages work too\n```\n\n### From Manifest Files\n\n```bash\nstacktower parse python poetry.lock -o deps.json\nstacktower parse python requirements.txt -o deps.json\nstacktower parse rust Cargo.toml -o deps.json\nstacktower parse javascript package.json -o deps.json\nstacktower parse ruby Gemfile -o deps.json\nstacktower parse php composer.json -o deps.json\nstacktower parse java pom.xml -o deps.json\nstacktower parse go go.mod -o deps.json\n```\n\nWhen the argument exists on disk or matches a known manifest filename, Stacktower auto-detects the language from the filename so you can omit the language subcommand:\n\n```bash\nstacktower parse poetry.lock -o deps.json\nstacktower parse package-lock.json -o deps.json\nstacktower parse Cargo.lock -o deps.json\n```\n\nThe project name (root node) is auto-detected from the manifest or a sibling file:\n\n- **Cargo.toml**: `[package].name`\n- **go.mod**: `module` directive\n- **package.json**: `name` field\n- **composer.json**: `name` field\n- **pom.xml**: `groupId:artifactId`\n- **poetry.lock / requirements.txt**: `pyproject.toml` (sibling)\n- **Gemfile**: `*.gemspec` (sibling)\n\nUse `--name` to override:\n\n```bash\nstacktower parse python requirements.txt --name=\"my-project\" -o deps.json\n```\n\n### Metadata Enrichment\n\nBy default, `parse` enriches packages with GitHub metadata (stars, maintainers, last commit) for richer visualizations. Set `GITHUB_TOKEN` for higher rate limits:\n\n```bash\nexport GITHUB_TOKEN=your_token\nstacktower parse python fastapi -o fastapi.json\n\n# Skip enrichment if you don't need it\nstacktower parse python fastapi --enrich=false -o fastapi.json\n```\n\n### Vulnerability Scanning\n\nAdd the `--security-scan` flag to annotate every package with its highest vulnerability severity via [OSV.dev](https://osv.dev/):\n\n```bash\nstacktower parse python fastapi --security-scan -o fastapi.json\nstacktower parse javascript package.json --security-scan -o deps.json\n```\n\nWhen `--security-scan` is enabled, Stacktower performs a best-effort query to OSV.dev in a single batch request and writes severity data (`critical`, `high`, `medium`, `low`) into each node's metadata when available. The scanned graph is cached separately from non-scanned graphs, so subsequent runs are instant.\n\nDuring rendering, vulnerable packages are automatically colour-coded by severity. Use `--show-vulns=false` to suppress the colours while keeping the data in the graph:\n\n```bash\nstacktower render fastapi.json --show-vulns=false -o fastapi.svg\n```\n\n### Parse from GitHub\n\nParse dependencies directly from a GitHub repository with interactive selection:\n\n```bash\nstacktower parse github                              # Full interactive flow\nstacktower parse github owner/repo                   # Select ref + manifest\nstacktower parse github owner/repo --ref v2.0.0      # Parse at specific tag\nstacktower parse github owner/repo --ref main         # Explicit branch\nstacktower parse github owner/repo --timeout 10m      # Custom timeout\n```\n\n| Flag            | Description                                         |\n| --------------- | --------------------------------------------------- |\n| `--ref`         | Git ref (branch, tag, or commit SHA)                |\n| `--timeout`     | Timeout for GitHub operations (default: 5m)         |\n| `--public-only` | Show only public repositories in interactive picker |\n\n\u003e **Private repos:** Install the GitHub App with `stacktower github install` and grant access to the repos you need.\n\n### Piping\n\nWhen `parse` detects that stdout is piped, it emits clean JSON with no chrome — making it composable with other tools:\n\n```bash\n# Pipe directly into render\nstacktower parse python flask | stacktower render - -o flask.svg\n\n# Pipe into jq for analysis\nstacktower parse python requests -o - | jq '.nodes | length'\n\n# Combine with security scan\nstacktower parse python django --security-scan | stacktower render - --show-vulns -o django.svg\n```\n\nProgress indicators and summary messages always go to stderr, so they never corrupt piped JSON.\n\n### Terminal Output\n\nA typical `parse` run looks like this:\n\n```\n⠙ Resolving python/flask...  [42/5000]\n  starlette  jinja2  markupsafe  click\n\n✓ Resolved flask (python)\n  42 packages · 67 edges · depth 5 · fresh · 2.3s\n  → flask.json\nRender: stacktower render flask.json\n```\n\n---\n\n## `stacktower resolve`\n\nLightweight alternative to `parse` for quickly testing dependency resolution. Auto-detects the language from manifest filenames and prints a human-readable dependency tree.\n\n```bash\nstacktower resolve \u003cmanifest-file\u003e\nstacktower resolve \u003clanguage\u003e \u003cpackage[@version]\u003e\n```\n\n### Resolve Options\n\n| Flag                   | Description                                                                  |\n| ---------------------- | ---------------------------------------------------------------------------- |\n| `-o`, `--output`       | Output file for JSON (stdout shows tree by default)                          |\n| `-n`, `--name`         | Project name (for manifest parsing)                                          |\n| `--max-depth N`        | Maximum dependency depth (default: 10)                                       |\n| `--max-nodes N`        | Maximum packages to fetch (default: 5000)                                    |\n| `--enrich`             | Enrich with GitHub metadata (off by default, unlike `parse`)                 |\n| `--dependency-scope`   | Dependency scope: `prod_only` (default) or `all`                             |\n| `--include-prerelease` | Include prerelease versions in resolution                                    |\n| `--runtime-version`    | Target runtime version for marker evaluation                                 |\n| `--no-cache`           | Disable caching                                                              |\n\n### Resolve Examples\n\n```bash\n# Auto-detect language from filename\nstacktower resolve poetry.lock\nstacktower resolve Cargo.lock\nstacktower resolve package-lock.json\nstacktower resolve go.mod\n\n# Resolve from registry\nstacktower resolve python fastapi\nstacktower resolve rust serde@1.0.195\n\n# Save resolution as JSON for rendering\nstacktower resolve poetry.lock -o deps.json\nstacktower render deps.json -o deps.svg\n```\n\n**Output:**\n\n```\nfastapi 0.104.1\n  starlette 0.27.0\n    anyio 4.0.0\n      sniffio 1.3.0\n  pydantic 2.5.2\n    pydantic-core 2.14.5\n    typing-extensions 4.8.0\n\nResolved 7 packages (max depth: 3, direct: 2)\n```\n\n### resolve vs parse\n\n| | `resolve` | `parse` |\n| --- | --- | --- |\n| Language detection | Auto-detected from filename | Must specify language subcommand (or auto-detect from filename) |\n| Default output | Human-readable tree | Graph JSON |\n| Metadata enrichment | Off (opt-in with `--enrich`) | On by default (`--enrich=false` to skip) |\n| Best for | Local testing, inspecting deps | Rendering pipeline, CI |\n\n---\n\n## `stacktower list`\n\nList all available versions of a package from its registry. Requires a language subcommand:\n\n```bash\nstacktower list \u003clanguage\u003e \u003cpackage\u003e [flags]\n```\n\n### List Options\n\n| Flag                   | Description                                                           |\n| ---------------------- | --------------------------------------------------------------------- |\n| `--all`                | Show all versions (default: newest 20)                                |\n| `--runtime-version`    | Filter versions compatible with a specific runtime (e.g., `3.8`)      |\n| `--supported-runtimes` | Display runtime constraint for each version                           |\n| `--no-cache`           | Bypass cached version data                                            |\n\n### List Examples\n\n```bash\nstacktower list python fastapi\nstacktower list rust serde\nstacktower list javascript react\nstacktower list go github.com/gin-gonic/gin\n\n# Show all versions\nstacktower list python django --all\n\n# Filter by Python version compatibility\nstacktower list python fastapi --runtime-version 3.8\n\n# Show runtime requirements for each version\nstacktower list python fastapi --supported-runtimes\n```\n\n**Output:**\n\n```\n  fastapi  python · 277 versions\n  latest   0.129.2\n\n  0.129.1   0.129.0   0.128.8   0.128.7   0.128.6   0.128.5   0.128.4\n  0.128.3   0.128.2   0.128.1   0.128.0   0.127.1   0.127.0   0.126.0\n  0.125.0   0.124.4   0.124.3   0.124.2   0.124.1   0.124.0\n\n  … 256 older versions not shown (use --all to show all)\n```\n\n---\n\n## `stacktower render`\n\nGenerate visualizations from parsed JSON graphs. This is a shortcut that combines `layout` and `visualize` in one step.\n\n```bash\nstacktower render \u003cgraph.json|-\u003e [flags]\n```\n\nUse `-` to read graph JSON from stdin.\n\n### Render Options\n\n| Flag               | Description                                                              |\n| ------------------ | ------------------------------------------------------------------------ |\n| `-o`, `--output`   | Output file or base path for multiple formats                            |\n| `-t`, `--type`     | Visualization type: `tower` (default), `nodelink`                        |\n| `-f`, `--format`   | Output format(s): `svg` (default), `json`, `pdf`, `png` (comma-separated)|\n| `--normalize`      | Apply graph normalization (default: true)                                |\n| `--show-vulns`     | Show vulnerability severity colours (default: true)                      |\n| `--show-licenses`  | Show license compliance indicators — copyleft/unknown borders (default: true) |\n| `--flags-on-top`   | Render security flags on top of all blocks (default: true)               |\n| `--no-cache`       | Disable caching                                                          |\n\n### Tower-Specific Options\n\n| Flag                              | Description                                                           |\n| --------------------------------- | --------------------------------------------------------------------- |\n| `--width N`                       | Frame width in pixels (default: 800)                                  |\n| `--height N`                      | Frame height in pixels (default: 600)                                 |\n| `--style handdrawn\\|simple`       | Visual style (default: handdrawn)                                     |\n| `--randomize`                     | Vary block widths to visualize load-bearing structure (default: true) |\n| `--merge`                         | Merge subdivider blocks into continuous towers (default: true)        |\n| `--popups`                        | Enable hover popups with package metadata (default: true)             |\n| `--nebraska`                      | Show \"Nebraska guy\" maintainer ranking panel                          |\n| `--edges`                         | Show dependency edges as dashed lines                                 |\n| `--ordering optimal\\|barycentric` | Crossing minimization algorithm (default: optimal)                    |\n| `--ordering-timeout N`            | Timeout for optimal search in seconds (default: 60)                   |\n\n### Render Examples\n\n```bash\n# Basic tower rendering (XKCD-style)\nstacktower render flask.json -o flask.svg\n\n# Clean style without hand-drawn effects\nstacktower render serde.json --style simple --randomize=false --popups=false -o serde.svg\n\n# Node-link diagram (uses Graphviz DOT layout)\nstacktower render yargs.json -t nodelink -o yargs.svg\n\n# With Nebraska maintainer rankings\nstacktower render flask.json --nebraska -o flask.svg\n\n# Multiple output formats\nstacktower render flask.json -f svg,pdf,png -o flask\n\n# Large graph with faster ordering\nstacktower render big-project.json --ordering barycentric -o big.svg\n\n# Custom dimensions\nstacktower render flask.json --width 1200 --height 900 -o flask-large.svg\n\n# Show dependency edges\nstacktower render flask.json --edges -o flask-edges.svg\n\n# Disable vulnerability colours\nstacktower render scanned.json --show-vulns=false -o clean.svg\n```\n\n### Output Formats\n\nOutput path behavior:\n\n- **No `-o`**: Derives from input (`input.json` → `input.\u003cformat\u003e`)\n- **Single format**: Uses exact path (`-o out.svg` → `out.svg`)\n- **Multiple formats**: Strips extension, adds format (`-o out -f svg,json` → `out.svg`, `out.json`)\n\n\u003e **Note:** PDF and PNG output requires [librsvg](https://wiki.gnome.org/Projects/LibRsvg):\n\u003e\n\u003e - macOS: `brew install librsvg`\n\u003e - Linux: `apt install librsvg2-bin`\n\n### Two-Step Workflow\n\nFor more control, run `layout` and `visualize` separately:\n\n```bash\nstacktower layout flask.json -o flask.layout.json\nstacktower visualize flask.layout.json -o flask.svg\n```\n\n---\n\n## `stacktower layout`\n\nCompute visualization layout from a dependency graph. Outputs a layout JSON that can be rendered separately with `visualize`.\n\n```bash\nstacktower layout \u003cgraph.json\u003e [flags]\n```\n\n### Layout Options\n\n| Flag                              | Description                                                           |\n| --------------------------------- | --------------------------------------------------------------------- |\n| `-o`, `--output`                  | Output file (default: `\u003cinput\u003e.layout.json`)                          |\n| `-t`, `--type`                    | Visualization type: `tower` (default), `nodelink`                     |\n| `--normalize`                     | Apply graph normalization (default: true)                             |\n| `--width N`                       | Frame width in pixels (default: 800)                                  |\n| `--height N`                      | Frame height in pixels (default: 600)                                 |\n| `--style`                         | Visual style: `handdrawn` (default), `simple`                         |\n| `--ordering`                      | Ordering algorithm: `optimal` (default), `barycentric`                |\n| `--ordering-timeout N`            | Timeout for optimal search in seconds (default: 60)                   |\n| `--randomize`                     | Randomize block widths (tower, default: true)                         |\n| `--merge`                         | Merge subdivider blocks (tower, default: true)                        |\n| `--nebraska`                      | Show Nebraska maintainer ranking (tower)                              |\n| `--show-vulns`                    | Show vulnerability colours (default: true)                            |\n| `--show-licenses`                 | Show license indicators (default: true)                               |\n| `--flags-on-top`                  | Render security flags on top of all blocks (default: true)            |\n| `--no-cache`                      | Disable caching                                                       |\n\n---\n\n## `stacktower visualize`\n\nRender visualization from a computed layout (produced by `layout`).\n\n```bash\nstacktower visualize \u003clayout.json\u003e [flags]\n```\n\n### Visualize Options\n\n| Flag               | Description                                                              |\n| ------------------ | ------------------------------------------------------------------------ |\n| `-o`, `--output`   | Output file or base path for multiple formats                            |\n| `-f`, `--format`   | Output format(s): `svg` (default), `json`, `pdf`, `png` (comma-separated)|\n| `--style`          | Visual style: `handdrawn` (default), `simple`                            |\n| `--edges`          | Show dependency edges (tower)                                            |\n| `--popups`         | Show hover popups with metadata (default: true)                          |\n| `--show-vulns`     | Show vulnerability colours (default: true)                               |\n| `--show-licenses`  | Show license indicators (default: true)                                  |\n| `--flags-on-top`   | Render security flags on top of all blocks (default: true)               |\n| `--no-cache`       | Disable caching                                                          |\n\n---\n\n## `stacktower cache`\n\nManage the local cache (`~/.cache/stacktower/`).\n\n```bash\nstacktower cache clear    # Delete all cached entries\nstacktower cache path     # Print cache directory path\nstacktower cache stats    # Show entry count, size, and age (alias: cache info)\n```\n\n```bash\n# Check cache statistics\nstacktower cache stats\n\n# Clear the cache before a fresh run\nstacktower cache clear\nstacktower parse python requests -o requests.json\n\n# Get cache path for scripting\nCACHE_DIR=$(stacktower cache path)\n```\n\n---\n\n## `stacktower why`\n\nFind all dependency paths from the root to a target package. Answers \"why is this package in my dependency tree?\"\n\n```bash\nstacktower why \u003cgraph.json|-\u003e \u003cpackage\u003e [package...] [flags]\n```\n\n### Why Options\n\n| Flag             | Description                                          |\n| ---------------- | ---------------------------------------------------- |\n| `-f`, `--format` | Output format: `text` (default), `json`              |\n| `-o`, `--output` | Output file (stdout if empty)                        |\n| `--max-paths N`  | Maximum paths to display per target (default: 10)    |\n| `--shortest`     | Show only the shortest path(s)                       |\n\n### Why Examples\n\n```bash\nstacktower why flask.json markupsafe\nstacktower why flask.json markupsafe -f json\nstacktower why flask.json markupsafe --shortest\n\n# Multiple targets\nstacktower why flask.json markupsafe click jinja2\n\n# Piped from parse\nstacktower parse python flask | stacktower why - markupsafe\n```\n\n**Output:**\n\n```\nmarkupsafe 3.0.3\n\n  flask → markupsafe\n  flask → jinja2 → markupsafe\n  flask → werkzeug → markupsafe\n\n3 paths found (shortest: depth 1)\n```\n\n---\n\n## `stacktower stats`\n\nProduce a dependency health report from a parsed graph. Answers \"how healthy is my dependency tree?\"\n\n```bash\nstacktower stats \u003cgraph.json|-\u003e [flags]\n```\n\n### Stats Options\n\n| Flag             | Description                         |\n| ---------------- | ----------------------------------- |\n| `-f`, `--format` | Output format: `text` (default), `json` |\n| `-o`, `--output` | Output file (stdout if empty)       |\n\n### Stats Examples\n\n```bash\nstacktower stats flask.json\nstacktower stats flask.json -f json\n\n# Piped from parse (with security scan for vuln data)\nstacktower parse python flask --security-scan | stacktower stats -\n```\n\n**Output:**\n\n```\nflask 3.1.3  ·  python\n\nOverview\n  8 packages · 9 edges · depth 2\n  6 direct · 1 transitive\n\nMaintenance\n  3 single-maintainer packages (43%)\n  1 brittle packages: pycparser\n  Median last commit: 47 days ago\n\nLicenses\n  7 permissive (MIT: 4, Apache-2.0: 2, BSD-3-Clause: 1)\n  1 unknown\n\nVulnerabilities\n  0 critical · 1 high · 2 medium · 0 low\n  Affected: werkzeug (high), jinja2 (medium)\n\nTop load-bearing packages (most reverse dependencies)\n  1. markupsafe           — 3 dependents\n  2. typing-extensions    — 2 dependents\n```\n\n---\n\n## `stacktower diff`\n\nCompare two dependency graphs and report what changed: added, removed, updated packages, and new vulnerabilities.\n\n```bash\nstacktower diff \u003cbefore.json\u003e \u003cafter.json|-\u003e [flags]\n```\n\n### Diff Options\n\n| Flag               | Description                                        |\n| ------------------ | -------------------------------------------------- |\n| `-f`, `--format`   | Output format: `text` (default), `json`            |\n| `-o`, `--output`   | Output file (stdout if empty)                      |\n| `--fail-on-vuln`   | Exit 3 if new vulnerabilities were introduced (CI) |\n\n### Diff Examples\n\n```bash\n# Compare two saved graphs\nstacktower diff flask-old.json flask.json\nstacktower diff flask-old.json flask.json -f json\n\n# Pipe the \"new\" graph from stdin\nstacktower parse python flask | stacktower diff flask-old.json -\n\n# Fail in CI if new vulnerabilities appear\nstacktower diff old.json new.json --fail-on-vuln\n```\n\n**Output:**\n\n```\nflask  2.0.0 → 3.1.3\n\n+ 1 added    - 0 removed    ~ 2 updated    = 5 unchanged\n\nAdded\n  + blinker 1.9.0\n\nUpdated\n  ~ flask                2.0.0 → 3.1.3\n  ~ jinja2               3.1.2 → 3.1.6\n```\n\n---\n\n## `stacktower sbom`\n\nExport the dependency graph as a standards-compliant Software Bill of Materials in CycloneDX or SPDX format.\n\n```bash\nstacktower sbom \u003cgraph.json|-\u003e [flags]\n```\n\n### SBOM Options\n\n| Flag               | Description                                              |\n| ------------------ | -------------------------------------------------------- |\n| `-f`, `--format`   | SBOM format: `cyclonedx` (default), `spdx`              |\n| `-o`, `--output`   | Output file (stdout if empty)                            |\n| `--encoding`       | Serialization: `json` (default), `xml` (CycloneDX only) |\n| `--spec-version`   | Specification version (default: latest supported)        |\n\n### SBOM Examples\n\n```bash\n# CycloneDX (default)\nstacktower sbom flask.json -o flask.cdx.json\nstacktower sbom flask.json -f cyclonedx --encoding xml -o flask.cdx.xml\n\n# SPDX\nstacktower sbom flask.json -f spdx -o flask.spdx.json\n\n# Piped from parse (with security scan for vuln data in SBOM)\nstacktower parse python flask --security-scan | stacktower sbom - -o flask.cdx.json\n```\n\nThe generated SBOM includes package identifiers (purls), license data, dependency relationships, and vulnerability findings when the graph was parsed with `--security-scan`.\n\n---\n\n## `stacktower github`\n\nGitHub authentication and app installation commands.\n\n```bash\nstacktower github login       # Authenticate with GitHub (device flow)\nstacktower github logout      # Remove stored credentials\nstacktower github whoami      # Show current session and app installation status\nstacktower github install     # Install/configure the GitHub App for repo access\nstacktower github uninstall   # Uninstall the GitHub App (opens settings page)\n```\n\n```bash\n# Login (opens browser for device authorization)\nstacktower github login\n\n# Install the GitHub App to grant access to your repositories\nstacktower github install\n\n# Verify session and check app installation\nstacktower github whoami\n\n# Parse from GitHub (auto-prompts login if needed)\nstacktower parse github owner/repo -o deps.json\n```\n\n\u003e **Note:** To access private repositories, install the Stacktower GitHub App\n\u003e and grant it access to the specific repos you want to analyze.\n\n---\n\n## `stacktower info`\n\nDisplay version, supported languages, registries, and manifest filenames.\n\n```bash\nstacktower info\n```\n\n**Output:**\n\n```\nstacktower v1.0.0 (abc1234, 2024-01-15)\nCommit abc1234\nBuilt  2024-01-15\n\nSupported Languages\n  python     registry: pypi.org\n    manifests: poetry.lock, pyproject.toml, requirements.txt, uv.lock\n  rust       registry: crates.io\n    manifests: Cargo.lock, Cargo.toml\n  javascript registry: registry.npmjs.org\n    manifests: package-lock.json, package.json\n  ...\n\nDocs: https://app.stacktower.io/cli-docs\n```\n\n---\n\n## `stacktower version`\n\nShow version and build information.\n\n```bash\nstacktower version\n```\n\n---\n\n## `stacktower completion`\n\nGenerate shell completion scripts.\n\n```bash\nstacktower completion bash\nstacktower completion zsh\nstacktower completion fish\nstacktower completion powershell\n```\n\n### Completion Setup\n\n```bash\n# Bash (Linux)\nstacktower completion bash \u003e /etc/bash_completion.d/stacktower\n\n# Bash (macOS with Homebrew)\nstacktower completion bash \u003e $(brew --prefix)/etc/bash_completion.d/stacktower\n\n# Zsh\nstacktower completion zsh \u003e \"${fpath[1]}/_stacktower\"\n\n# Fish\nstacktower completion fish \u003e ~/.config/fish/completions/stacktower.fish\n```\n\n---\n\n## Included Examples\n\nThe repository ships with pre-parsed graphs and manifest files so you can experiment immediately:\n\n```bash\n# Real packages with full metadata (XKCD-style by default)\nstacktower render examples/real/flask.json -o flask.svg\nstacktower render examples/real/serde.json -o serde.svg\nstacktower render examples/real/yargs.json -o yargs.svg\n\n# With Nebraska guy maintainer ranking\nstacktower render examples/real/flask.json --nebraska -o flask.svg\n\n# Synthetic test cases for layout algorithm testing\nstacktower render examples/test/diamond.json -o diamond.svg\nstacktower render examples/test/crossing.json -o crossing.svg\n```\n\n\u003e **Note:** For accurate Nebraska rankings, parse with `--contributors` to fetch maintainer data:\n\u003e ```bash\n\u003e stacktower parse python flask --contributors -o flask.json\n\u003e stacktower render flask.json --nebraska -o flask.svg\n\u003e ```\n\n### Example Manifest \u0026 Lock Files\n\nThe `examples/manifest/` directory contains sample manifest and lock files for every supported ecosystem, useful for testing `resolve` and `parse` locally:\n\n| File | Language | Type |\n| --- | --- | --- |\n| `poetry.lock` | Python | Lock file |\n| `uv.lock` | Python | Lock file |\n| `pyproject.toml` | Python | Manifest |\n| `requirements.txt` | Python | Manifest |\n| `package-lock.json` | JavaScript | Lock file |\n| `package.json` | JavaScript | Manifest |\n| `Cargo.lock` | Rust | Lock file |\n| `Cargo.toml` | Rust | Manifest |\n| `Gemfile.lock` | Ruby | Lock file |\n| `Gemfile` | Ruby | Manifest |\n| `composer.lock` | PHP | Lock file |\n| `composer.json` | PHP | Manifest |\n| `go.mod` | Go | Manifest |\n| `pom.xml` | Java | Manifest |\n\n```bash\nstacktower resolve examples/manifest/poetry.lock\nstacktower resolve examples/manifest/Cargo.lock\nstacktower resolve examples/manifest/package-lock.json\n```\n\n---\n\n## JSON Format\n\nThe render layer accepts a simple JSON format, making it easy to visualize **any** directed graph—not just package dependencies. You can hand-craft graphs for component diagrams, callgraphs, or pipe output from other tools.\n\n### Minimal Example\n\n```json\n{\n  \"nodes\": [{ \"id\": \"app\" }, { \"id\": \"lib-a\" }, { \"id\": \"lib-b\" }],\n  \"edges\": [\n    { \"from\": \"app\", \"to\": \"lib-a\" },\n    { \"from\": \"lib-a\", \"to\": \"lib-b\" }\n  ]\n}\n```\n\n### Required Fields\n\n| Field          | Type   | Description                                 |\n| -------------- | ------ | ------------------------------------------- |\n| `nodes[].id`   | string | Unique node identifier (displayed as label) |\n| `edges[].from` | string | Source node ID                              |\n| `edges[].to`   | string | Target node ID                              |\n\n### Optional Fields\n\n| Field                    | Type   | Description                                                        |\n| ------------------------ | ------ | ------------------------------------------------------------------ |\n| `nodes[].row`            | int    | Pre-assigned layer (computed automatically if omitted)             |\n| `nodes[].kind`           | string | Internal use: `\"subdivider\"` or `\"auxiliary\"`                      |\n| `nodes[].vuln_severity`  | string | Max vulnerability severity: `critical`, `high`, `medium`, or `low` |\n| `nodes[].meta`           | object | Freeform metadata for display features                             |\n\n### Recognized `meta` Keys\n\nThese keys are read by specific render flags. All are optional—missing keys simply disable the corresponding feature.\n\n| Key                 | Type          | Used By                                    |\n| ------------------- | ------------- | ------------------------------------------ |\n| `repo_url`          | string        | Clickable blocks, `--popups`, `--nebraska` |\n| `repo_stars`        | int           | `--popups`                                 |\n| `repo_owner`        | string        | `--nebraska`                               |\n| `repo_maintainers`  | []string      | `--nebraska`                               |\n| `repo_last_commit`  | string (date) | `--popups`, brittle detection              |\n| `repo_last_release` | string (date) | `--popups`                                 |\n| `repo_archived`     | bool          | `--popups`, brittle detection              |\n| `summary`           | string        | `--popups` (fallback: `description`)       |\n| `vuln_severity`     | string        | `--show-vulns` (severity colour coding)    |\n\n---\n\n## Troubleshooting\n\n| Symptom | Cause | Fix |\n| --- | --- | --- |\n| `rate limited: too many requests` | GitHub/PyPI API rate limit exceeded | Set `GITHUB_TOKEN`; use `--no-cache` sparingly |\n| `librsvg` / `rsvg-convert` errors | Missing system dependency for PDF/PNG | Install librsvg: `brew install librsvg` (macOS), `apt install librsvg2-bin` (Linux) |\n| Very slow for large graphs | Graph exceeds default limits | Lower `--max-nodes` or `--max-depth`; use `--ordering barycentric` for faster layout |\n| `context deadline exceeded` | Ordering search timeout | Increase `--ordering-timeout` or switch to `--ordering barycentric` |\n| No colours in output | Terminal doesn't support ANSI or `NO_COLOR` is set | Unset `NO_COLOR`; check terminal supports 256 colours |\n\n## Exit Codes\n\nStacktower uses stable exit codes for automation and CI:\n\n| Code | Meaning |\n| --- | --- |\n| `0` | Success |\n| `1` | Runtime/system failure (network, registry/API, render/pipeline errors) |\n| `2` | Invalid usage or input (unsupported language, invalid package/manifest arguments) |\n| `3` | New vulnerabilities detected (`diff --fail-on-vuln`) |\n| `130` | Interrupted (`Ctrl+C` / termination signal) |\n\n## How It Works\n\n1. **Parse** — Fetch package metadata from registries or local manifest files\n2. **Scan** _(optional)_ — Query OSV.dev for known vulnerabilities and annotate nodes by severity\n3. **Analyze** _(optional)_ — Trace dependency paths (`why`), compute health stats (`stats`), diff graphs (`diff`), or export SBOM (`sbom`)\n4. **Reduce** — Remove transitive edges to show only direct dependencies\n5. **Layer** — Assign each package to a row based on its depth\n6. **Order** — Minimize edge crossings using branch-and-bound with PQ-tree pruning\n7. **Layout** — Compute block widths proportional to downstream dependents\n8. **Render** — Generate output in SVG, JSON, PDF, or PNG format, with vulnerability colours when available\n\nThe ordering step is where the magic happens. Stacktower uses an optimal search algorithm that guarantees minimum crossings for small-to-medium graphs. For larger graphs, it gracefully falls back after a configurable timeout.\n\n## Environment Variables\n\n| Variable              | Description                                                      |\n| --------------------- | ---------------------------------------------------------------- |\n| `GITHUB_TOKEN`        | GitHub API token for metadata enrichment                         |\n| `XDG_CACHE_HOME`      | Override default cache directory (`~/.cache`)                    |\n| `NO_COLOR`            | Disable colour output (see https://no-color.org)                 |\n\n## Using as a Library\n\nStacktower can be used as a Go library for programmatic graph visualization.\n\n```go\nimport (\n    \"github.com/stacktower-io/stacktower/pkg/core/dag\"\n    \"github.com/stacktower-io/stacktower/pkg/core/dag/transform\"\n    \"github.com/stacktower-io/stacktower/pkg/core/render/tower/layout\"\n    \"github.com/stacktower-io/stacktower/pkg/core/render/tower/sink\"\n)\n\n// Build a graph\ng := dag.New(nil)\ng.AddNode(dag.Node{ID: \"app\", Row: 0})\ng.AddNode(dag.Node{ID: \"lib\", Row: 1})\ng.AddEdge(dag.Edge{From: \"app\", To: \"lib\"})\n\n// Normalize and render\n_, _ = transform.Normalize(g)\nl := layout.Build(g, 800, 600)\nsvg := sink.RenderSVG(l, sink.WithGraph(g), sink.WithPopups())\n```\n\n📚 **[Full API documentation on pkg.go.dev](https://pkg.go.dev/github.com/stacktower-io/stacktower)**\n\nKey packages:\n\n- [`pkg/core/dag`](https://pkg.go.dev/github.com/stacktower-io/stacktower/pkg/core/dag) — DAG data structure and crossing algorithms\n- [`pkg/core/dag/transform`](https://pkg.go.dev/github.com/stacktower-io/stacktower/pkg/core/dag/transform) — Graph normalization pipeline\n- [`pkg/core/render/tower`](https://pkg.go.dev/github.com/stacktower-io/stacktower/pkg/core/render/tower) — Layout, ordering, and rendering\n- [`pkg/core/deps`](https://pkg.go.dev/github.com/stacktower-io/stacktower/pkg/core/deps) — Dependency resolution from registries\n- [`pkg/pipeline`](https://pkg.go.dev/github.com/stacktower-io/stacktower/pkg/pipeline) — Complete parse → layout → render pipeline\n- [`pkg/security`](https://pkg.go.dev/github.com/stacktower-io/stacktower/pkg/security) — Vulnerability scanning via OSV.dev\n- [`pkg/sbom`](https://pkg.go.dev/github.com/stacktower-io/stacktower/pkg/sbom) — SBOM generation (CycloneDX, SPDX)\n\n## Contributing\n\nSee [CONTRIBUTING.md](CONTRIBUTING.md) for guidelines on adding new languages, manifest parsers, or output formats.\n\n## Development\n\n```bash\nmake install-tools  # Install required tools (golangci-lint, goimports, govulncheck)\nmake check          # Run all CI checks locally (fmt, lint, test, vuln)\nmake build          # Build binary to bin/stacktower\n```\n\n| Command         | Description                                |\n| --------------- | ------------------------------------------ |\n| `make check`    | Format, lint, test, vulncheck (same as CI) |\n| `make fmt`      | Format code with gofmt and goimports       |\n| `make lint`     | Run golangci-lint                          |\n| `make test`     | Run tests with race detector               |\n| `make cover`    | Run tests with coverage report             |\n| `make vuln`     | Check for known vulnerabilities            |\n| `make e2e`      | Run end-to-end tests                       |\n| `make snapshot` | Build release locally (no publish)         |\n\nCommit messages follow [Conventional Commits](https://www.conventionalcommits.org/).\n\n## Learn More\n\n- 📖 **[stacktower.io](https://www.stacktower.io)** — Interactive examples and the full story behind tower visualizations\n- 🐛 **[Issues](https://github.com/stacktower-io/stacktower/issues)** — Bug reports and feature requests\n\n## License\n\nApache-2.0\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fstacktower-io%2Fstacktower","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fstacktower-io%2Fstacktower","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fstacktower-io%2Fstacktower/lists"}