npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@chronicle.app/github

v0.5.0

Published

Chronicle plugin for your GitHub pull requests, issues, comments, stars, and gists

Readme

@chronicle.app/github

Read your GitHub activity through the GraphQL API: the pull requests and issues you opened, how issues and pull requests were resolved, your comments, the repositories you starred, your gists, and other people's replies to you.

Usage

chronicle extract github stars
chronicle extract github pull-requests commits --since 2025-01-01
chronicle extract github -t all

Each kind is one record type, named after the source (or with --type). Several kinds merge newest first. GitHub declares no default kinds: a bare chronicle extract github asks which, and outside a terminal says to name them or pass -t all. replies and resolutions are slow, so pick them knowingly.

  • repositories: repositories you created, forks included.
  • commits: commits you authored, on the default branch of every repository the token can see.
  • pull-requests: pull requests you opened.
  • issues: issues you opened.
  • comments: your comments on issues and pull requests.
  • resolutions: issues and pull requests you closed or merged, among your own and those in repositories you own, and what became of your pull requests, whoever decided it.
  • replies: other people's comments on your issues and pull requests, and on anyone else's issue or pull request, every comment after your first one there.
  • stars: repositories you starred, dated when you starred them.
  • gists: your gists, public and secret.

--since ends each walk once it passes older items. GitHub lists comments by when they were last edited, so comments stops at the first comment last edited before --since and keeps those written since then. resolutions walks closed issues and pull requests, most recently active first, and stops at the first quiet since --since; without it, it reads every closed issue in every repository you own, which takes a while (about a minute and a half for 64 repositories). replies is the slowest kind: it walks every thread you opened or commented on, because an old thread can get a new reply, and skips only threads with no activity since --since.

Activity in private repositories is included when the token can see it, marked visibility: private.

Credentials

The first of these found is used, and the run says which:

  1. --token.
  2. A token stored with chronicle auth set github.
  3. GH_TOKEN, then GITHUB_TOKEN.
  4. The gh CLI's login, from gh auth token. The token is only held in memory for the run, so refreshing or revoking it in gh takes effect on the next run. --no-gh turns this off.

gh's default scopes (repo, read:org, gist) cover everything. For a personal access token, public activity needs no scopes, private repositories need repo, and secret gists need gist. A missing scope stops the run with the scope to add: for a gh login, gh auth refresh -s <scope>.

Schema

| GitHub activity | Chronicle action | object | Other | | -------------------------------------------------- | --------------------------------- | ------------------------- | ---------------------------------- | | You opened an issue | PlanAction | Task | | | You opened a pull request | PublishAction | | result: the Changeset | | You or someone else commented | RespondAction | the Task or Changeset | result: the Comment | | An issue was closed as completed / not planned | CompleteAction / CancelAction | Task | | | A pull request was merged | AcceptAction | Changeset | | | A pull request was closed unmerged by someone else | RejectAction | Changeset | | | A pull request was closed unmerged by its author | CancelAction | Changeset | | | You created a repository | CreateAction | result: Repository | a fork isBasedOn its source | | You committed | UpdateAction | Repository | result: the Revision | | You starred a repository | LikeAction | Repository | | | You created a gist | PublishAction | | result: the SoftwareSourceCode |

| GitHub thing | Chronicle entity | Key | Properties | | ----------------------- | ----------------------------------------- | ---------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------- | | Repository | Repository | @type, source, creator[*].handle, handle | handle (its name, without the owner), name, url, description, references (homepage), tags (topics), creator (owner), visibility | | Issue | Task | @type, source, isPartOf[*].creator[*].handle, isPartOf[*].handle, handle | handle (number), name (title), description, url, isPartOf (repository) | | Pull request | Changeset | @type, source, isPartOf[*].creator[*].handle, isPartOf[*].handle, handle | handle (number), name (title), description, url, author, isPartOf (repository), visibility | | Commit | Revision | @type, sourceId (the hash, any source) | name (subject line), description (the rest of the message), url, author, isPartOf (repository and pull requests), isBasedOn (parent commits) | | Comment | Comment | @type, source, sourceId | body, url, author, about (its Task or Changeset), visibility | | Gist | SoftwareSourceCode | @type, source, sourceId | name, url, author, visibility | | User, organization, bot | Person, Organization, SoftwareAgent | @type, source, sourceId | sourceId (numeric id), handle (login), name, url |

SHAPES.md sketches every record type's output, generated from the tests.

Decisions

Accounts are keyed by their numeric id; a repository by its owner and name. An account's sourceId is GitHub's numeric id, the one in api.github.com/user/<id>, which survives a change of login; its login is the handle. A repository's handle is its own name (maloja in krateng/maloja), and it's keyed by that and its owner's handle, its creator's login: the owner/name in its URL and its git remotes, which other sources, such as a clone, can see without asking GitHub. A repository becomes a new node when it's renamed or transferred, or when its owner changes their login; GitHub redirects the old name, but the archive keeps both. A star is keyed the same way through its repository, since you can star a repository once.

Issues and pull requests are keyed by repository and number. The #12 in an issue's or pull request's URL is its handle, and with its repository it's the key: the name people use for it, which other sources can see in a URL or a commit message. Issues and pull requests share one sequence of numbers in a repository, so a number is never both. Like its repository, an issue or pull request becomes a new node when the repository's owner/name changes, or when an issue moves to another repository.

Everything else is keyed by its GitHub node id. Comments and resolutions have no number of their own, and the kinds of objects are numbered separately, so numeric ids could collide; node ids can't. The plugin asks for GitHub's next-generation ids, which are stable: without that, old objects answer with legacy ids GitHub is migrating away from. An action has no ID of its own, so it's keyed through what it acted on or made: a comment's RespondAction by the comment, an opened issue's PlanAction by the issue's repository and number, and a commit's UpdateAction by its hash. A resolution is the exception, since GitHub gives the event its own node id.

An issue is a Task. An issue is work to be done, filed against a repository, as a to-do item is filed in a project, so it's a Task that isPartOf its Repository, and its life follows a to-do's: opening it is a PlanAction, closing it as completed a CompleteAction, and closing it as not planned or a duplicate a CancelAction. A close from before GitHub recorded reasons counts as completed, as GitHub shows it. A task isn't a creative work, so who filed it is the agent of its PlanAction, and it carries no author or visibility of its own. A comment on a pull request is on the pull request, not on the issue GitHub keeps behind it.

A pull request is a Changeset. It's a set of changes to its repository, put forward as a unit and revised until it's decided, and it's neither an issue nor a commit: an issue asks for work, a commit is a point in the repository's history, and a pull request offers changes for its maintainers to take in or not. What became of it is told by the actions on it, not a stored state: opening it is a PublishAction, merging it an AcceptAction, and closing it unmerged a RejectAction, or a CancelAction when its own author withdrew it. GitHub records a merge as a close too; that close adds nothing.

Dates are on actions. When a pull request was opened or a gist created is the timestamp of the PublishAction that did it; the work carries no datePublished of its own.

An issue's, pull request's, or commit's own text is its description. That text says what the work is for, as a to-do's notes do. A body is the work's content: a comment's text is its body, and a Changeset's body is the text it adds, which GitHub doesn't give.

A commit is a Revision. It's a version of its repository, not a change: git stores each commit as the whole tree, and the same change on another base is another commit, which is what a rebase makes. The change it made is the difference from the commits it isBasedOn. It's keyed by its hash with no source, so a clone read by the git plugin and GitHub's view of it are one node. It isPartOf its repository and the pull requests it came in through, and a merge's AcceptAction has the merge commit as its result. Committing is an UpdateAction on the repository. commits uses GitHub's commit search, which finds your commits across repositories newest first; it reads default branches only, so commits only on other branches aren't read. Search answers at most a thousand results per query, so the walk restarts from the oldest date it reached, and it allows thirty searches a minute, so a full history (a hundred commits a search) waits out the limit rather than failing.

Resolutions are yours, or of your pull requests. resolutions reads closed issues and pull requests among your own and those in repositories you own. It keeps the ones you resolved, and every resolution of a pull request you opened, since "my pull request was merged" is someone else's action on your work. Someone else closing an issue isn't. An issue closed, reopened, and closed again is closed twice.

Replies stop at what's addressed to you. On your own issues and pull requests, everything others wrote counts. Elsewhere, GitHub comments aren't threaded, so a comment counts as a reply when it comes after your first one on that issue or pull request. Comments by deleted accounts are skipped.

Reviews aren't read. A pull request review, its verdict, and its inline comments on the code aren't imported; conversation comments on a pull request are.

Gists are SoftwareSourceCode; repositories are Repository. A repository is a version-controlled store of files and their history, whatever it holds (code, data, documents), so it isn't a kind of source code; a gist is a snippet of code. A secret gist is hidden from listings but open to anyone with its URL, so it's unlisted, not private. A gist is named by its description, or its first file when it has none. An enterprise's internal repository is private.

A repository's owner is its creator. An owner publishes the repository but may not have written it, and it can change hands, so author isn't claimed.

Tests

Tests run the extractors against a fake GitHub GraphQL API on 127.0.0.1 with synthetic data, a fake gh on PATH, and an empty credential store. They never reach the network or read your GitHub login.