GitHub maintenance toolkit β a single CLI for common GitHub bulk operations. Pure bash, zero dependencies beyond gh and jq.
brew install maxgfr/tap/github-helpersOr manually:
curl -fsSL https://raw.githubusercontent.com/maxgfr/github-helpers/main/script.sh -o /usr/local/bin/github-helpers
chmod +x /usr/local/bin/github-helpers| Command | Description |
|---|---|
unstar |
Filter & bulk-unstar repos |
cleanup-forks |
Audit forks; delete only inactive ones |
sync-forks |
Update your forks from their upstream |
cleanup-branches |
Delete merged, squash-merged or stale branches |
archive-repos |
Batch archive inactive repos |
release-cleanup |
Delete old releases, keep N latest |
pr-cleanup |
Close abandoned pull requests |
cleanup-packages |
Delete old GitHub Package versions |
stale-issues |
Find/close stale issues and PRs |
cache-cleanup |
Purge Actions caches (10 GB/repo quota) |
artifact-cleanup |
Delete Actions artifacts |
run-cleanup |
Delete old workflow runs and their logs |
gist |
List, export and bulk-delete gists |
notifications |
Triage and bulk-clear your inbox |
invite-cleanup |
List, accept or decline pending invitations |
repo-audit |
Check for missing LICENSE, README, etc. |
stats |
Dashboard: repos, stars, languages |
workflow-status |
Latest CI status across repos |
secret-audit |
List Actions secrets & variables |
license-check |
Check/add LICENSE files |
vulnerability-check |
Audit Dependabot vulnerability alerts |
branch-protection |
Audit/enforce branch protection rules |
webhook-audit |
List webhooks across repos |
collaborator-audit |
Audit outside collaborators and permissions |
activity-report |
Activity summary for a period |
traffic |
Snapshot views and clones (14-day window) |
org-audit |
Org security and membership posture |
follow-audit |
Who follows you back, and who does not |
inbox |
What is waiting on you: replies, reviews, new issues |
branches |
Open branches, commits ahead/behind the default branch |
clone-org |
Clone/pull all repos from an org or user |
bulk-topic |
Add/remove topics in batch |
sync-labels |
Sync issue labels across repos |
export-stars |
Export stars to JSON/CSV/Markdown |
rename-default-branch |
Rename masterβmain in batch |
dependabot-enable |
Enable Dependabot in batch |
mirror |
Mirror repos to GitLab/Bitbucket/etc. |
bulk-settings |
Apply repo settings in batch |
repo-template |
Sync settings from a template repo |
bulk-merge |
Merge green dependency-update PRs |
backup |
Export repos and metadata locally |
Filter and bulk-unstar repos by last commit date, last push date, or archived status.
# Preview: repos with no commit since 2024 OR archived
github-helpers unstar --commit-before 2024-01-01 --archived --dry-run -v
# Edit the generated list, then execute
vim unstar-repos.txt
github-helpers unstar --from unstar-repos.txt
# One-shot: unstar all archived repos
github-helpers unstar --archived -y| Flag | Description |
|---|---|
--commit-before DATE |
Last commit was before this date (YYYY-MM-DD) |
--commit-after DATE |
Last commit was after this date |
--activity-before DATE |
Last push was before this date |
--activity-after DATE |
Last push was after this date |
--archived / --not-archived |
Filter by archive status |
--any |
Match if ANY filter hits (OR, default) |
--all |
Match if ALL filters hit (AND) |
--dry-run |
Preview only, saves list to file |
--out FILE |
Output file (default: unstar-repos.txt) |
--save-list |
Save the matched list even outside --dry-run |
--from FILE |
Unstar from a previous dry-run file |
Alias: github-helpers forks. A fork is kept when any activity signal fires β every
protection is on by default and has an explicit opt-out.
# read-only audit, with the reason for every fork
github-helpers forks --report -v
# preview, review the list, then execute
github-helpers cleanup-forks --older-than 180 --dry-run
vim cleanup-forks.txt
github-helpers cleanup-forks --from cleanup-forks.txt| Protection (on by default) | Opt out with |
|---|---|
| An open pull request from the fork | --ignore-open-prs |
| Own commits on any branch | --default-branch-only |
| Stars, watchers, or forks of the fork | --ignore-popularity |
| Pushed or created within N days | --older-than 0 |
| Archived fork | --include-archived |
| Upstream gone or private (orphan) | --include-orphans |
| Flag | Description |
|---|---|
--user NAME / --org NAME |
Target (default: authenticated user) |
--older-than N |
Only forks untouched for N days (default: 30) |
--report |
Audit only β classify everything, never delete |
--format table|json|csv |
Report format (default: table) |
--max-branches N |
Protect, without verifying, above N branches (max 100) |
--out FILE / --save-list / --from FILE |
Review-then-execute loop |
--no-verify |
With --from, skip re-checking protections (dangerous) |
--limit N |
Max forks to examine (default: 1000) |
Deleting a fork permanently closes any pull request opened from it, so open PRs protect a
fork by default. Anything that cannot be fully resolved is reported as SKIP and never
deleted. Orphans need a second confirmation because their divergence cannot be verified.
Deletion needs the delete_repo scope: gh auth refresh -h github.com -s delete_repo.
github-helpers sync-forks --dry-run
github-helpers sync-forks
github-helpers sync-forks --repo me/my-fork --branch main| Flag | Description |
|---|---|
--user NAME / --org NAME |
Target (default: authenticated user) |
--repo OWNER/NAME |
Sync a single fork |
--branch NAME |
Branch to sync (default: each fork's default branch) |
--limit N |
Max forks to examine (default: 1000) |
Reports SYNCED / UP-TO-DATE / CONFLICT / SKIPPED per fork. No confirmation prompt:
merge-upstream only fast-forwards or merges from the upstream, so it never discards
your work. Archived forks and orphans are skipped.
Built on the same scan as branches, with
the review-then-execute loop of unstar and cleanup-forks.
# preview every repo you own, review the list, then execute
github-helpers cleanup-branches --dry-run
vim cleanup-branches.txt # delete the lines of the branches to keep
github-helpers cleanup-branches --from cleanup-branches.txt
# one repo, only branches with no commit ahead of the default branch
github-helpers cleanup-branches --repo maxgfr/my-repo --strict
# also stale branches, never release/hotfix ones, across an org
github-helpers cleanup-branches --org my-company --stale-days 90 --exclude "^(release|hotfix)/" --dry-run| Deleted | Condition |
|---|---|
MERGED |
No commit ahead of the default branch |
PR_MERGED |
Its last PR was merged and the branch has not moved since (squash or rebase merge) |
PR_CLOSED |
Its last PR was closed unmerged and the branch has not moved since |
STALE |
Only with --stale-days N: no commit in N days |
Never deleted: the default branch, protected branches, the head or base of an open pull
request, and any branch that could not be fully verified (UNKNOWN). No branch of a repo
with more than 100 open PRs is deleted.
| Flag | Description |
|---|---|
--repo OWNER/REPO |
Single repository |
--org NAME / --user NAME |
All non-archived source repos of an owner (default: you) |
--merged |
The default; kept for compatibility |
--strict |
Delete MERGED branches only (the previous behaviour) |
--stale-days N |
Also delete STALE branches |
--exclude REGEX |
Never touch branches matching REGEX |
--dry-run / --out FILE |
Preview and write the list (default: cleanup-branches.txt) |
--from FILE |
Delete the listed branches after re-checking each one |
--limit N |
Max repos to scan (default: 1000) |
-y |
Skip the confirmation prompt |
--from rescans the listed repos: a branch whose SHA moved since the dry run, or whose
verdict changed (an open PR appeared, for instance), is skipped and reported. The list keeps
each branch's SHA, so a branch deleted by mistake can be restored with
gh api repos/OWNER/REPO/git/refs -f ref=refs/heads/BRANCH -f sha=SHA.
Behaviour changes. A confirmation is now asked before deleting: scripts must pass
-y. Squash-merged branches and branches of closed PRs are deleted by default (--strictrestores the old rule).--stale-daysadds to the default instead of replacing it, and no longer touches branches with an open PR. The target is optional and defaults to your repos.
Batch archive repos with no push activity in N months.
github-helpers archive-repos --inactive-months 24 --dry-run
github-helpers archive-repos --org my-company --inactive-months 12 -y| Flag | Description |
|---|---|
--user NAME / --org NAME |
Target (default: authenticated user) |
--inactive-months N |
Inactivity threshold (default: 12) |
--language LANG |
Filter by language |
--topic TOPIC |
Filter by topic |
github-helpers release-cleanup --repo maxgfr/my-repo --keep 5 --dry-run
github-helpers release-cleanup --user maxgfr --pre-only --keep 3 -y| Flag | Description |
|---|---|
--repo OWNER/REPO |
Single repo |
--user NAME / --org NAME |
All repos |
--keep N |
Releases to keep (default: 5) |
--pre-only |
Only delete pre-releases |
Find and optionally close pull requests with no activity in N days.
github-helpers pr-cleanup --repo maxgfr/my-repo --days 60
github-helpers pr-cleanup --org my-company --draft-only --days 30
github-helpers pr-cleanup --repo maxgfr/my-repo --close --delete-branch --dry-run| Flag | Description |
|---|---|
--repo OWNER/REPO |
Single repo |
--user NAME / --org NAME |
Target (default: authenticated user) |
--days N |
Days without activity (default: 90) |
--draft-only |
Only target draft PRs |
--close |
Close abandoned PRs |
--comment TEXT |
Comment before closing |
--delete-branch |
Delete head branch after closing |
Delete old GitHub Package versions, keeping the N most recent.
github-helpers cleanup-packages --type container --keep 3 --dry-run
github-helpers cleanup-packages --org my-company --type npm --keep 10
github-helpers cleanup-packages --type container --package myapp --keep 1| Flag | Description |
|---|---|
--user NAME / --org NAME |
Target (default: authenticated user) |
--type TYPE |
Package type: npm, maven, rubygems, docker, nuget, container (required) |
--package NAME |
Specific package name (default: all) |
--keep N |
Versions to keep per package (default: 5) |
Find and optionally close issues and PRs with no activity in N days.
github-helpers stale-issues --repo maxgfr/my-repo --days 180
github-helpers stale-issues --org my-company --type pr --days 60
github-helpers stale-issues --repo maxgfr/my-repo --close --comment "Closing as stale" --dry-run| Flag | Description |
|---|---|
--repo OWNER/REPO |
Single repo |
--user NAME / --org NAME |
Target (default: authenticated user) |
--days N |
Days without activity (default: 90) |
--type TYPE |
Filter: issue, pr, all (default: all) |
--label LABEL |
Filter by label |
--close |
Close stale issues/PRs |
--comment TEXT |
Comment before closing |
Each repository has a 10 GB Actions cache quota. Reports the bytes reclaimed.
github-helpers cache-cleanup --dry-run
github-helpers cache-cleanup --older-than 30 -y
github-helpers cache-cleanup --repo me/proj --keep 5
github-helpers cache-cleanup --org my-company --larger-than 500MB --dry-run| Flag | Description |
|---|---|
--older-than N |
Caches not accessed for N days |
--key PATTERN |
Cache key matches this regex |
--ref REF |
Only caches for this ref |
--larger-than SIZE |
100MB, 1.5GiB, 500K⦠|
--keep N |
Keep the N most recently accessed per repo |
--user / --org / --repo / --limit |
Targeting (--limit defaults to 200 repos) |
github-helpers artifact-cleanup --dry-run
github-helpers artifact-cleanup --older-than 14 -y
github-helpers artifact-cleanup --repo me/proj --larger-than 200MB| Flag | Description |
|---|---|
--older-than N |
Artifacts created more than N days ago |
--name PATTERN |
Artifact name matches this regex |
--branch NAME |
Only artifacts from runs on this branch |
--larger-than SIZE |
100MB, 1.5GiB⦠|
--expired |
Only already-expired artifacts |
Expired artifacts no longer count against billed storage, so deleting them reclaims
nothing. They are excluded by default; --expired selects only them, for tidiness.
github-helpers run-cleanup --older-than 90 --dry-run
github-helpers run-cleanup --keep 10 -y
github-helpers run-cleanup --conclusion failure --older-than 30
github-helpers run-cleanup --repo me/proj --workflow ci.yml --keep 5| Flag | Description |
|---|---|
--older-than N |
Runs created more than N days ago |
--keep N |
Keep the N most recent runs of each workflow |
--conclusion C |
success, failure, cancelled, skipped⦠|
--branch NAME / --workflow NAME |
Narrow further |
--keep is per workflow, not per repo: a noisy workflow would otherwise wipe out the
history of a rarely-run one. Deleting a run also deletes its logs and artifacts β to
reclaim storage while keeping the history, use artifact-cleanup. Queued and in-progress
runs are never deleted. At least one of --older-than, --keep or --conclusion is
required.
Alias: github-helpers gists.
github-helpers gist
github-helpers gist --format csv --out gists.csv
github-helpers gist --empty --no-description --delete --dry-run
github-helpers gist --from gist-delete.txt| Flag | Description |
|---|---|
--public / --secret |
Filter by visibility |
--older-than N |
Created more than N days ago |
--untouched N |
Not updated in N days |
--empty |
All files empty or whitespace-only |
--no-description |
No description |
--match PATTERN |
Regex on the description or any filename |
--starred |
Operate on gists you starred (read-only) |
--delete |
Delete the matches (requires at least one filter) |
--format text|json|csv|md, --out FILE |
Export |
--dry-run / --save-list / --from FILE |
Review-then-execute loop |
Filters are ANDed, unlike unstar's default OR β you are building a deletion set, and
an OR would be a trap. --empty reads file sizes from the listing for free and only
fetches the content of gists whose largest file is under 64 bytes. The generated list is
annotated, because a bare 32-hex gist id tells a human nothing.
Alias: github-helpers notifs.
# clear a week of CI noise
github-helpers notifications --reason ci_activity --older-than 7 --mark-read --dry-run
github-helpers notifications --reason ci_activity --older-than 7 --mark-read -y
# permanently mute every thread in one repo
github-helpers notifications --repo owner/repo --all --unsubscribe --mark-read| Flag | Description |
|---|---|
--repo OWNER/NAME |
Only this repository |
--reason REASON |
ci_activity, mention, review_requested, security_alert⦠|
--type TYPE |
Issue, PullRequest, Release, Discussion, CheckSuite⦠|
--older-than N |
Not updated in the last N days |
--unread / --all |
Unread only (default), or include read |
--mark-read |
Mark the matches as read |
--unsubscribe |
Mute the matched threads permanently |
--format text|json|csv|md |
Export |
gh does not request the notifications scope by default:
gh auth refresh -h github.com -s notifications.
--unsubscribe sets the thread to ignored rather than just dropping the subscription,
because a plain unsubscribe resubscribes you on the next comment.
github-helpers invite-cleanup
github-helpers invite-cleanup --accept --repo friend/project
github-helpers invite-cleanup --outgoing --org my-company --decline --older-than 60| Flag | Description |
|---|---|
--incoming / --outgoing |
Invitations to you (default), or ones you sent |
--repo OWNER/NAME / --org NAME |
Restrict the scope |
--older-than N |
Only invitations older than N days |
--accept / --decline |
Never implicit; listing is the default |
--outgoing requires --repo or --org: GraphQL exposes no outgoing-invitation
connection, so a whole-account scan would cost one request per repository, while --org
answers in one. There is no REST endpoint to decline an org invitation β those are
reported with a link and counted as Manual.
Check repos for missing LICENSE, README, description, or topics.
github-helpers repo-audit
github-helpers repo-audit --org my-company
github-helpers repo-audit --language Shell -v| Flag | Description |
|---|---|
--user NAME / --org NAME |
Target (default: authenticated user) |
--language LANG |
Filter by language |
--topic TOPIC |
Filter by topic |
--limit N |
Max repos to scan |
Quick dashboard: repo count, total stars/forks, top languages, most starred, least active.
github-helpers stats
github-helpers stats --org my-companySee the latest CI run status for all your repos at a glance.
github-helpers workflow-status
github-helpers workflow-status --org my-company --failed
github-helpers workflow-status --limit 50| Flag | Description |
|---|---|
--user NAME / --org NAME |
Target (default: authenticated user) |
--limit N |
Max repos to scan (default: 30) |
--failed |
Show only repos with failed workflows |
github-helpers secret-audit
github-helpers secret-audit --org my-company
github-helpers secret-audit --repo maxgfr/my-repo -v| Flag | Description |
|---|---|
--user NAME / --org NAME |
Target (default: authenticated user) |
--repo OWNER/REPO |
Single repo |
--limit N |
Max repos to scan |
github-helpers license-check
github-helpers license-check --add --template MIT --dry-run
github-helpers license-check --org my-company --add --template Apache-2.0 -y| Flag | Description |
|---|---|
--user NAME / --org NAME |
Target (default: authenticated user) |
--add |
Add LICENSE to repos missing one |
--template SPDX |
License template (e.g., MIT, Apache-2.0) |
Scan repos for open Dependabot vulnerability alerts, grouped by severity.
github-helpers vulnerability-check
github-helpers vulnerability-check --org my-company --severity critical
github-helpers vulnerability-check --repo maxgfr/my-repo -v| Flag | Description |
|---|---|
--repo OWNER/REPO |
Single repo |
--user NAME / --org NAME |
Target (default: authenticated user) |
--severity LEVEL |
Filter: critical, high, medium, low |
--limit N |
Max repos to scan |
Check which repos lack branch protection on their default branch, and optionally enforce rules.
github-helpers branch-protection
github-helpers branch-protection --org my-company
github-helpers branch-protection --enforce --require-reviews 2 --dry-run
github-helpers branch-protection --repo maxgfr/my-repo --enforce -y| Flag | Description |
|---|---|
--repo OWNER/REPO |
Single repo |
--user NAME / --org NAME |
Target (default: authenticated user) |
--enforce |
Apply protection rules (default: audit only) |
--require-reviews N |
Required approving reviews (default: 1) |
--require-status-checks |
Require status checks to pass |
--allow-force-push |
Allow force push (default: disallow) |
List all configured webhooks with their URL, events, and status.
github-helpers webhook-audit
github-helpers webhook-audit --org my-company -v
github-helpers webhook-audit --repo maxgfr/my-repo| Flag | Description |
|---|---|
--repo OWNER/REPO |
Single repo |
--user NAME / --org NAME |
Target (default: authenticated user) |
--limit N |
Max repos to scan |
List outside collaborators and their permission levels across repos.
github-helpers collaborator-audit --org my-company
github-helpers collaborator-audit --org my-company --permission admin
github-helpers collaborator-audit --user maxgfr| Flag | Description |
|---|---|
--org NAME / --user NAME |
Target (required) |
--permission LEVEL |
Filter: admin, write, read |
--limit N |
Max repos to scan |
Generate a summary of PRs, issues, and repo activity for a given period.
github-helpers activity-report
github-helpers activity-report --org my-company --since 2025-01-01
github-helpers activity-report --since 2025-06-01 --until 2025-06-30 --format json
github-helpers activity-report --user octocat --format csv| Flag | Description |
|---|---|
--user NAME / --org NAME |
Target (default: authenticated user) |
--since DATE |
Start date YYYY-MM-DD (default: 30 days ago) |
--until DATE |
End date YYYY-MM-DD (default: today) |
--format FORMAT |
text, json, csv (default: text) |
GitHub only keeps 14 days of traffic data, so the point is to build your own history.
github-helpers traffic
github-helpers traffic --sort clones --top 10
github-helpers traffic --repo me/proj --paths --referrers
# cron this: appends only rows the file does not already have
github-helpers traffic --format csv --out traffic.csv --append| Flag | Description |
|---|---|
--per day|week |
Granularity (default: day) |
--sort views|clones |
Sort the summary |
--top N |
Only the top N repos |
--paths / --referrers |
Most visited paths, top referring sites |
--format text|json|csv|md, --out FILE |
Export |
--append |
Append only new (date, repo) rows β idempotent |
These endpoints need push access, so repos you do not own are skipped.
github-helpers org-audit --org my-company
github-helpers org-audit --org my-company --format json | jq .summary
github-helpers org-audit --org my-company --fail-on-issues # in CI| Check | Reports |
|---|---|
2fa_required |
Two-factor enforced organization-wide |
2fa_members |
Members with two-factor disabled |
owner_count |
Too many owners, or a bus factor of one |
outside_collaborators |
How many, with a pointer to collaborator-audit |
pending_invitations |
Invitations still outstanding after 30 days |
default_repo_permission |
Base permission granted on every new repo |
member_repo_creation |
Whether members can create public repos |
teams |
Access managed through teams rather than per person |
| Exit code | With --fail-on-issues |
|---|---|
0 |
No problems |
1 |
Fatal error (missing --org, org not found) |
2 |
At least one FAIL |
4 |
No FAIL, but a check could not run (SKIP) |
3 |
No FAIL or SKIP, at least one WARN |
SKIP outranks WARN on purpose: an incomplete audit is worse than a known warning, because you cannot know what the check that did not run would have found.
This is org-level only and never iterates repositories β that is the boundary with
collaborator-audit. Several endpoints are owner-only:
gh auth refresh -h github.com -s read:org,admin:org.
Alias: github-helpers follow. Read-only by default.
github-helpers follow-audit
github-helpers follow-audit --not-followed-by-me
github-helpers follow-audit --format csv --out follows.csv
github-helpers follow-audit --not-following-back --exclude keep.txt --unfollow --dry-run| Flag | Description |
|---|---|
--not-following-back |
You follow them, they do not follow you (default view) |
--not-followed-by-me |
They follow you, you do not follow back |
--mutuals |
Mutual follows |
--exclude FILE |
Logins never to unfollow, one per line |
--inactive N |
No push to a public repo of theirs in N days |
--unfollow |
Unfollow (only with --not-following-back) |
--format text|json|csv|md, --out FILE |
Export |
--inactive measures the last push to a public repository they own. Someone working
only in private repos will look inactive; it is a hint, not proof.
--exclude fails loudly if the file is missing β a silently-empty whitelist is the
disaster case here. Unfollowing goes through the dry-run β edit β --from loop so a human
reads every login first, and needs the user:follow scope.
Alias: github-helpers recap. Read-only. A daily view of the issues and PRs other people
opened on your repositories, without going through the notification inbox.
github-helpers inbox # since your last run (7 days the first time)
github-helpers recap --since 30 --org my-company # fixed window, your repos + an org
github-helpers inbox --only awaiting --only review --open 5
github-helpers inbox --format md --output digest.md --no-save
github-helpers inbox --format json | jq '.[] | select(.section == "review") | .url'Each item is listed once, in the first section it matches:
| Section | Meaning |
|---|---|
β³ awaiting |
The last human comment is from someone who is not OWNER/MEMBER/COLLABORATOR (or nobody answered an outside issue yet) |
π review |
Open PR, not a draft, no conflict, CI green or absent, and you have not reviewed it |
π new |
Opened inside the window |
π· untriaged |
Issue with no label and no assignee |
β updated |
Older item with activity inside the window |
| Flag | Description |
|---|---|
--user NAME |
Scan this user's repos instead of yours |
--org NAME |
Also scan an organization (repeatable) |
--repo OWNER/NAME |
Only this repository |
--type issue|pr|all |
Default all |
--since N|YYYY-MM-DD |
Fixed window instead of "since last run" (not saved) |
--no-save |
Do not update the last-run timestamp |
--label NAME |
Only items with this label |
--archived |
Include archived repositories |
--include-forks |
Include forks (excluded by default) |
--include-bots |
List bot items instead of a one-line summary |
--include-mine |
List items you opened yourself |
--all |
Also list every other open item from other people, last, as "other" |
--exclude-author LOGIN |
Treat this login as a bot (repeatable) |
--only SECTION |
Keep only this section (repeatable) |
--max-per-section N |
Lines per section in text mode (default 15, 0 = all) |
--limit N |
Max results per search (default 1000) |
--format text|json|csv|md, --output FILE |
Export; md is a paste-ready digest |
--open N |
Open the N most urgent items in the browser (asks above 5) |
It runs one paginated GraphQL search per owner and type rather than one request per
repository. Known bots (Dependabot, Renovate, github-actionsβ¦) are excluded server-side and
reported as a count, with a pointer to bulk-merge. GitHub search stops at 1000 results;
the command warns when a search hits it or --limit.
The last-run timestamp is stored per scope (owners + type + label) in
${XDG_STATE_HOME:-~/.local/state}/github-helpers/inbox.json. It records the start of
the run, so nothing opened during the scan is missed, and it is only written after a
complete scan: a skipped owner, --since, --only or --no-save leave it untouched.
Alias: github-helpers branch-status. Read-only. Every branch other than the default one, on
every non-archived source repo, with how many commits it is ahead of and behind the default
branch, its last PR, and what cleanup-branches would do with it.
github-helpers branches
github-helpers branches --org my-company --stale-days 90
github-helpers branch-status --repo maxgfr/github-helpers
github-helpers branches --format json | jq '.[] | select(.behind > 50)'maxgfr/github-helpers (main)
feat/foo β3 β12 4d #42 open ACTIVE
wip/squashed β5 β8 20d #38 merged PR_MERGED
fix/old β0 β40 90d β MERGED
βββββββββββββββββββββββββββββββββββββββββββββ
14 branch(es) in 5 repo(s): 6 active Β· 2 open PR Β· 5 cleanable Β· 1 protected
β github-helpers cleanup-branches --dry-run
| Verdict (first match wins) | Meaning |
|---|---|
PROTECTED |
Covered by a branch protection rule |
OPEN_PR |
Head or base of an open pull request |
UNKNOWN |
Could not be compared or verified |
MERGED |
No commit ahead of the default branch |
PR_MERGED |
Last PR merged, branch unchanged since (squash or rebase merge) |
PR_CLOSED |
Last PR closed unmerged, branch unchanged since |
STALE |
Only with --stale-days N: no commit in N days |
ACTIVE |
Everything else, including commits pushed after a merged PR |
| Flag | Description |
|---|---|
--user NAME / --org NAME |
Owner to scan (default: you) |
--repo OWNER/NAME |
Only this repository |
--stale-days N |
Label branches untouched for N days as STALE |
--exclude REGEX |
Hide branches matching REGEX |
--limit N |
Max repos to scan (default: 1000) |
--format text|json|csv|md, --output FILE |
Export |
One GraphQL request covers 10 repositories: branches, comparisons and PRs together. Only the first 100 branches of a repo are listed, and the command says so when that happens.
github-helpers clone-org --org my-company --ssh --not-archived
github-helpers clone-org --user octocat --source --language Go
github-helpers clone-org --org my-company --dir ~/projects --pull| Flag | Description |
|---|---|
--org NAME / --user NAME |
Target (one required) |
--dir PATH |
Clone destination (default: .) |
--ssh |
Clone via SSH instead of HTTPS |
--pull |
Pull existing repos instead of skipping |
--archived / --not-archived |
Filter by archive status |
--fork / --source |
Filter by fork status |
--visibility TYPE |
public, private, or internal |
--language LANG |
Filter by primary language |
--topic TOPIC |
Filter by topic |
--limit N |
Max repos to clone |
github-helpers bulk-topic --add shell --language Shell --dry-run
github-helpers bulk-topic --remove deprecated --topic deprecated -y
github-helpers bulk-topic --add cli --pattern "^maxgfr/(git-|package-)" --dry-run| Flag | Description |
|---|---|
--add TOPIC |
Add topic to matching repos |
--remove TOPIC |
Remove topic from matching repos |
--user NAME / --org NAME |
Target (default: authenticated user) |
--language LANG |
Filter by language |
--topic TOPIC |
Filter by existing topic |
--pattern PATTERN |
Filter by name (grep regex) |
github-helpers sync-labels --from maxgfr/template --to maxgfr/my-repo --dry-run
github-helpers sync-labels --from maxgfr/template --org my-company -y| Flag | Description |
|---|---|
--from OWNER/REPO |
Source repo with template labels |
--to OWNER/REPO |
Single target repo |
--org NAME / --user NAME |
Apply to all repos |
github-helpers export-stars --format json --out stars.json
github-helpers export-stars --format csv --out stars.csv
github-helpers export-stars --format md| Flag | Description |
|---|---|
--format FORMAT |
json, csv, or md (default: json) |
--out FILE |
Output file (default: stdout) |
github-helpers rename-default-branch --from master --to main --dry-run
github-helpers rename-default-branch --org my-company --dry-run
github-helpers rename-default-branch --repo maxgfr/old-repo -y| Flag | Description |
|---|---|
--from NAME |
Current branch name (default: master) |
--to NAME |
New branch name (default: main) |
--repo OWNER/REPO |
Single repo |
--user NAME / --org NAME |
Target (default: authenticated user) |
github-helpers dependabot-enable --dry-run
github-helpers dependabot-enable --ecosystems npm,github-actions --schedule weekly
github-helpers dependabot-enable --org my-company -y| Flag | Description |
|---|---|
--user NAME / --org NAME |
Target (default: authenticated user) |
--ecosystems LIST |
Comma-separated ecosystems (default: auto-detect) |
--schedule FREQ |
daily, weekly, monthly (default: weekly) |
github-helpers mirror --repo maxgfr/my-repo --target "git@gitlab.com:maxgfr/{name}.git" --dry-run
github-helpers mirror --user maxgfr --target "git@gitlab.com:maxgfr/{name}.git" -y| Flag | Description |
|---|---|
--repo OWNER/REPO |
Single repo |
--user NAME / --org NAME |
All repos from user/org |
--target URL |
Target URL template with {name} placeholder |
--dir PATH |
Temp directory for bare clones |
Enable or disable repo features in bulk: wiki, issues, projects, discussions, auto-merge, delete-branch-on-merge.
github-helpers bulk-settings --disable-wiki --language TypeScript --dry-run
github-helpers bulk-settings --enable-delete-branch --enable-auto-merge --org my-company
github-helpers bulk-settings --disable-projects --disable-wiki --topic archived --dry-run| Flag | Description |
|---|---|
--enable-wiki / --disable-wiki |
Toggle wiki |
--enable-issues / --disable-issues |
Toggle issues |
--enable-projects / --disable-projects |
Toggle projects |
--enable-discussions / --disable-discussions |
Toggle discussions |
--enable-auto-merge / --disable-auto-merge |
Toggle auto-merge |
--enable-delete-branch / --disable-delete-branch |
Toggle delete branch on merge |
--user NAME / --org NAME |
Target (default: authenticated user) |
--language LANG |
Filter by language |
--topic TOPIC |
Filter by topic |
--pattern PATTERN |
Filter by repo name (grep regex) |
Copy settings, labels, and/or branch protection rules from a template repo to other repos.
github-helpers repo-template --from maxgfr/template --sync-labels --dry-run
github-helpers repo-template --from maxgfr/template --all --org my-company
github-helpers repo-template --from maxgfr/template --sync-settings --topic typescript| Flag | Description |
|---|---|
--from OWNER/REPO |
Template repo (required) |
--user NAME / --org NAME |
Target (default: authenticated user) |
--sync-settings |
Sync repo settings |
--sync-labels |
Sync issue labels |
--sync-protection |
Sync branch protection rules |
--all |
Sync everything |
--language LANG |
Filter target repos by language |
--topic TOPIC |
Filter target repos by topic |
github-helpers bulk-merge --dry-run
github-helpers bulk-merge --patch-only --delete-branch -y
github-helpers bulk-merge --author app/renovate --minor-only --dry-run| Flag | Description |
|---|---|
--author LOGIN |
PR author (default: app/dependabot) |
--label NAME / --title-match REGEX |
Narrow the selection |
--patch-only / --minor-only |
Filter by the semver bump in the title |
--max N |
Max PRs merged per repo (default: 10) |
--strategy squash|merge|rebase |
Merge strategy (default: squash) |
--delete-branch |
Delete the head branch after merging |
--no-checks |
Merge even when checks have not passed (dangerous) |
--allow-unstable |
Allow UNSTABLE (non-required checks failing) |
This writes to default branches. Checks must pass unless you pass --no-checks,
drafts are always skipped, and only mergeStateStatus == CLEAN is merged β DIRTY,
BLOCKED, BEHIND and UNKNOWN never are. Every PR is listed before the single
confirmation. Titles whose bump cannot be parsed count as unknown, which both
--patch-only and --minor-only exclude.
github-helpers backup --repo me/proj --out /tmp/bk
github-helpers backup --gists
github-helpers backup --org my-company --mirror --out /backups/org
github-helpers backup --verify /tmp/bk| Flag | Description |
|---|---|
--out DIR |
Destination (default: github-backup-YYYY-MM-DD) |
--no-git / --no-metadata |
Take only one half |
--mirror |
Keep a bare mirror instead of a bundle |
--gists |
Also back up your gists |
--assets |
Also download release binaries (can be large) |
--include-forks |
Include forks (excluded by default) |
--resume |
Skip repos already recorded as done |
--verify DIR |
Check an existing backup against its manifest |
DIR/manifest.json counts, timestamps, sha256 per file
DIR/.repos.jsonl append-only journal, drives --resume
DIR/<owner>/<repo>/repo.bundle git history (or repo.git/ with --mirror)
DIR/<owner>/<repo>/issues.json issues only, pull requests stripped out
DIR/<owner>/<repo>/issue_comments.json, review_comments.json, pulls.json, β¦
issues.json holds the opening message but not the thread β the conversation lives in
issue_comments.json and review_comments.json, the biggest omission in a naive backup.
Every file is written as .part and renamed on success, so the presence of a final file
proves the write completed; that is what makes --resume safe. --verify re-checks every
sha256 and exits 2 on any missing or mismatched file.
Not included: Git LFS objects, release binaries (unless --assets), Actions logs,
Projects, Discussions and Packages.
All commands support these flags:
| Flag | Description |
|---|---|
--no-color |
Disable colored output (also respects NO_COLOR env var) |
--dry-run |
Preview changes without applying |
-y, --yes |
Skip confirmation prompt |
-v, --verbose |
Detailed output |
-h, --help |
Show help |
MIT