A quicker way to drive GitHub Projects

GitHub Projects lets teams plan and track work without leaving the platform where that work happens. Now the project command is part of the official gh CLI, replacing the previously archived gh-projects extension. It makes managing project structure, fields, and items possible directly from the terminal or from GitHub Actions.

Every project is made of three components that map neatly to the new command’s subcommands:

  • Project – belongs to a user or organization and is identified by a project number. Handled by project create, project copy, project list, and project view.
  • Project fields – typed metadata like Status, Assignee, or Number, with values set per item. Use project field-create, project field-list, and project field-delete.
  • Project items – draft issues, issues, or pull requests. Drafts belong to a single project; issues and PRs can appear in multiple ones. Manage them with project item-add, item-edit, item-archive, and item-list.

The full command reference is in the CLI manual.

Check your auth scope first

The project command relies on the project auth scope, which is not among the default scopes granted to a gh token. To see what your current token supports:

$ gh auth status
github.com
✓ Logged in to github.com as mntlty (keyring)
✓ Git operations for github.com configured to use https protocol.
✓ Token: gho_************************************
✓ Token scopes: gist, read:org, repo, workflow

If project is missing, run the following and follow the interactive prompts:

$ gh auth refresh -s project

Inside GitHub Actions, you’ll need to supply a token that includes the project scope, following the options in the automatic token authentication docs.

Common workflows from the terminal

The examples below use the GitHub public roadmap project (github organization, project 4247) and a user-owned project for demonstration.

List projects owned by the current user (no --owner flag needed):

$ gh project list
NUMBER TITLE STATE ID
1 my first project open PVT_kwxxx
2 @mntlty's second project open PVT_kwxxx

Create a project under your own account:

$ gh project create --owner mntlty --title 'my project'

View the roadmap project:

$ gh project view --owner github 4247

Title

GitHub public roadmap

## Description

--

## Visibility

Public

## URL

<https://github.com/orgs/github/projects/4247>

## Item count

208

## Readme

--

## Field Name (Field Type)

Title (ProjectV2Field)

Assignees (ProjectV2Field)

Status (ProjectV2SingleSelectField)

Labels (ProjectV2Field)

Repository (ProjectV2Field)

Milestone (ProjectV2Field)

Linked pull requests (ProjectV2Field)

Reviewers (ProjectV2Field)

Tracks (ProjectV2Field)

Tracked by (ProjectV2Field)

List items in the roadmap project:

$ gh project item-list --owner github 4247

TYPE TITLE NUMBER REPOSITORY ID
Issue Kotlin security analysis support in CodeQL code scanning
(public beta) 207 github/roadmap
PVTI_lADNJr_NE13OAALQgw
Issue Swift security analysis support in CodeQL code scanning
(beta) 206 github/roadmap
PVTI_lADNJr_NE13OAALQhA
Issue Fine-grained PATs (v2 PATs) - [Public Beta]
184 github/roadmap PVTI_lADNJr_NE13OAALQmw

Clone the roadmap project’s structure into a new project:

$ gh project copy 4247 --source-owner github --target-owner mntlty --title 'my roadmap'

https://github.com/users/mntlty/projects/1

When running in an interactive terminal, commands that require an owner or project number display a picker if you omit those arguments.

JSON output for scripting

Every project subcommand accepts --format=json, giving you more fields to work with for automation, piping, and reporting:

$ gh project view --owner github 4247 --format=json
{"number":4247,"url":"<https://github.com/orgs/github/projects/4247","shortDescription":"", "public":true,"closed":false,"title":"GitHub> public roadmap","id":"PVT_kwDNJr_NE10","readme":"","items":{"totalCount":208},"fields":{"totalCount":10},"owner":{"type":"Organization","login":"github"}}%

Combined with a tool like jq, you can build precise queries. For instance, to grab the URLs of all roadmap issues with the status “Future”:

$ gh project item-list --owner github 4247 --format=json | jq '.items[] |
select(.status=="Future" and .content.type == "Issue") | .content.url'

"<https://github.com/github/roadmap/issues/188>"
"<https://github.com/github/roadmap/issues/187>"
"<https://github.com/github/roadmap/issues/166>"

Automation with GitHub Actions

The project command works just as well in CI. A workflow triggered on workflow_dispatch, for example, can iterate over a user’s projects and close those without any items:

on: 
  workflow_dispatch:

jobs:
  close_empty:
    runs-on: ubuntu-latest
    env:
      GH_TOKEN: ${{ secrets.PROJECT_TOKEN }}
    steps:
      - run: |
          gh project list --owner mntlty --format=json \
          | jq '.projects[] | select(.items.totalCount == 0) | .number' \
          | xargs -n1 gh project close --owner mntlty 

GitHub Actions runners always have the current gh release pre-installed, so no setup step is needed.

Migrating from the gh-projects extension

If you’re still on the gh-projects extension, nothing breaks immediately, but that repository is archived and will not gain new features. Moving to the built-in command is straightforward:

  • Upgrade gh to the latest version.
  • Replace --user/--org with --owner, where the value is the login of the project’s owner (a user or an organization).
  • Change gh projects to gh project in your scripts and aliases.

You can also remove the old extension entirely:

$ gh ext remove gh-projects

The latest CLI release and feedback channels are listed in the release notes and discussions, respectively.