https://github.com/nsexceptional/gh-projects
A gh CLI extension for GitHub Projects (v2): concise, agent-friendly commands for boards, items, fields, and repo links.
https://github.com/nsexceptional/gh-projects
cli gh-extension github-projects projects-v2
Last synced: 19 days ago
JSON representation
A gh CLI extension for GitHub Projects (v2): concise, agent-friendly commands for boards, items, fields, and repo links.
- Host: GitHub
- URL: https://github.com/nsexceptional/gh-projects
- Owner: NSExceptional
- License: mit
- Created: 2026-06-07T18:39:16.000Z (21 days ago)
- Default Branch: main
- Last Pushed: 2026-06-07T19:27:09.000Z (21 days ago)
- Last Synced: 2026-06-07T21:08:21.562Z (21 days ago)
- Topics: cli, gh-extension, github-projects, projects-v2
- Language: Go
- Size: 32.2 KB
- Stars: 0
- Watchers: 0
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
# gh-projects
A [`gh`](https://cli.github.com) extension for working with **GitHub Projects (v2)** from the
command line — built to be concise enough for both humans and agents.
The built-in `gh project` commands can read and write Projects, but every
mutation makes you resolve a chain of opaque node IDs first (the project's
`PVT_…` id, the item's `PVTI_…` id, the field's id, and an option hash like
`df73e18b`), there's no convenience for "what's ready to work on," and you
can't toggle a task-list checkbox at all. `gh-projects` resolves those IDs for
you and adds the project-management verbs you actually use:
```sh
gh projects ready 4 # items in the "Todo" column
gh projects move 4 12 "Needs review" # by issue number + column name
gh projects check 4 12 "write tests" # tick a checkbox in the issue body
gh projects create 4 --title "Add caching" --label enhancement
```
You address **projects by number**, **items by issue number**, and
**columns/fields by name**. It reuses `gh`'s authentication, so there are no
tokens to manage.
## Install
```sh
gh extension install NSExceptional/gh-projects
```
Upgrade with `gh extension upgrade gh-projects`.
### Authentication scopes
Reading needs `read:project`; writing needs `project`:
```sh
gh auth refresh -s project # read + write
```
## Usage
`gh projects [] [flags]`
The project owner defaults to the authenticated user; pass `--owner `
for organizations or other users. Most read commands accept `--json` for
machine-readable output.
### Reading
| Command | Description |
| --- | --- |
| `list` | List projects (number, title, item count, linked repos) |
| `view ` | Project details, columns, field list, counts |
| `board ` | Items grouped by their `Status` column |
| `items [--status NAME]` | List items, optionally filtered by column |
| `ready ` | Shortcut for `items --status Todo` |
| `fields ` | Field definitions and single-select options |
| `links ` | Repositories the project is linked to |
### Items
| Command | Description |
| --- | --- |
| `create --title T [--repo O/R] [--body B] [--label L]… [--status S]` | Create a **real issue** and add it to the board (default way to make a task). `--repo` defaults to the project's sole linked repo. |
| `draft --title T [--body B] [--status S]` | Add a draft issue (project-only) |
| `add [--repo O/R]` | Add an existing issue/PR |
| `rm ` | Remove an item from the project (does not delete the issue) |
| `convert --repo O/R` | Convert a draft into a real issue |
### Field values & movement
| Command | Description |
| --- | --- |
| `move ` | Set the `Status` field (move between columns) |
| `set ` | Set any field value (single-select by option name, plus text/number/date/iteration) |
| `check ` | Tick the task-list box whose text uniquely matches |
| `uncheck ` | Untick it |
> **Addressing items:** anywhere an `` is expected, you can pass an issue/PR
> **number** (`12`), a **`PVTI_…` item id**, or a **unique substring of the item's
> title**. The title form is how you target **draft issues**, which have no number.
### Field definitions & repo links
| Command | Description |
| --- | --- |
| `field-create --name N --type TYPE [--option O]…` | Create a field (`text`, `number`, `date`, `single_select`) |
| `field-delete ` | Delete a field |
| `link ` | Link the project to a repo (so it appears on that repo's Projects tab) |
| `unlink ` | Remove that link |
## Notes
- A project is linked to **zero or more** repositories (it's many-to-many);
that link is what makes a project appear on a repo's Projects tab. Manage it
with `link` / `unlink` / `links`. (The web UI's "default repository" setting,
which only pre-selects a repo in dropdowns, is not exposed by GitHub's public
API, so it can't be read or changed here.)
- `check`/`uncheck` require the matched task text to be **unique** within the
body; they error rather than risk toggling the wrong box.
- **Read-after-write lag:** GitHub's project *items* connection is eventually
consistent, so an item you just created may take a few seconds to show up in
`items`/`board`. The write commands themselves use the returned IDs directly
and are unaffected.
## Development
```sh
go build -o gh-projects . # build the binary
go test ./... # fast unit tests (no network)
```
Live integration tests exercise the real API and are gated behind a build tag.
They need gh authenticated with the `project` scope and operate on a disposable
project, cleaning up everything they create:
```sh
# link/unlink, set all field types, add existing item, draft body toggle
GHP_TEST_PROJECT=5 go test -tags integration -run Integration ./internal/projects -v
# also the issue-creating paths (create/convert); point at a throwaway repo
GHP_TEST_PROJECT=5 GHP_TEST_ISSUE_REPO=you/throwaway \
go test -tags integration -run Integration ./internal/projects -v
```
Configuration env vars: `GHP_TEST_OWNER` (default: authed user), `GHP_TEST_PROJECT`
(default: 5), `GHP_TEST_REPO` (default: `NSExceptional/home`, only referenced not
modified), `GHP_TEST_ISSUE_REPO` (required to run create/convert tests).
## License
MIT — see [LICENSE](LICENSE).