gh for GitHub, but for Forgejo instances.
- Go 96.5%
- Shell 3.1%
- Makefile 0.4%
fgj - Forgejo CLI Tool
Go Version CI Status License: MIT
fgj is a command-line tool for working with Forgejo instances (including Codeberg.org). It brings pull requests, issues, and other Forgejo concepts to the terminal, similar to what gh does for GitHub.
Features
- Multi-instance support (works with any Forgejo instance)
- Pull request management (create, list, view, edit, merge, checkout, comment, review, checks)
- Issue tracking (create, list, view, comment, close, labels)
- Repository operations (view, list, create, clone, fork)
- Forgejo Actions (workflow runs with filters, watch/rerun/cancel/delete, run artifacts, enable/disable, runners, repo/org secrets and variables)
- Generic authenticated API access (
fgj api, likegh api) for any endpoint fgj has no dedicated command for - Releases (create, upload, delete)
- Milestones (create, list, edit, delete)
- Labels (create, list, edit, delete)
- Shell completions (bash, zsh, fish, PowerShell) and man pages
- JSON output (
--json) for all list/view commands - Automatic repository and hostname detection from git context
- Secure authentication with personal access tokens
- XDG Base Directory compliant config location
- AI coding agent friendly
Installation
Arch Linux (AUR)
fgj is available in the Arch User Repository:
yay -S fgj
macOS (Homebrew)
brew tap romaintb/fgj https://codeberg.org/romaintb/homebrew-fgj.git
brew install fgj
Using Go Install
go install codeberg.org/romaintb/fgj@latest
Note: The module path tracks upstream (
codeberg.org/romaintb/fgj) so the fork upstreams cleanly, sogo installalways fetches the upstream build —go installfrom a fork URL fails with a module-path mismatch. To get a fork-specific build, use the release binaries orscripts/install.sh.
Other Distributions
We'd love your help packaging fgj for other distributions! If you're interested in creating packages for Debian, Ubuntu, Fedora, or any packaging systems, please open an issue or reach out.
Quick Start
1. Authenticate
First, authenticate with your Forgejo instance:
fgj auth login
If your Git remotes use a different SSH host than the Forgejo API host:
fgj auth login --hostname my-forgejo.com --git-host git.my-forgejo.internal
You'll be prompted for:
- Forgejo instance hostname (default: codeberg.org)
- Personal access token
To create a personal access token:
- Go to your Forgejo instance (e.g., https://codeberg.org)
- Navigate to Settings > Applications > Generate New Token
- Give it appropriate permissions (repo, issue, etc.)
- Copy the token and paste it when prompted
2. Check Authentication Status
fgj auth status
Auth Helpers
# Print the stored token for the current host
fgj auth token
# Remove authentication for a host
fgj auth logout
Usage
Repository Detection
fgj automatically detects the repository from your git context, similar to gh:
# When inside a git repository, no -R flag needed!
cd /path/to/your/repo
fgj pr list # Automatically uses current repo
fgj issue list # Automatically uses current repo
fgj pr view 123 # Automatically uses current repo
# Or explicitly specify a repository with -R
fgj pr list -R owner/repo
The tool reads .git/config to find the origin remote and extract both the owner/repo information and the Git remote hostname. If the remote hostname is listed in a configured host's git_hosts, fgj uses that configured Forgejo API host. If you're not in a git repository, you'll need to use the -R flag.
Pull Requests
# List pull requests (auto-detects repo and hostname from git)
fgj pr list
# Or specify explicitly
fgj pr list -R owner/repo
# Filter by state
fgj pr list --state closed
# Filter by base or head branch (combine with each other and with --state)
fgj pr list --base main
fgj pr list --head feature-branch
fgj pr list --base main --head feature-branch
# View a specific pull request
fgj pr view 123
# Create a pull request
fgj pr create -t "PR Title" -b "PR Description" -H feature-branch -B main
# Merge a pull request
fgj pr merge 123 --merge-method squash
# Edit a pull request's title and body
fgj pr edit 123 -t "New title" -b "New description"
# Edit a pull request's body from a file (or stdin with -)
fgj pr edit 123 -F body.md
# Change the base branch
fgj pr edit 123 -B main
# Manage labels and assignees
fgj pr edit 123 --add-label bug --remove-label wontfix
fgj pr edit 123 --add-assignee @me --remove-assignee someone
# Manage reviewers
fgj pr edit 123 --add-reviewer monalisa --remove-reviewer hubot
# Comment on a pull request
fgj pr comment 123 -b "LGTM"
# Comment with body from a file (or stdin with -)
fgj pr comment 123 -F review.md
# Review a pull request (one of --approve/-a, --comment/-c, --request-changes/-r)
fgj pr review 123 --approve
fgj pr review 123 --request-changes -b "Please add tests"
# Review with inline comments from a JSON array (or stdin with -)
# [{"path": "main.go", "new_position": 12, "body": "nit: rename"}]
fgj pr review 123 --comment --comments review-comments.json
# List reviews and their inline comments
fgj pr review list 123
fgj pr review comments 123
# Check out a pull request locally (alias: co)
fgj pr checkout 123
# Pick a custom local branch name
fgj pr checkout 123 -b review/pr-123
# Reset an existing local branch that has diverged from the PR
fgj pr checkout 123 --force
# Check out with a detached HEAD (no local branch created)
fgj pr checkout 123 --detach
# Check out a PR from a different repository (works for forks too)
fgj pr checkout 123 -R owner/repo
# Show open PRs relevant to you: current branch, created by you, assigned to you
fgj pr status
# Status for a different repository
fgj pr status -R owner/repo
# Cap PRs fetched per section (default 30)
fgj pr status -L 10
# JSON output for scripting
fgj pr status --json
# Show CI status checks for a pull request's head commit. Each line reports one
# status context; the combined state is printed last. Exit codes gate scripts
# and CI: 1 when the combined state is "failure" or "error", 8 while checks are
# still pending, 0 otherwise.
fgj pr checks 123
# JSON output for scripting
fgj pr checks 123 --json
Issues
# List issues (auto-detects repo and hostname from git)
fgj issue list
# Or specify explicitly
fgj issue list -R owner/repo
# Filter by state
fgj issue list --state all
# Filter by label (repeatable or comma-separated)
fgj issue list -l bug
fgj issue list -l bug,enhancement
# View an issue
fgj issue view 456
# Create an issue
fgj issue create -t "Issue Title" -b "Issue Description"
# Create an issue with labels
fgj issue create -t "Issue Title" -b "Issue Description" -l bug -l enhancement
# Comment on an issue
fgj issue comment 456 -b "My comment"
# Close an issue
fgj issue close 456
# Close an issue with a comment
fgj issue close 456 -c "Fixed in v2.0"
# Edit an issue (title, body, state, labels)
fgj issue edit 456 -t "New Title"
fgj issue edit 456 --add-label priority --remove-label bug
Repositories
# View repository details
fgj repo view owner/repo
# View repository details as JSON
fgj repo view owner/repo --json
# List your repositories
fgj repo list
# Create a repository
fgj repo create my-repo
fgj repo create my-repo -d "My project" --private --add-readme -g Go -l MIT
# Clone a repository
fgj repo clone owner/repo
# Clone via SSH
fgj repo clone owner/repo -p ssh
# Fork a repository
fgj repo fork owner/repo
Releases
# List releases
fgj release list
# View a release (or use "latest")
fgj release view v1.2.3
# Create a release with notes and optional assets
fgj release create v1.2.3 -t "v1.2.3" -n "Release notes" ./dist/app.tar.gz
# Upload assets to an existing release
fgj release upload v1.2.3 ./dist/app.tar.gz --clobber
# Delete a release (keeps the Git tag), prompts for confirmation
fgj release delete v1.2.3
# Skip confirmation
fgj release delete v1.2.3 --yes
Milestones
# List open milestones (auto-detects repo)
fgj milestone list
# Filter by state
fgj milestone list --state closed
fgj milestone list --state all
# Create a milestone
fgj milestone create "v1.0" -d "First stable release" --due 2026年06月30日
# Create or update if it already exists
fgj milestone create "v1.0" -d "Updated description" -f
# Edit a milestone (rename, change description, due date, or state)
fgj milestone edit "v1.0" --title "v1.0.0"
fgj milestone edit "v1.0.0" --due 2026年07月15日
fgj milestone edit "v1.0.0" --state closed
# Delete a milestone (prompts for confirmation)
fgj milestone delete "v1.0.0"
fgj milestone delete "v1.0.0" --yes
Labels
# List labels (auto-detects repo)
fgj label list
# Create a label
fgj label create bug -c ff0000 -d "Something isn't working"
# Create an exclusive (scoped) label
fgj label create priority/high -c ff8800 --exclusive
# Create or update if it already exists
fgj label create bug -c ee0701 -f
# Edit a label
fgj label edit bug -n defect -c cc0000
# Delete a label (prompts for confirmation)
fgj label delete wontfix
fgj label delete wontfix -y
Forgejo Actions
# List workflows. Workflows resolve the way the server does: the first of
# .forgejo/workflows, .gitea/workflows, or .github/workflows that exists wins
# and shadows the rest — the directories are not merged.
fgj actions workflow list
# View a workflow
fgj actions workflow view ci.yml
# Run a workflow (trigger workflow_dispatch)
# On Forgejo v16+ the dispatch also prints the started run's number and ID
fgj actions workflow run deploy.yml
# Run a workflow with inputs
fgj actions workflow run deploy.yml -f environment=production -f version=1.2.3
# Run a workflow on a specific branch
fgj actions workflow run deploy.yml -r feature-branch
# Enable or disable a workflow
fgj actions workflow enable ci.yml
fgj actions workflow disable ci.yml
# List workflow runs
fgj actions run list
# Filter the list; the filters combine, so a run must match all of them.
# --status and --event repeat or accept a comma-separated list.
fgj actions run list --status failure --status cancelled
fgj actions run list --event push --branch main --workflow ci.yml
# The RUN column shows the per-repo run number from the web UI (e.g. #42); the
# ID column shows the internal run ID that the other run subcommands take.
# View a specific run (the argument is the internal run ID)
fgj actions run view 123
# Address any run subcommand (view, watch, cancel, delete, rerun) by the web UI
# run number instead of the internal ID with --number/-n
fgj actions run view 42 --number
# View run with job details
fgj actions run view 123 --verbose
# View run logs
fgj actions run view 123 --log
# View logs for only the run's failed jobs (prints a note and exits 0 when none failed)
fgj actions run view 123 --log-failed
# View a specific job's logs by its run-job ID (as shown by --verbose)
fgj actions run view 123 --job 456 --log
# Watch a run until completion
fgj actions run watch 123
# Explain how to rerun a run. Forgejo exposes no rerun API, so this prints the
# web URL for "Re-run all jobs" and always exits non-zero.
fgj actions run rerun 123
# Cancel a running workflow
fgj actions run cancel 123
# Delete a run (only completed runs; prompts for confirmation)
fgj actions run delete 123
fgj actions run delete 123 --yes
# List workflow run artifacts for the whole repository
fgj actions artifact list
# List a single run's artifacts, filtered by name
fgj actions artifact list --run 123 --name build-output
# View an artifact by its ID
fgj actions artifact view 42
# Download an artifact's zip (defaults to <artifact-name>.zip; -o overrides)
fgj actions artifact download 42
fgj actions artifact download 42 -o build.zip
# Delete an artifact (prompts for confirmation; the server removes it asynchronously)
fgj actions artifact delete 42
fgj actions artifact delete 42 --yes
# List repository runners
fgj actions runner list
# List repository and inherited runners visible to this repository
fgj actions runner list --visible
# Register a runner and print its credentials
fgj actions runner register my-runner
# Register an ephemeral runner and print its id, uuid, and token as JSON
fgj actions runner register my-runner --ephemeral --json
# Delete a repository runner
fgj actions runner delete 123
# List secrets (repository-scoped)
fgj actions secret list
# Create a secret: value from --body, piped stdin, or an interactive prompt
fgj actions secret create MY_SECRET -b "s3cret"
echo "s3cret" | fgj actions secret create MY_SECRET
# Delete a secret
fgj actions secret delete MY_SECRET
# Organization-scoped secrets (mutually exclusive with -R; needs a
# write:organization-scoped token)
fgj actions secret list --org my-org
echo "s3cret" | fgj actions secret create SHARED_TOKEN --org my-org
fgj actions secret delete SHARED_TOKEN --org my-org
# List variables
fgj actions variable list
# Get a variable
fgj actions variable get MY_VAR
# Create a variable
fgj actions variable create MY_VAR "value"
# Update a variable
fgj actions variable update MY_VAR "new value"
# Delete a variable
fgj actions variable delete MY_VAR
# Organization-scoped variables
fgj actions variable list --org my-org
fgj actions variable create REGION us-east --org my-org
Raw API Access
fgj api is a generic authenticated passthrough to the Forgejo REST API, mirroring gh api.
It covers any endpoint that fgj has no dedicated command for, and its output is printed
verbatim so you can pipe it to jq.
# GET a path ("/api/v1" is added when omitted); pipe to jq as usual
fgj api repos/owner/repo | jq .default_branch
# Send typed parameters with -F (true/false/null/integers become JSON literals,
# @file or @- reads a value) or strings with -f; this defaults to POST
fgj api repos/owner/repo/issues -F title='Bug' -F body=@report.md -f labels[]=bug
# Choose the method explicitly
fgj api -X PATCH repos/owner/repo/issues/1 -F state=closed
# Send a raw request body from a file (or stdin with -); field flags become query params
fgj api repos/owner/repo/contents/README.md -X PUT --input payload.json
# Add request headers, and include the response status line and headers
fgj api -i -H 'Accept: application/json' repos/owner/repo
# Follow pagination; --slurp wraps every page in a single JSON array
fgj api --paginate --slurp repos/owner/repo/commits | jq 'add | length'
# Reach endpoints fgj has no dedicated command for, e.g. branch protections
# (Forgejo's equivalent of rulesets) and combined commit status / checks
fgj api repos/owner/repo/branch_protections
fgj api repos/owner/repo/commits/<sha>/status
fgj api does not embed a jq engine; pipe the JSON output to jq for filtering.
Shell Completions and Man Pages
# Generate shell completion scripts
fgj completion bash > /etc/bash_completion.d/fgj
fgj completion zsh > "${fpath[1]}/_fgj"
fgj completion fish > ~/.config/fish/completions/fgj.fish
# Generate man pages to a directory
fgj manpages --dir ~/.local/share/man/man1
JSON Output
Most list and view commands support --json for machine-readable output:
fgj pr list --json
fgj pr review list 123 --json
fgj pr checks 123 --json
fgj issue view 456 --json
fgj repo view owner/repo --json
fgj release list --json
fgj actions run list --json
fgj actions artifact list --json
fgj actions workflow view ci.yml --json
fgj actions runner list --json
For any endpoint without a dedicated --json command, use fgj api.
Configuration
Configuration is stored in ~/.config/fgj/config.yaml:
hosts:codeberg.org:hostname:codeberg.orgtoken:your_token_hereuser:your_usernamegit_protocol:httpsmy-forgejo.com:hostname:my-forgejo.comtoken:another_tokenuser:another_usernamegit_protocol:sshgit_hosts:- git.my-forgejo.internalgit_hosts lists Git remote hostnames that should resolve to the configured
Forgejo API host. Use it when SSH remotes go through aliases, private network
names, jump hosts, or vanity Git endpoints while API requests still use
hostname.
Environment Variables
FGJ_HOST: Override the default Forgejo instance (auto-detected from git remote if not set)FGJ_TOKEN: Provide authentication token
Hostname is resolved in this priority order:
- Command-specific flags (e.g.,
--hostname) FGJ_HOSTenvironment variable- Auto-detected from git remote URL, including configured
git_hostsaliases - Default to
codeberg.org
Token is resolved in this priority order:
FGJ_TOKENenvironment variable- Stored token from
~/.config/fgj/config.yaml
Command-line Flags
--hostname: Specify Forgejo instance for a command (overrides auto-detection and environment variables)--config: Use a custom config file
When working in a git repository, fgj automatically detects the Forgejo instance from your origin remote URL, so you typically don't need to specify --hostname unless working with multiple instances.
Use with AI Coding Agents
fgj is designed to work seamlessly with AI coding agents like Claude Code. Common patterns:
# Create PR from agent's changes
fgj pr create -R owner/repo -t "feat: add new feature" -b "$(cat <<EOF
## Summary
- Added new feature X
- Fixed bug Y
Generated with AI assistance
EOF
)"
# Check PR status during development
fgj pr list -R owner/repo --state open
# View PR details for review
fgj pr view 123 -R owner/repo
Supported Forgejo Instances
fgj works with any Forgejo instance, including:
- Codeberg.org (default)
- Self-hosted Forgejo instances
- Gitea instances (compatible API)
Contributing
Contributions are welcome! Please feel free to submit a Pull Request. See CONTRIBUTORS.md for a list of people who have contributed to the project.
Missing Features / Roadmap
fgj aims to be a drop-in replacement for gh when working with Forgejo instances. While we've implemented the core features, some gh commands are not yet available:
Not Yet Implemented:
pr reopen,pr diff,pr ready/draftissue reopen,issue assignrelease edit,release download,release generate-notesrepo delete,repo rename,repo visibility
A pull request's combined checks now have a dedicated command (fgj pr checks). Other
endpoints without one — such as branch protections (Forgejo's equivalent of rulesets) and the
combined commit status of an arbitrary commit — remain reachable through
fgj api.
We welcome contributions to implement any of these features! Please check the issues or create a new one to discuss implementation before starting work.
License
MIT License