GitHub Commands

Overview

The netapi github commands provide CLI access to GitHub’s REST API. All commands support -f json for jq piping.

Prerequisites

# Set GitHub token (personal access token)
export GITHUB_TOKEN="ghp_..."

# Or use gh CLI token
export GH_TOKEN="ghp_..."

Create token at: github.com/settings/tokens

Commands (26 total)

Command Description

user

Get user info

rate-limit

Check API rate limit

repos

List user repositories

repo

Get repository details

search

Search repositories

branches

List branches

commits

List commits

releases

List releases

tags

List tags

languages

Get language breakdown

contributors

List contributors

issues

List issues

issue

Get issue details

prs

List pull requests

pr

Get PR details

pr-files

List PR files

orgs

List organizations

org

Get org details

org-repos

List org repos

org-members

List org members

workflows

List workflows

runs

List workflow runs

gists

List gists

gist

Get gist details

starred

List starred repos

api

Raw API request

JSON Output

All commands support -f json for machine-readable output:

netapi github repos -f json
netapi github repo owner/repo -f json
netapi github prs owner/repo -f json

jq Patterns

Exploring Structure

# See all keys in first object
netapi github repos -f json | jq '.[0] | keys'

# Keys with types
netapi github repos -f json | jq '.[0] | to_entries | .[] | "\(.key): \(.value | type)"'

# Full first object (all keys + values)
netapi github repos -f json | jq '.[0]'

Repository Operations

# List all repo names
netapi github repos -f json | jq -r '.[].full_name'

# Filter by language
netapi github repos -f json | jq -r '.[] | select(.language == "Python") | .full_name'

# Get specific fields
netapi github repos -f json | jq '.[] | {name: .full_name, stars: .stargazers_count, language}'

# Sort by stars
netapi github repos -f json | jq 'sort_by(-.stargazers_count) | .[] | "\(.full_name): \(.stargazers_count) stars"'

# Private repos only
netapi github repos -f json | jq '.[] | select(.private) | .full_name'

# Repos updated in last 7 days
netapi github repos -f json | jq --arg d "$(date -d '7 days ago' -Iseconds)" \
  '.[] | select(.updated_at > $d) | .full_name'

Single Repository

# Full repo details
netapi github repo EvanusModestus/netapi -f json | jq '.'

# Summary object
netapi github repo EvanusModestus/netapi -f json | jq '{
  name: .full_name,
  stars: .stargazers_count,
  forks: .forks_count,
  issues: .open_issues_count,
  language: .language,
  default_branch: .default_branch,
  created: .created_at[0:10],
  updated: .updated_at[0:10]
}'

# Clone URL
netapi github repo EvanusModestus/netapi -f json | jq -r '.ssh_url'

Language Breakdown

# All languages with bytes
netapi github languages EvanusModestus/netapi -f json | jq '.'

# Sorted by size
netapi github languages EvanusModestus/netapi -f json | jq 'to_entries | sort_by(-.value) | .[] | "\(.key): \(.value)"'

# As percentage
netapi github languages EvanusModestus/netapi -f json | jq '
  to_entries |
  (map(.value) | add) as $total |
  map({key, pct: (.value / $total * 100 | floor)}) |
  sort_by(-.pct) |
  .[] | "\(.key): \(.pct)%"
'

Commits

# Recent commits - short format
netapi github commits EvanusModestus/netapi -l 10 -f json | jq -r '.[] | "\(.sha[0:8]) \(.commit.author.name): \(.commit.message | split("\n")[0])"'

# Commits by author
netapi github commits EvanusModestus/netapi -f json | jq -r '.[] | .commit.author.name' | sort | uniq -c | sort -rn

# Commits with date
netapi github commits EvanusModestus/netapi -f json | jq '.[] | {
  sha: .sha[0:8],
  author: .commit.author.name,
  date: .commit.author.date[0:10],
  message: (.commit.message | split("\n")[0])
}'

Pull Requests

# Open PRs - titles only
netapi github prs EvanusModestus/netapi -f json | jq -r '.[] | "#\(.number): \(.title)"'

# PRs with stats
netapi github prs EvanusModestus/netapi -f json | jq '.[] | {
  number,
  title,
  author: .user.login,
  branch: .head.ref,
  base: .base.ref,
  created: .created_at[0:10]
}'

# Get specific PR
netapi github pr EvanusModestus/netapi 42 -f json | jq '{
  title,
  state,
  author: .user.login,
  commits,
  additions,
  deletions,
  changed_files
}'

# PR files with diff stats
netapi github pr-files EvanusModestus/netapi 42 -f json | jq -r '.[] | "\(.filename): +\(.additions) -\(.deletions)"'

# Only modified files (not added/removed)
netapi github pr-files EvanusModestus/netapi 42 -f json | jq -r '.[] | select(.status == "modified") | .filename'

Issues

# Open issues - titles
netapi github issues EvanusModestus/netapi -f json | jq -r '.[] | "#\(.number): \(.title)"'

# Issues with labels
netapi github issues EvanusModestus/netapi -f json | jq '.[] | {
  number,
  title,
  author: .user.login,
  labels: [.labels[].name]
}'

# Filter by label
netapi github issues EvanusModestus/netapi -f json | jq '.[] | select(.labels | map(.name) | index("bug"))'

# Issue details
netapi github issue EvanusModestus/netapi 123 -f json | jq '{
  title,
  state,
  author: .user.login,
  body,
  comments,
  labels: [.labels[].name]
}'

Workflows & Actions

# List workflows
netapi github workflows EvanusModestus/netapi -f json | jq '.workflows[] | {name, state, path}'

# Recent runs with status
netapi github runs EvanusModestus/netapi -f json | jq '.workflow_runs[] | {
  id,
  name,
  status,
  conclusion,
  branch: .head_branch,
  created: .created_at[0:10]
}'

# Failed runs only
netapi github runs EvanusModestus/netapi -f json | jq '.workflow_runs[] | select(.conclusion == "failure") | {name, branch: .head_branch}'

Organizations

# List orgs
netapi github orgs -f json | jq -r '.[].login'

# Org repos sorted by stars
netapi github org-repos anthropics -f json | jq 'sort_by(-.stargazers_count) | .[] | "\(.name): \(.stargazers_count) stars"'

# Org members
netapi github org-members anthropics -f json | jq -r '.[].login'
# Search repos
netapi github search "network automation" -f json | jq '.items[] | {name: .full_name, stars: .stargazers_count, language}'

# Search with language filter
netapi github search "network automation language:python" -f json | jq '.items[] | .full_name'

# Top results by stars
netapi github search "ansible network" -f json | jq '.items | sort_by(-.stargazers_count)[0:5] | .[] | "\(.full_name): \(.stargazers_count)"'

Rate Limits

# Check rate limit
netapi github rate-limit -f json | jq '.resources.core | "Remaining: \(.remaining)/\(.limit)"'

# All rate limits
netapi github rate-limit -f json | jq '.resources | to_entries | .[] | "\(.key): \(.value.remaining)/\(.value.limit)"'

Gists

# List gists
netapi github gists -f json | jq '.[] | {id: .id[0:8], description, files: [.files | keys[]]}'

# Get gist content
netapi github gist abc123 -f json | jq '.files | to_entries | .[] | {name: .key, content: .value.content}'

Starred Repos

# Starred repos
netapi github starred -f json | jq -r '.[].full_name'

# Starred by language
netapi github starred -f json | jq -r '.[] | .language // "unknown"' | sort | uniq -c | sort -rn

Raw API Access

# Any GET endpoint
netapi github api /users/torvalds | jq '.'

# Repository topics
netapi github api /repos/EvanusModestus/netapi/topics | jq '.names'

# Repo README
netapi github api /repos/EvanusModestus/netapi/readme | jq -r '.content' | base64 -d

Environment Variables

Variable Description

GITHUB_TOKEN

Personal access token (required)

GH_TOKEN

Alternative token variable (gh CLI compatible)

Useful jq Recipes

Export to CSV

netapi github repos -f json | jq -r '.[] | [.full_name, .language, .stargazers_count] | @csv'

Create Markdown Table

netapi github repos -f json | jq -r '["Name", "Language", "Stars"], (.[] | [.full_name, .language, .stargazers_count]) | @tsv' | column -t -s$'\t'

Count by Field

# Repos by language
netapi github repos -f json | jq 'group_by(.language) | map({lang: .[0].language, count: length}) | sort_by(-.count)'

Filter and Transform

# Active repos (pushed in last 30 days) with stats
netapi github repos -f json | jq --arg d "$(date -d '30 days ago' -Iseconds)" '
  .[] | select(.pushed_at > $d) | {
    name: .full_name,
    pushed: .pushed_at[0:10],
    stars: .stargazers_count
  }
'