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

@meowshed/meow-github

v0.17.1

Published

Reads a GitHub repository's issues, pull requests and comments through gh as one JSON document, projects an approved epic's tasks onto issues and writes nothing else, and asks before a Bash command's gh changes how the repository is governed. Distributed

Readme


reader: someone installing or running meow-github on Pi answers: what meow-github provides, what it adds to a session and how to install it kind: reference describes: [@meowshed/[email protected]]

@meowshed/meow-github

Reads a GitHub repository's issues, pull requests and comments through gh as one JSON document, projects an approved epic's tasks onto issues and writes nothing else, and asks before a Bash command's gh changes how the repository is governed. Distributed as a Pi package.

Install it

pi install npm:@meowshed/meow-github

The paw executable ships with @meowshed/meow-core alone (REQ-4612): this package's launchers run their subcommands through it, so install the core package beside this one for the binary-backed checks. Without it, every check reports unrun and lets the command through.

pi install npm:@meowshed/meow-core   # carries the binary the launchers run

Installing from this repository's checkout is the development install: it loads the package in place and runs no lifecycle script.

The pack is current as of gh 2.101.0, observed on 2026-10-24.

Read a repository's history

In a clone, or naming the repository:

meow-github history
meow-github history meowshed/meowpaw

It reads four listings, every page of each, and keeps each response for an hour so a second read costs no quota:

{
  "repository": "meowshed/meowpaw",
  "issues": [
    {
      "number": 2,
      "kind": "pull request",
      "merged": true,
      "title": "...",
      "body": "...",
      "state": "closed",
      "author": "...",
      "url": "...",
      "labels": []
    }
  ],
  "comments": [{ "on": 3, "author": "...", "body": "...", "url": "..." }],
  "review_comments": [
    { "on": 2, "path": "...", "author": "...", "body": "...", "url": "..." }
  ],
  "credential": "gh's stored credential",
  "budget": [
    "primary requests: 5 sent; core limit 5,000, 4,995 remaining, resets 2026-09-29T15:30:37Z",
    "secondary points: 5 of 900 this minute",
    "content creation: 0 of 80 this minute, 0 of 500 this hour",
    "spacing: 0 writes, each at least one second after the previous one"
  ]
}

merged is false for a pull request closed without merging, which records a rejected approach, and null for an issue. credential and budget are the credential line and the budget lines that Credential and budgets describes.

Project an authorising record's tasks onto issues

Declare the tracker in .meowpaw/profile.toml first. Without it the command says so and does nothing, and nothing in the method needs a tracker:

[tracker]
kind = "github"

The command opens by naming the profile's state, as meow-github project: profile: parsed, and each key no unit reads, as meow-github project: unknown key: <path>, which changes no exit status.

Once an epic is approved, file one issue per task:

meow-github project EPC-1310

An approved defect that carries tasks directly uses the same projection and derives completion from its own task marks:

meow-github project BUG-1210

Each issue is titled with the task's identifier and title, and its body cites the authorising epic, defect or decision, the requirements the task closes and its dependencies, and ends with a marker naming the task and a fingerprint. The task gains issue: with the issue's number and projected: with the fingerprint, so the mapping lives in the repository. After its last create, the run reads the issues it created back in one listing of the issues written since the updated_at GitHub gave the earliest of them, and matches each by number. Where no create's answer states that time, the listing starts at the Date of the run's first response. The listing can lag a create by seconds, so the run reads each issue it left out on its own, by number, before it reports that issue as not read back. Run it again and nothing changes. It refuses an authorising record that isn't approved.

Where a run stops before it has projected every task, or neither the listing nor a read by number shows a created issue as it was written, the report says which tasks are in which state and the run exits 3:

partial: projected TSK-1930; created, not read back TSK-1940 (issue #512, reads differently from what was written); not projected TSK-1950, TSK-1960

| Group | Holds each task whose issue | | ------------------------ | ------------------------------------------------------------------------- | | projected | Was updated, was found unchanged, or was created and read back as written | | created, not read back | Was created and not confirmed, with the issue's number and the reason | | not projected | Is any other task of the authorising record, visited or not |

A group with no task reads none. The reason is couldn't be read, reads differently from what was written, the listing couldn't be read, the listing was throttled, the listing's credential was rejected, no listing ran, no read ran or the task doesn't name it. The run sends the listing after a write GitHub refused, and sends none after a throttle, a budget ceiling or a 401, where it sends nothing more at all. A task under created, not read back keeps its issue:, so the next run reads that issue through the mapping and creates no second one. The one exception is the task doesn't name it: the issue exists and the task file couldn't be written, so set issue: on the task by hand before the next run.

A decision one task realises has no epic. Name the decision, and the pack files an issue for each task that names it in realises:, under nothing:

meow-github project ADR-2300

The record owns each issue's title and body, and GitHub owns whether it is open or closed, which the pack never writes. A task you changed updates its issue on the next run. An issue someone edited on GitHub is reported and left as it is, and an issue closed on GitHub while its task remains open in the record is reported. An epic or defect decides that state with its task marks; a task directly realising a decision decides it with Evidence. Add --check to see each task's state without writing anything:

meow-github project EPC-1310 --check

Synchronise a task and its issue

project writes the record to the issues. sync also takes a change made on GitHub back into the record, by a fingerprint of each side taken at the last synchronisation and not by a clock, because GitHub keeps one time for a whole issue. The mapping carries both: projected: for the record side and tracked: for the tracker side, which an older task gains at its next run.

meow-github sync EPC-1310
meow-github sync EPC-1310 --check

For each task that project has mapped, sync finds which side changed since the last run:

| Changed since the last run | What sync does | | -------------------------- | --------------------------------------------------------------------------------------------------------- | | Neither | Reports unchanged | | The record only | Updates the issue to what the record says | | The issue only | Writes the issue's title into a draft task; reports the difference for an approved one and writes nothing | | Both | Writes the record's text to the issue, because the record wins a conflict |

The body of an issue is derived from the task's closes and its dependencies, so a body edited on GitHub isn't written into the record, and the record's body goes back to the issue. An approved task's wording is never written from the tracker, which the method forbids for any approved record; the difference is reported and sync exits 1. An issue closed on GitHub marks the task done in its epic or defect where the task's Evidence is written, and is reported where it isn't. --check prints what each task would get and writes nothing.

The method runs sync before the first task of an epic and after the last task's Evidence, where a tracker is declared, and nothing runs it in the background.

Report the trunk's protections

protections reads the protection of the trunk .meowpaw/profile.toml declares as [git] trunk, and the rules in force on that branch, and prints which of six protections are in force. It changes nothing, because which protections the trunk should have is the repository owner's decision. In a clone, or naming the repository:

meow-github protections
meow-github protections meowshed/meowpaw

The report opens with the credential line and the profile's state, gives one line for each protection in this order, and ends with the four budget lines that Credential and budgets describes:

credential: gh's stored credential
meow-github protections: profile: parsed
force pushes blocked: in force
deletion blocked: in force
required reviews: absent
required checks: in force
signed commits: absent
linear history: in force
primary requests: 3 sent; core limit 5,000, 4,997 remaining, resets 2026-09-29T15:30:37Z
secondary points: 3 of 900 this minute
content creation: 0 of 80 this minute, 0 of 500 this hour
spacing: 0 writes, each at least one second after the previous one

A protection that the branch protection or a ruleset sets is in force. Where GitHub answers 404 for the branch protection, the branch has none, and the rules are still read. Where a read is refused or throttled, the report prints the refused: or throttled: line When it can't read describes, names each protection it couldn't read as unread, never as absent, and exits 3. Where the profile declares no trunk, the report says so and exits 3.

To read the same thing without the pack, run the two reads it sends:

gh api repos/OWNER/REPO/branches/main/protection
gh api repos/OWNER/REPO/rules/branches/main

Project a task by hand

You can file a task's issue without the pack and get the same issue and the same mapping, so installing the pack later continues your record instead of duplicating it. Write the body as the pack does: the task and its authorising record, the requirements it closes, and its dependencies. Its first sentence is <task> of <epic>, which realises <decision>. for an epic, <task> of <defect>. for a defect, or <task>, which realises <decision>. for a direct decision task. Then:

title="TSK-1930: Project an approved epic's tasks onto issues"
fingerprint=$(printf '%s\n%s' "$title" "$body" | shasum -a 256 | cut -c1-12)
gh api repos/OWNER/REPO/issues -X POST -f title="$title" -f body="$body

<!-- meow-github: projected from TSK-1930 at $fingerprint -->"

Set the task's issue: to the number GitHub returns and add projected: <fingerprint> beside it.

When it can't read

Where gh is missing, isn't signed in, or GitHub refuses a listing, it prints unread, names the listing that failed and what GitHub said, lists what it read before it stopped, and exits 3. It prints no document then, because a part of the history would read as the whole of it. It prints the credential line first and the four budget lines last, as text, as Credential and budgets describes. Where its binary is missing for your machine, it names the machine and says to reinstall the unit.

Where GitHub refuses a call, history and project name the method, the endpoint and the permission the call needed, carry GitHub's own reason, and exit 3:

unauthenticated: POST repos/OWNER/REPO/issues; GitHub said "Bad credentials"
refused: POST repos/OWNER/REPO/issues needs issues=write; GitHub said "Resource not accessible by personal access token"
refused: GET repos/OWNER/REPO/issues/512 needs a permission: GitHub named no permission and said "Not Found", or it is hidden from this credential

| Line | GitHub answered | | ---------------------------------------- | ------------------------------------------------------------------------------------------ | | unauthenticated: <method> <endpoint> | 401: the credential is missing, expired or revoked | | refused: <method> <endpoint> needs ... | 403 that isn't a throttle | | The same, ending , or it is hidden ... | 404 on an issue a task maps, which GitHub also gives for an issue the credential can't see |

After needs, the line gives the permission as GitHub named it, from the first of these the response carries:

  • X-Accepted-GitHub-Permissions, as it stands, such as issues=write;
  • X-Accepted-OAuth-Scopes beside the credential's own X-OAuth-Scopes, as one of the scopes repo; the credential holds read:org, gist;
  • neither, as a permission: GitHub named no permission and said "...", with GitHub's message quoted.

A line that doesn't already quote GitHub's message ends with ; GitHub said "<message>", because a permission header says what an endpoint accepts and not why this credential was refused. After a 401 the run sends nothing more, the read-back listing included, because GitHub rejects an account's valid credentials too after several rejected requests. project then prints stopped at the rejected credential above and the partial: line.

Wherever a line quotes GitHub's message, a control character or a line separator in it prints as a space, and a backslash or a double quote is escaped, so the message can't split the line or end its quotation.

project prints the line on a line of its own, above the line saying what the refusal stopped: a task, the naming of the repository or the read-back. history prints it after unread:, and after the listing's name where a listing was refused.

Every call goes through one request layer that reads GitHub's rate-limit headers on each response. Where GitHub throttles a call, or a fresh response says the limit is spent, history and project send nothing more, print throttled: <method> <endpoint>, retry after <UTC time> (<header>) and exit 3, because a call sent while throttled can get the integration banned. Where the header doesn't parse as its unit, the line says the time is unknown. Add --wait to either command to sleep until that time and send the call again:

meow-github project EPC-1310 --wait

A run sleeps an hour at most in all, because a primary limit resets within an hour and a run still throttled after that has another cause. It stops as throttled at the wait that would pass the hour.

Credential and budgets

GitHub's limits differ by how a run signs in, so every run names the form of its credential. The first line of project's report is credential: <form>, and credential in history's document is the form alone. The form is one of these, in the order gh prefers them:

  • GH_TOKEN from the environment
  • GITHUB_TOKEN from the environment
  • gh's stored credential

Inside a GitHub Actions workflow, where GITHUB_ACTIONS is true, the line adds , inside a GitHub Actions workflow. The pack reads only whether each variable is set and never prints a token.

The request layer keeps four counts for the run and prints them as the last lines of project's report, and as budget in history's document:

primary requests: 12 sent; core limit 5,000, 4,988 remaining, resets 2026-09-29T15:30:37Z
secondary points: 32 of 900 this minute
content creation: 5 of 80 this minute, 5 of 500 this hour
spacing: 5 writes, each at least one second after the previous one

Primary requests takes the limit from GitHub's x-ratelimit-limit header, because the limit depends on the credential and the pack doesn't guess it. Secondary points cost 1 for a read and 5 for a write. Content creation counts each issue created. The layer sends one write at a time, at least a second after the previous one. Where the next request would pass a ceiling, the run doesn't send it: it stops as at a throttle, and the throttled: line names the count and when it frees, as in (content creation: 500 of 500 this hour). --wait sleeps until then, as it does at a throttle. The counts cover this run alone, so another run or a person using the same account can still meet GitHub's own throttle.

Read a subcommand's help

meow-github --help lists the subcommands, and meow-github <subcommand> --help names what it determines, what you read to reach the same answer without the tool, and the first word of each kind of line it prints. A test holds each subcommand's output to those words.

Hooks

governance-guard stops a gh command that changes how the repository is governed, until a person answers, by asking through the platform and never answering for them; the meow-github:governance skill says why, and mise run test-tools fails a workflow step that runs a gh command the guard asks about. irreversible-guard stops a merge, a release or an issue or pull request write in a session a person attends, until the person answers for that one call, and never answers for them; rule M25 of the meow-flow:method skill says why, and mise run test-crate fails a guard whose second call doesn't ask again.