NORMAL ~/wkd/p/github_stats/help :set skin=modern utf-8

github_stats.txt

GitHub Traffic Statistics Collector for Neovim — github_stats.nvim

doc/github_stats.txt — rendered from the plugin's own vimdoc

*github_stats.txt*  GitHub Traffic Statistics Collector for Neovim
                                                           *github_stats.nvim*

CONTENTS *github_stats-contents*

    1. Introduction ........................... |github_stats-introduction|
    2. Requirements ........................... |github_stats-requirements|
    3. Installation ........................... |github_stats-installation|
    4. Configuration .......................... |github_stats-configuration|
    5. Commands ............................... |github_stats-commands|
    6. Dashboard UI ........................... |github_stats-dashboard|
    7. Visualization .......................... |github_stats-visualization|
    8. Export ................................. |github_stats-export|
    9. Diff ................................... |github_stats-diff|
   10. Date Presets ........................... |github_stats-date-presets|
   11. API .................................... |github_stats-api|
   12. Storage ................................ |github_stats-storage|
   13. Digest ................................. |github_stats-digest|
   14. Troubleshooting ........................ |github_stats-troubleshooting|

INTRODUCTION *github_stats-introduction*

GitHub Stats is a Neovim plugin for automatic collection and analysis of
GitHub repository traffic statistics.

Features:

  • Silent background data collection (session-persistent, no messages
    unless something actually fails)
  • Auto-discovered repos: track every public repo of a GitHub user with
    one line (watch_users), on top of any explicitly listed repos
  • Flexible configuration (setup() or config.json)
  • Historical JSON storage with custom paths
  • Smart date defaults in commands
  • Time-range based analytics
  • ASCII visualizations (sparklines, charts)
  • Export to CSV and Markdown
  • Period comparison (diff mode)
  • Async-first (no UI blocking)
  • Floating windows for results
  • Cross-platform (Windows, macOS, Linux)

REQUIREMENTS *github_stats-requirements*

• Neovim >= 0.10.0 (vim.uv is used unguarded)
• lib.nvim (required: notify, fs.json, net.curl, usercmd composer,
  cross.executable)
• curl (for API requests)
• GitHub Personal Access Token with repo permission

Optional:

• pdfport.nvim for .pdf export
• nvzone/menu for the dashboard's right-click menu

Create token:

  1. https://github.com/settings/tokens
  2. "Generate new token (classic)"
  3. Select repo scope
  4. Save token securely

INSTALLATION *github_stats-installation*

When to use which:

    Default (lazy)        Minimal startup impact, loads on first command use
    lazy = false           Loads immediately at startup
    event = "VimEnter"      Loads after UI init (RECOMMENDED - matches the
                            plugin's own auto-fetch/dashboard auto-open timing)

With lazy.nvim (recommended):

    {
      "StefanBartl/github_stats.nvim",
      dependencies = { "StefanBartl/lib.nvim" },
      event = "VimEnter",
      config = function()
        require("github_stats").setup({
          repos = { "user/repo1", "user/repo2" },
        })
      end,
    }

With lazy.nvim (eager):

    {
      "StefanBartl/github_stats.nvim",
      dependencies = { "StefanBartl/lib.nvim" },
      lazy = false,
      config = function()
        require("github_stats").setup({
          repos = { "user/repo1", "user/repo2" },
        })
      end,
    }

With packer.nvim:

    use {
      "StefanBartl/github_stats.nvim",
      requires = { "StefanBartl/lib.nvim" },
      config = function()
        require("github_stats").setup({
          repos = { "user/repo1", "user/repo2" },
        })
      end,
    }

CONFIGURATION *github_stats-configuration*

Two configuration methods are supported:

Option A: Direct Setup (Recommended)

    require("github_stats").setup({
      repos = { "username/repo1", "username/repo2" },
      token_source = "env",           -- or "file"
      token_env_var = "GITHUB_TOKEN",
      fetch_interval_hours = 24,
      notification_level = "all",     -- "all", "errors", "silent"
      progress_style = "auto",        -- indicator while a manual fetch runs
    })

Option B: Config File

Create: ~/.config/nvim/lua/plugins/github-stats/config.json
    {
      "repos": ["username/repo1", "username/repo2"],
      "token_source": "env",
      "token_env_var": "GITHUB_TOKEN",
      "fetch_interval_hours": 24,
      "notification_level": "all"
    }
Then in init.lua:
    require("github_stats").setup()  -- Reads from config.json

Why Option B?

If you sync Neovim config across systems via Git, using config.json in
stdpath('config') allows:
  • Same historical data across all systems
  • Consistent repository lists
  • Everything in one backup-able location

See docs/configurations/INTRO.md for detailed guide.

Configuration Options:

repos                   List of individually tracked repositories
                        (format: "owner/repo")
                        Type: string[]
                        Required: only if watch_users is not set

watch_users             GitHub usernames whose public repos are
                        auto-discovered and tracked in addition to repos
                        Type: string[]
                        Default: {}

background.enabled      Master switch for the silent background
                        fetch/discovery cycle. When false, only manual
                        :GithubStats fetch ever fetches data.
                        Type: boolean
                        Default: true

token_source            Token source ("env" or "file")
                        Type: string
                        Default: "env"

token_env_var           Environment variable name
                        Type: string
                        Default: "GITHUB_TOKEN"

token_file              Path to token file (when token_source="file")
                        Type: string
                        Example: "~/.github_token"

fetch_interval_hours    Hours between automatic fetches
                        Type: number
                        Default: 24

notification_level      Notification verbosity
                        Type: "all" | "errors" | "silent"
                        Default: "all"

progress_style          Progress indicator while a fetch is in flight.
                        Type: "auto" | "notify" | "statusline" | "fidget"
                              | "float" | "kit"
                        Default: "auto"

                        A full fetch is four API calls per repository, so
                        over a dozen-plus repositories it can run for a
                        while with no output of its own.

                        Requires lib.nvim, which provides the indicator
                        via its lib.nvim.progress module. Without
                        lib.nvim installed this option is silently a
                        no-op and fetching behaves exactly as before.

                        Only manual fetches (:GithubStats fetch, the
                        dashboard refresh keys) show an indicator — the
                        silent background cycle never does, matching how
                        it already suppresses info notifications.

                        "auto" prefers fidget.nvim when installed and
                        falls back to vim.notify. "statusline" draws
                        nothing and instead publishes the text for your
                        own statusline to read via
                        `require("lib.nvim.progress.styles.statusline")
                        .active()`.

config_dir              Custom config directory (advanced)
                        Type: string
                        Default: stdpath('config') .. '/lua/plugins/github-stats'

data_dir                Custom data directory (advanced)
                        Type: string
                        Default: config_dir .. '/data'

digest_dir              Where the per-repository digest for other programs
                        is written (see |github_stats-digest|). Local to one
                        machine on purpose: set it in setup(), not in the
                        synced config.json.
                        Type: string
                        Default: stdpath('data') .. '/github_stats.nvim'

digest_daily_days       Days of daily values each digest keeps per metric.
                        Type: integer
                        Default: 400

Custom Storage Paths Example:

    require("github_stats").setup({
      repos = { "username/repo" },
      config_dir = "~/my-github-stats",
      data_dir = "/mnt/nas/github-data",
    })

Background Fetching & Auto-Discovered Repos:

By default a silent background cycle starts on VimEnter and stays active
for the whole session (not just a one-shot startup check). It periodically
checks whether a fetch is due (per fetch_interval_hours) and fetches
quietly: no notification on success. Real failures (bad token, network,
rate limit) still notify, subject to notification_level.

Track every public repo of a GitHub user with one line:
    require("github_stats").setup({
      repos = { "username/some-other-repo" },  -- still tracked individually
      watch_users = { "username" },            -- all public repos of this user
    })
watch_users is re-resolved every background cycle (new repos show up
automatically) and is additive to repos. Repos you lack push access to
simply never populate data, silently.

Disable background fetching entirely:
    require("github_stats").setup({
      repos = { "username/repo" },
      background = { enabled = false },  -- only manual :GithubStats fetch runs
    })

Token Setup:

Environment Variable:
    # In ~/.bashrc, ~/.zshrc, etc.
    export GITHUB_TOKEN="ghp_your_token_here"
Token File:
    echo "ghp_your_token_here" > ~/.github_token
    chmod 600 ~/.github_token
Then in setup:
    require("github_stats").setup({
      repos = { "username/repo" },
      token_source = "file",
      token_file = "~/.github_token",
    })

COMMANDS *github_stats-commands*

One command, :GithubStats <subcommand> (built via lib.nvim.bindings.usercmd.composer,
with <Tab> completion at every level -- subcommand name, then each
positional argument; repo names and date presets complete dynamically from
live config). Bang attaches to the VERB, not the subcommand: :GithubStats!
dashboard (not :GithubStats dashboard!). See |github_stats-dashboard| for
the dashboard subcommand.
                                                             *:GithubStats*
                                                      *:GithubStats-fetch*
:GithubStats fetch [force]
    Fetches statistics for all configured repositories.

    Optional: force - Ignores interval and forces immediate fetch

    Autocompletion: force

Examples:

        :GithubStats fetch           " Respects 24h interval
        :GithubStats fetch force     " Immediate fetch

                                                       *:GithubStats-show*
:GithubStats show {repo} {metric} [start_date] [end_date]
    Shows detailed statistics for a repository and metric.

Parameters:

        {repo}        Repository in "owner/repo" format
        {metric}      "clones" or "views"
        [start_date]  Optional start (ISO: YYYY-MM-DD)
        [end_date]    Optional end (ISO: YYYY-MM-DD, default: today)

Smart Defaults:

        No start_date → All available data
        No end_date   → Today's date
        Plugin notifies about applied defaults

    Autocompletion: Repository names, metrics

Examples:

        :GithubStats show username/repo clones
        :GithubStats show username/repo views 2025-01-01
        :GithubStats show username/repo clones 2025-01-01 2025-12-31

                                                    *:GithubStats-summary*
:GithubStats summary {metric}
    Shows summary across all configured repositories.

Parameters:

        {metric}      "clones" or "views"

    Note: Does not accept date parameters (shows entire history)

    Autocompletion: Metrics

Examples:

        :GithubStats summary clones
        :GithubStats summary views

                                                  *:GithubStats-referrers*
:GithubStats referrers {repo} [limit]
    Shows top referrers for a repository.

Parameters:

        {repo}        Repository in "owner/repo" format
        {limit}       Number of results (default: 10)

    Autocompletion: Repository names

Examples:

        :GithubStats referrers username/repo
        :GithubStats referrers username/repo 20

                                                      *:GithubStats-paths*
:GithubStats paths {repo} [limit]
    Shows most visited paths in a repository.

Parameters:

        {repo}        Repository in "owner/repo" format
        {limit}       Number of results (default: 10)

    Autocompletion: Repository names

Examples:

        :GithubStats paths username/repo
        :GithubStats paths username/repo 20

                                                      *:GithubStats-chart*
:GithubStats chart {repo} {metric} [start_date] [end_date]
    Displays ASCII sparkline chart for traffic data.

Parameters:

        {repo}        Repository in "owner/repo" format
        {metric}      "clones", "views", or "both" (comparison)
        [start_date]  Optional start (ISO: YYYY-MM-DD)
        [end_date]    Optional end (ISO: YYYY-MM-DD, default: today)

Smart Defaults:

        No start_date → All available data
        No end_date   → Today's date

    Autocompletion: Repository names, metrics

Examples:

        :GithubStats chart username/repo clones
        :GithubStats chart username/repo both
        :GithubStats chart username/repo views 2025-01-01 2025-12-31

                                                     *:GithubStats-export*
:GithubStats export {repo|all} {metric} {filepath}
    Exports data to CSV, Markdown, or PDF format.

Parameters:

        {repo|all}    Repository or "all" for summary
        {metric}      "clones", "views", or "both" (combined report)
        {filepath}    Output file (.csv, .md, or .pdf extension)

Format Support:

        CSV:      Single repository only
        Markdown: Single repository or all repositories
        PDF:      Single repository or all repositories (same content as
                  Markdown; requires pdfport.nvim, see below)

PDF export (optional dependency):

        Routes through pdfport.nvim (github.com/StefanBartl/pdfport.nvim,
        soft dependency, pcall-guarded): the exact same report
        export_markdown()/export_summary_markdown()/etc. would write to a
        .md file is instead handed to pdfport.create() as text (no
        intermediate .md file). Needs pdfport.nvim installed and a
        markdown producer available (pandoc + a PDF engine) --
        pdfport.can_create("markdown"). Without pdfport.nvim installed,
        or without an available producer, the export fails with a clear
        error instead of silently falling back to another format.

Extension Defaulting:

        If {filepath} has no extension at all, one is appended
        automatically: ".md" for the "all" target (the only format it
        supports by default), ".csv" otherwise. A path that already has
        some other extension (e.g. ".txt") is left alone and still
        errors, since silently rewriting a deliberately-named path would
        be more surprising than helpful.

Parent Directories:

        Created automatically if they don't exist yet.

Combined Metric ("both"):

        Merges clones and views into a single report instead of two
        separate ones -- one CSV row (or Markdown table row) per date,
        with both metrics side by side. "all" + "both" produces one
        combined Markdown/PDF summary across every configured repository.
        Summary reports (target "all", or a single-repo report) also
        include a "## Highlights" section: most cloned/viewed repository,
        best month, and best single day.

    Autocompletion: Repository names, metrics, file paths

Examples:

        :GithubStats export username/repo clones ~/data.csv
        :GithubStats export username/repo views ~/report.md
        :GithubStats export username/repo clones ~/report.pdf
        :GithubStats export username/repo both ~/combined.csv
        :GithubStats export all clones ~/summary.md
        :GithubStats export all clones ~/summary.pdf
        :GithubStats export all both ~/summary.md
        :GithubStats export username/repo clones ~/reports/repo   " -> ~/reports/repo.csv

                                                       *:GithubStats-diff*
:GithubStats diff {repo} {metric} {period1} {period2}
    Compares metrics between two time periods.

Parameters:

        {repo}        Repository in "owner/repo" format
        {metric}      "clones" or "views"
        {period1}     First period (YYYY-MM or YYYY)
        {period2}     Second period (YYYY-MM or YYYY)

Period Formats:

        YYYY-MM: Single month (e.g., 2025-01)
        YYYY:    Full year (e.g., 2025)

    Autocompletion: Repository names, metrics, period suggestions

Examples:

        :GithubStats diff username/repo clones 2025-01 2025-02
        :GithubStats diff username/repo views 2024 2025

                                                    *:GithubStats-compact*
:GithubStats compact [dry-run]
    Archives old clones/views data and prunes stale referrers/paths
    snapshots. Runs automatically at most once per 24h after a fetch;
    this command runs it on demand.

    Optional: dry-run - Reports would-be archived/deleted counts and
    freed bytes without touching disk

    Autocompletion: dry-run

    Governed by: retention.enabled, retention.cutoff_days (floor 14),
    retention.prune_days

Examples:

        :GithubStats compact dry-run
        :GithubStats compact

                                                     *:GithubStats-digest*
:GithubStats digest
    Rebuilds, now, the per-repository digest that other programs read
    (see |github_stats-digest|). It is written automatically after every
    fetch and whenever the synced history is newer than it; use this after
    a sync from your other machine. Reports how many digests were written,
    were unchanged, or had no history, and where they went.

Examples:

        :GithubStats digest

                                                      *:GithubStats-debug*
:GithubStats debug
    Shows debug information and tests API connectivity.

Information Shown:

        • Configuration status
        • Token availability
        • Last fetch summary with detailed errors
        • Test API call for first repository

Use when:

        • After initial setup
        • Troubleshooting errors
        • Before opening support issues

                                                      *:checkhealth-github_stats*
:checkhealth github_stats
    Performs comprehensive diagnostics.

Checks:

        • Configuration validity
        • Token access
        • curl availability (cross-platform)
        • Storage paths
        • Digest: options, writable directory, root.json, lag behind the history
        • API connectivity (with timeout)

DASHBOARD *github_stats-dashboard*

The dashboard provides an interactive TUI for monitoring multiple repositories
simultaneously with real-time statistics and visual trend indicators.

Opening the Dashboard:

                                                   *:GithubStats-dashboard*
    :GithubStats dashboard       Open dashboard
    :GithubStats! dashboard      Open with forced refresh

Dashboard Layout:

    ┌─────────────────────────────────────────────────────────┐
    │ GitHub Stats Dashboard            [?] Help  [q] Quit    │
    ├─────────────────────────────────────────────────────────┤
    │ Overall: 3 repos | 12,345 clones | 5,678 views         │
    │ Last update: 2025-12-23 10:30:00                        │
    ├─────────────────────────────────────────────────────────┤
    │ ▶ username/repo1                  ⬆ +15% ↗             │
    │   Clones: 1,234 | Views: 5,678 | Referrers: 45         │
    │   ▂▃▅▇█▇▅▃▂▁▂▃▅▇█▇▅▃▂▁                                  │
    └─────────────────────────────────────────────────────────┘

Key Bindings:

Navigation:

  j, <Down>       Navigate to next repository
  k, <Up>         Navigate to previous repository
  <Enter>         Show detailed statistics for selected repository

Actions:

  r               Drop the in-memory read cache and re-render from disk
                  (no API call); see |github_stats-dashboard-caching|
  R               Force-fetch all repositories from GitHub (bypass interval)
  f               Force-fetch selected repository from GitHub (bypass interval)
  s               Cycle sort criteria (clones/views/name/trend)
  t               Cycle time range (7d/30d/90d/max)
  T               Enter a custom time range (e.g. "14d", "3m", "1y",
                  "since:2025-01-01", or any date_presets name)
  m               Set the maximum time range: the longest duration the
                  stored data covers, notifying the resolved span

Other:

  ?               Toggle help overlay
  q, <Esc>        Quit dashboard

Configuration:

dashboard.enabled               (boolean)
    Enable dashboard functionality.
    Default: true

dashboard.auto_open             (boolean)
    Automatically open dashboard on VimEnter.
    Default: false

dashboard.refresh_interval_seconds  (integer)
    While the dashboard is open, re-render it every N seconds. This
    re-renders only; it never fetches, so an open dashboard cannot burn
    the API rate limit (see |github_stats-dashboard-auto-refresh|).
    Set to 0 to disable. A non-number value is ignored.
    Default: 300 (5 minutes)

dashboard.trend_window_days     (integer)
    Days per trend comparison window: each entry's arrow compares the last
    N complete days against the N before them, the same N whatever range is
    displayed. See |github_stats-dashboard-trend|.
    Default: 7

dashboard.sort_by               (string)
    Default sort criteria: "clones", "views", "name", or "trend".
    Default: "clones"

dashboard.time_range            (string)
    Default time range: "7d", "30d", "90d", "max"/"all", or any expression
    accepted by the 'T' custom time range prompt (e.g. "14d", "3m", "1y",
    "since:2025-01-01", a date_presets name). See
    |github_stats-time-range-expressions|.
    Default: "30d"

dashboard.keybindings           (table)
    Customize keybindings. See |github_stats-dashboard-keybindings|

Example Configuration:

    require("github_stats").setup({
      repos = { "user/repo1", "user/repo2" },
      dashboard = {
        enabled = true,
        auto_open = false,
        refresh_interval_seconds = 300,
        sort_by = "clones",
        time_range = "30d",
        trend_window_days = 7,
        keybindings = {
          navigate_down = "j",
          navigate_up = "k",
          show_details = "<CR>",
          refresh_selected = "r",
          refresh_all = "R",
          force_refresh = "f",
          cycle_sort = "s",
          cycle_time_range = "t",
          custom_time_range = "T",
          max_time_range = "m",
          show_help = "?",
          quit = "q",
        },
      },
    })

Features:

Repository Cards:

  Each repository is displayed with:
  • Current statistics (clones, views, referrers)
  • Trend indicator (↑↓ with percentage change)
  • Mini sparkline showing recent activity
  • Selection indicator (▶) for current repository

Overall Summary:

  Displays aggregate statistics:
  • Total number of configured repositories
  • Combined clones and views across all repos
  • Timestamp of last data refresh

Visual Indicators:

  ⬆ +15%    Growing (positive trend)
  ⬇ -8%     Declining (negative trend)
  ⬌ 0%      Stable (no change)
  ⬌ n/a     Neither comparison window holds any data

                                        *github_stats-dashboard-caching*

Caching:

  Stored metrics are read from disk once and kept in memory until something
  changes them. Without that, every keypress re-read and re-decoded the
  entire stored history of every configured repository.

  The cache is dropped when a fetch writes, when a retention run archives
  or prunes, and when 'r' is pressed. There is no time-based expiry, so
  nothing serves stale data because a timer had not fired yet, and nothing
  re-reads because one did.

  'r' is therefore how to pick up a change this Neovim did not make
  itself -- a fetch from another window, or another Neovim entirely.

                                        *github_stats-dashboard-highlights*

Colours:

  The dashboard defines named highlight groups and links them to stock
  groups with default = true, so colours follow the user's colourscheme
  and a single :hi link overrides them permanently:
    GithubStatsHeader       -> Title
    GithubStatsTotals       -> MoreMsg
    GithubStatsStatus       -> Comment
    GithubStatsKeyHint      -> Comment
    GithubStatsRepo         -> Identifier
    GithubStatsSelected     -> PmenuSel
    GithubStatsLabel        -> Comment
    GithubStatsValue        -> Number
    GithubStatsSparkline    -> Special
    GithubStatsTrendUp      -> DiagnosticOk
    GithubStatsTrendDown    -> DiagnosticError
    GithubStatsTrendFlat    -> Comment
    GithubStatsSeparator    -> NonText
  Example override:
    vim.api.nvim_set_hl(0, "GithubStatsTrendUp", { link = "DiffAdd" })

Totals:

  The header's second line summarises every configured repository over the
  active range, and names the one with the most clones:
    2 repos  2,540 clones  2,540 views  top:user/a
  It follows the time range, so "max" gives all-time totals. No top
  repository is named while nothing has been cloned.

Sparklines:

  Each entry's "Period:" line ends with a sparkline of daily clones over
  the active range, sampled to a fixed width so a longer range compresses
  rather than widening the entry:
    Period:  2026-08-05 to 2026-08-24  ▆▂▆▂▅▁▅▁▄█▃▇▃▆▂▆▂▅▁▅
  A repository with no data in range shows no sparkline.

                                        *github_stats-dashboard-trend*

Trend:

  The arrow compares the last dashboard.trend_window_days complete days
  (default 7) against the same number of days before them. The comparison
  is fixed: it does not follow the displayed range, so "+40%" means the
  same thing at Range:7d and at Range:max, and sorting by trend orders
  numbers that are actually comparable. The window is named in the header
  as "Trend:7d/7d".

  It is measured back from yesterday, not today: today's data is always
  incomplete and is excluded from every aggregation, so anchoring on today
  would compare six days against seven and invent a decline.

  "n/a" means neither window holds data -- a different statement from
  "0%", which means genuinely flat. Such entries sort below all others
  under sort_by = "trend".

Sorting:

  Press 's' to cycle through sort options:
  • clones   - Sort by total clone count
  • views    - Sort by total view count
  • name     - Sort alphabetically
  • trend    - Sort by percentage change

Time Ranges:

  Press 't' to cycle through the fixed quick-access ranges:
  • 7d       - Last 7 days
  • 30d      - Last 30 days
  • 90d      - Last 90 days
  • max      - The maximum duration the stored data covers

  Press 'm' to jump straight to "max" without stepping through the cycle.
  It also notifies the window it resolved to, e.g.
  "max (2025-03-04 to 2026-08-22, 172 days)".

  Press 'T' to type an arbitrary range instead, via a prompt pre-filled
  with the current value. See |github_stats-time-range-expressions| for
  every accepted form.

  Whatever range is active, the header status line appends the window it
  actually resolved to, or "(no data)" when nothing is stored yet:
    Sort:clones   Range:max (2025-03-04 -> 2026-08-22, 172 days)
                                        *github_stats-time-range-expressions*

Time Range Expressions:

  Both the 'T' dashboard prompt and any time_range field accept:
  • "all" / "max"           - no filtering, every available day; identical
                              in effect, "max" is the label the 't' cycle
                              and the 'm' key produce
  • "Nd" / "Nw"             - N days / N weeks back from today
  • "Nm" / "Ny"             - N calendar months / years back; the day is
                              clamped to the target month's length, so one
                              month back from the 31st lands on the 28th,
                              29th or 30th rather than rolling forward
  • "since:YYYY-MM-DD"      - that date through today
  • "YYYY-MM-DD"            - same as "since:YYYY-MM-DD"
  • any |github_stats-date-presets| name, built-in or user-custom
    (e.g. "this_month", "this_year", "last_quarter", a custom preset)

  Examples: "14d", "3m", "1y", "since:2025-01-01", "this_year".
  An unrecognized expression is rejected with an error notification and
  the previous range is kept.

                                        *github_stats-dashboard-auto-refresh*

Auto-Refresh:

  While the dashboard is open it re-renders itself every
  dashboard.refresh_interval_seconds seconds (default 300, 0 disables).

  It re-renders; it never fetches. The GitHub traffic API is a rolling
  14-day window updated once a day, so polling it every few minutes would
  spend rate limit on data that cannot have changed. Fetching stays the
  job of 'R', 'f', and the fetch_interval_hours gate. What the periodic
  re-render does pick up is anything that reached disk meanwhile: a
  background fetch, a :GithubStats fetch from another window, or a
  retention run.

  The timer is stopped and closed when the dashboard closes, on the same
  teardown path as everything else.

See also:

  |github_stats-commands|
  |github_stats-configuration|

VISUALIZATION *github_stats-visualization*

The visualization module provides ASCII charts and sparklines for traffic
data visualization directly in Neovim.

Sparklines:

  Unicode block characters (▁▂▃▄▅▆▇█) represent data points.
  Normalized to show relative values across the range.

Bar Charts:

  Horizontal bars with numeric labels.
  Useful for comparing multiple items (e.g., referrers, paths).

Comparison Charts:

  Side-by-side sparklines for count vs uniques.
  Shows both metrics in a single view.

Example Output:

    GitHub Stats: username/repo/clones
    ────────────────────────────────────────────────────────────────

    ▂▃▅▇█▇▅▃▂▁▂▃▅▇█▇▅▃▂▁▂▃▅▇█▇▅▃▂▁▂▃▅▇█▇▅▃▂▁▂▃▅▇█▇▅▃▂▁▂▃▅▇█▇▅▃▂▁

    Period: 2025-11-20 to 2025-12-20 (30 days)
    Max: 1,234 | Avg: 567 | Min: 123 | Total: 17,010

EXPORT *github_stats-export*

The export module supports CSV and Markdown formats for external analysis
and documentation.

CSV Format:

    repository,metric,date,count,uniques
    username/repo,clones,2025-12-20,45,12
    username/repo,clones,2025-12-21,52,15

Markdown Format:

  - Formatted tables with daily breakdown
  - Summary statistics (max, avg, min, total)
  - Recent values highlighted
  - Suitable for documentation and reports

Summary Export:

  When using "all" as repository target with Markdown format, creates
  a comprehensive report across all configured repositories with:
  - Overview table
  - Detailed sections per repository
  - Recent data trends

DIFF *github_stats-diff*

The diff module provides period-over-period comparison to analyze trends
and growth.

Period Formats:

  YYYY-MM       Monthly comparison (e.g., 2025-01)
  YYYY          Yearly comparison (e.g., 2025)

Calculated Metrics:

  - Total count and uniques per period
  - Number of days with data
  - Average per day
  - Percentage change

Example Output:

    Period Comparison: username/repo - clones
    ══════════════════════════════════════════════════════════════════

    Period 1: 2025-01
      Total Count:   1,234
      Total Uniques: 567
      Days:          31
      Avg/Day:       39 count, 18 uniques

    Period 2: 2025-02
      Total Count:   1,423
      Total Uniques: 645
      Days:          28
      Avg/Day:       50 count, 23 uniques

    Changes:
    ──────────────────────────────────────────────────────────────────
      Count:   +15.3%
      Uniques: +13.8%

DATE PRESETS *github_stats-date-presets*

Date range presets provide quick access to common time periods without
requiring full ISO date strings.

Built-in Presets:

  today           Current day only
  yesterday       Previous day
  last_week       7 days ago to today
  last_month      30 days ago to today
  last_quarter    90 days ago to today
  last_year       365 days ago to today
  this_week       Monday to today
  this_month      First of month to today
  this_quarter    Start of quarter to today
  this_year       January 1st to today

Where presets resolve:

Completion offers preset names in more slots than the code resolves them
in. Resolution happens only in analytics.parse_time_range, and only these
reach it:

    Dashboard T prompt          always
    :GithubStats chart, arg 3   only if the name contains "last" or looks
                                like Nd (chart.lua routes those to
                                time_range)
    :GithubStats show           never - the argument becomes start_date,
                                which accepts YYYY-MM-DD only
    :GithubStats diff           never - parse_period accepts YYYY-MM or
                                YYYY only

Where a name is not resolved it fails to parse as a date and becomes NO
filter, silently reporting the full stored history.

Usage with Commands:

    :GithubStats chart username/repo clones last_month
    :GithubStats show username/repo clones 2025-12-01   " ISO dates here

Custom Presets:

Custom presets are Lua functions that return two ISO date strings.
They must be added programmatically after setup().

Example Configuration:

    -- In init.lua
    require("github_stats").setup()

    local config = require("github_stats.config").get()

    -- Fiscal Year Preset (April 1 - March 31)
    config.date_presets.custom.fiscal_year = function()
      local now = os.date("*t")
      local fy_year = now.month >= 4 and now.year or now.year - 1
      local start_date = string.format("%04d-04-01", fy_year)
      local end_date = string.format("%04d-03-31", fy_year + 1)
      return start_date, end_date
    end

    -- Current Sprint (2-week periods)
    config.date_presets.custom.current_sprint = function()
      local now = os.time()
      local sprint_length = 14 * 86400
      local date_info = os.date("*t", now)
      local days_since_monday = (date_info.wday == 1) and 6 or (date_info.wday - 2)
      local monday = now - (days_since_monday * 86400)
      local sprint_start = monday - (monday % sprint_length)
      local sprint_end = sprint_start + sprint_length - 86400
      return os.date("%Y-%m-%d", sprint_start), os.date("%Y-%m-%d", sprint_end)
    end

Using Custom Presets:

    :GithubStats chart username/repo clones last_fiscal_year
    " fiscal_year / current_sprint resolve only at the dashboard T prompt

Configuration Options:

date_presets.enabled        (boolean)
    Whether date presets are enabled.
    Default: true

date_presets.builtins       (string[])
    List of enabled built-in preset names.
    Default: All built-in presets enabled

date_presets.custom         (table<string, function>)
    User-defined preset functions.
    Default: {}

Preset Function Requirements:

  • Must return two strings in ISO format (YYYY-MM-DD)
  • Start date must be before or equal to end date
  • Should handle edge cases (month/year boundaries)
  • Can use os.date(), os.time() for calculations

Autocompletion:

Tab-completion shows all available presets when entering date parameters,
including in the slots that do not resolve them (see above)
in commands that support date ranges:
  • |:GithubStats-show|
  • |:GithubStats-chart|
  • |:GithubStats-diff|

Example:

    :GithubStats show username/repo clones <Tab>
    " Shows: today, yesterday, last_week, ..., fiscal_year, current_sprint

Troubleshooting:

Preset not appearing:

  • Restart Neovim after adding custom preset
  • Verify preset added after setup() call
  • Check: :lua print(vim.inspect(require("github_stats.date_presets").list()))

"Unknown preset" error:

  • Check spelling of preset name
  • Ensure config.date_presets.enabled = true
  • Verify preset function is properly assigned

Wrong date calculation:

  • Use os.date("*t") for reliable date components
  • Account for timezone/DST if relevant
  • Test with different dates (month/year boundaries)

See also:

  |github_stats-commands|
  |github_stats-configuration|

For detailed examples and best practices:
  docs/configurations/USER-DEFINED-DATE-PRESETS.md

API *github_stats-api*

Lua API for advanced usage:

Setup:

    require("github_stats").setup({
      repos = { "username/repo" },
      -- Options (see |github_stats-configuration|)
    })

Modules:

    local config = require("github_stats.config")
    local api = require("github_stats.api")
    local storage = require("github_stats.storage")
    local fetcher = require("github_stats.fetcher")
    local analytics = require("github_stats.analytics")
    local visualization = require("github_stats.visualization")
    local export = require("github_stats.export")
    local diff = require("github_stats.diff")

Example - Programmatic Fetch:

    local fetcher = require("github_stats.fetcher")

    fetcher.fetch_all(true, function(summary)
      print("Success:", #summary.success)
      print("Errors:", vim.tbl_count(summary.errors))
    end)

Example - Data Query:

    local analytics = require("github_stats.analytics")

    local stats, err = analytics.query_metric({
      repo = "username/repo",
      metric = "clones",
      start_date = "2025-01-01",
      end_date = "2025-12-31",
    })

    if stats then
      print("Total clones:", stats.total_count)
    end

Example - Visualization:

    local visualization = require("github_stats.visualization")

    local sparkline = visualization.generate_sparkline({
      10, 15, 20, 25, 30, 25, 20, 15, 10
    })

    print(sparkline)  -- ▂▃▅▆█▆▅▃▂

STORAGE *github_stats-storage*

Data Structure:

    ~/.config/nvim/lua/plugins/github-stats/
    ├── config.json                    # User configuration (Option B)
    ├── last_fetch.json                # Interval tracking
    └── data/
        └── username_repo/
            ├── clones/
            │   ├── 2025-12-20T10-30-00.json
            │   └── 2025-12-21T10-30-00.json
            ├── views/
            ├── referrers/
            └── paths/

Custom Paths:

Default paths can be customized in setup():
    require("github_stats").setup({
      repos = { "username/repo" },
      config_dir = "~/my-github-stats",
      data_dir = "/mnt/nas/github-data",
    })

File Format (Example):

    {
      "timestamp": "2025-12-20T10:30:00Z",
      "data": {
        "count": 42,
        "uniques": 15,
        "clones": [
          {
            "timestamp": "2025-12-19T00:00:00Z",
            "count": 10,
            "uniques": 5
          }
        ]
      }
    }

Storage Management:

  • JSON-based (~2KB per data point)
  • Atomic writes (temp + rename)
  • Automatic directory creation
  • No automatic cleanup (manual when needed)

DIGEST *github_stats-digest*

Other programs (a desktop app, another Neovim plugin) can show this traffic
without a token and without re-implementing the plugin's rules: after each
fetch the plugin writes one small JSON file per repository, plus a pointer.
The file contract is docs/FEATURES/DIGEST.md.

Where:

    <stdpath('data')>/github_stats.nvim/root.json   pointer, always here
    <digest_dir>/digest/<owner_repo>.json           one file per repository
The history lives in the (synced) Neovim config; the digest is derived and
rewritten in place, so it is local to each machine and rebuilt from the
history whenever that is newer. root.json names where the digests really
are, so a reader needs no setting even when digest_dir is overridden.

Contents:

  • 7, 30 and 90 complete days of views and clones (count and uniques) and a
    7-day trend in percent
  • the daily series, oldest first, at most digest_daily_days days
  • GitHub's top 10 referrers and paths (absent = unknown, not "none")
  • generated (when built) and fetched (the data it was built from)

Never contained: the token, any path of yours.

Reading it from Lua:

    -- Probe this module, not require("github_stats"): the top-level module
    -- loads the dashboard, which needs ui.nvim.
    local dir = require("github_stats.digest").digest_dir()
Commands: |:GithubStats-digest|

TROUBLESHOOTING *github_stats-troubleshooting*

Problem: "Token error: GITHUB_TOKEN not set"

Solution:

    1. Set token: export GITHUB_TOKEN="ghp_..."
    2. Restart Neovim
    3. Or configure token_file in setup

Problem: "No data found for username/repo"

Possible Causes:

    1. Repository name in configuration incorrect (case-sensitive)
    2. No data fetched yet → :GithubStats fetch force
    3. Token lacks permission for repository

Problem: "Fetched X metrics, Y errors"

Solution:

    Run :GithubStats debug to see detailed error information for each
    failed repository/metric combination.

    Common error codes:
    • 404 Not Found    - Repository name incorrect or deleted
    • 403 Forbidden    - Token lacks permissions or rate limit
    • 401 Unauthorized - Invalid or expired token

Problem: "curl not found in PATH"

Solution:

    • Linux/Debian/Ubuntu: sudo apt install curl
    • macOS: brew install curl
    • Windows 10+: curl is included since build 1803
    • Windows (manual): https://curl.se/windows/

Problem: Autocompletion not working

Solution:

    1. Check Neovim version: >= 0.10.0 required
    2. Run: :checkhealth github_stats
    3. Verify configuration is loaded

Diagnostic Workflow:

    1. :checkhealth github_stats
    2. :GithubStats debug
    3. :messages
    4. Check config: ~/.config/nvim/lua/plugins/github-stats/config.json

For comprehensive troubleshooting guide, see:
    docs/troubleshooting.md

keys

j / k
next / previous line
gg / G
first / last line
⏎
open the line under the cursor
/
search the plugins
:
command line — Tab completes
:help x
vimdoc of a plugin (:e x = plugin page)
:ls · :log · :stack
plugin list · activity stream · dependency graph
:colo x
colorscheme
:set skin=
modern | tui
?
this help
esc
close