Forgejo/Gitea CLI tool with agentic dev features
Find a file
sid 155ddb97ba
Some checks are pending
CI / lint (push) Waiting to run
CI / build (push) Waiting to run
CI / test (push) Waiting to run
CI / functional (push) Blocked by required conditions
fix(api): validate same-origin before forwarding auth on --paginate
Codex flagged: the --paginate loop rebuilt the next request from the raw
`Link: rel="next"` URL and reattached the bearer token without checking
that the next URL was on the same host. Forgejo emits same-origin next-
links in practice, but a buggy or malicious upstream could redirect us
to a foreign host, at which point the token would leak.

Now the loop:
- url.Parse the Link target.
- Resolve relative URLs against the original base (https://<host>/api/v1).
- Refuse to proceed if the resolved URL's scheme isn't https or its host
  doesn't match `host.Hostname`. The error names both the foreign URL
  and the expected origin so the user can tell why pagination stopped.

Verified: same-origin pagination still works (`--paginate` against
forgejo.zerova.net commits returns 44 across 22 pages).
2026-05-02 15:48:59 -06:00
.gitea/workflows rename fgj to fj 2026-04-26 08:16:52 -06:00
cmd fix(api): validate same-origin before forwarding auth on --paginate 2026-05-02 15:48:59 -06:00
internal fix(cmd): correctness + audit hardening across cmd/ + internal/ 2026-05-02 15:41:48 -06:00
tests/functional complete fgj → fj rename: env vars, config migration, docs 2026-04-26 08:23:48 -06:00
.editorconfig tools: add a basic .editorconfig 2026-01-05 12:57:57 +01:00
.gitignore fix .gitignore: update binary name, ignore bin/ dir 2026-04-26 08:17:15 -06:00
.golangci.yml chore: setup CI 2025-12-08 09:56:54 +01:00
CHANGELOG.md complete fgj → fj rename: env vars, config migration, docs 2026-04-26 08:23:48 -06:00
go.mod rename fgj to fj 2026-04-26 08:16:52 -06:00
go.sum feat: v0.3.0d — add PR checks, iostreams, aliases, and broad enhancements 2026-03-23 12:42:24 -06:00
LICENSE feat: initial version of the project 2025-12-08 09:49:07 +01:00
main.go rename fgj to fj 2026-04-26 08:16:52 -06:00
Makefile rename fgj to fj 2026-04-26 08:16:52 -06:00
README.md fix brew tap instructions to use public/homebrew-sid 2026-04-26 08:34:10 -06:00

fj - Forgejo/Gitea CLI Tool

Go Version License: MIT

fj is a command-line tool for working with Forgejo and Gitea instances. It brings pull requests, issues, and other forge concepts to the terminal, similar to what gh does for GitHub. This fork adds agentic dev features — raw API access, PR review workflows, structured error output, and machine-readable I/O for AI coding agents.

Forked from codeberg.org/romaintb/fj and hosted at forgejo.zerova.net/public/fj.

Features

  • Multi-instance support (works with any Forgejo or Gitea instance)
  • Pull request management (create, list, view, merge, diff, comment, review)
  • Issue tracking (create, list, view, comment, close, labels)
  • Repository operations (view, list, create, edit, clone, fork)
  • Label management (list, create, edit, delete)
  • Milestone management (list, view, create, edit, delete)
  • Wiki page management (list, view, create, edit, delete)
  • Issue dependencies (--add-dependency, --remove-dependency)
  • Forgejo Actions (workflow runs, watch/rerun/cancel, enable/disable, secrets, variables)
  • Releases (create, upload, delete)
  • Raw API access (fj api) for arbitrary REST calls
  • Shell completions (bash, zsh, fish, PowerShell) and man pages
  • JSON output (--json) for all list/view commands
  • Structured JSON error output (--json-errors) for machine consumption
  • 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

macOS (Homebrew)

brew tap public/sid git@forgejo.zerova.net:public/homebrew-sid.git
brew install fj

Using Go Install

go install forgejo.zerova.net/public/fj@latest

From Source

git clone https://forgejo.zerova.net/public/fj.git
cd fj
go build -o fj .

Quick Start

1. Authenticate

First, authenticate with your Forgejo or Gitea instance:

fj auth login

You'll be prompted for:

  • Instance hostname (default: codeberg.org)
  • Personal access token

To create a personal access token:

  1. Go to your instance (e.g., https://forgejo.zerova.net)
  2. Navigate to Settings > Applications > Generate New Token
  3. Give it appropriate permissions (repo, issue, etc.)
  4. Copy the token and paste it when prompted

2. Check Authentication Status

fj auth status

Auth Helpers

# Print the stored token for the current host
fj auth token

# Remove authentication for a host
fj auth logout

Usage

Repository Detection

fj 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
fj pr list              # Automatically uses current repo
fj issue list           # Automatically uses current repo
fj pr view 123          # Automatically uses current repo

# Or explicitly specify a repository with -R
fj pr list -R owner/repo

The tool reads .git/config to find the origin remote and extract both the owner/repo information and the instance hostname. 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)
fj pr list

# Or specify explicitly
fj pr list -R owner/repo

# Filter by state
fj pr list --state closed

# View a specific pull request
fj pr view 123

# Create a pull request
fj pr create -t "PR Title" -b "PR Description" -H feature-branch -B main

# Merge a pull request
fj pr merge 123 --merge-method squash

# View PR diff
fj pr diff 123

# View diff with color
fj pr diff 123 --color always

# Show only changed file names
fj pr diff 123 --name-only

# Show diffstat summary
fj pr diff 123 --stat

# Comment on a pull request
fj pr comment 123 -b "Looks good, minor nit on line 42"

# Comment from a file
fj pr comment 123 --body-file review-notes.md

# Approve a pull request
fj pr review 123 --approve -b "LGTM"

# Request changes
fj pr review 123 --request-changes -b "Please fix the error handling"

# Submit a review comment (neither approve nor request changes)
fj pr review 123 --comment -b "Some observations"

Issues

# List issues (auto-detects repo and hostname from git)
fj issue list

# Or specify explicitly
fj issue list -R owner/repo

# Filter by state
fj issue list --state all

# View an issue
fj issue view 456

# Create an issue
fj issue create -t "Issue Title" -b "Issue Description"

# Create an issue with labels
fj issue create -t "Issue Title" -b "Issue Description" -l bug -l enhancement

# Comment on an issue
fj issue comment 456 -b "My comment"

# Close an issue
fj issue close 456

# Close an issue with a comment
fj issue close 456 -c "Fixed in v2.0"

# Edit an issue (title, body, state, labels)
fj issue edit 456 -t "New Title"
fj issue edit 456 --add-label priority --remove-label bug

# Manage issue dependencies
fj issue edit 456 --add-dependency 123
fj issue edit 456 --remove-dependency 123

Labels

# List labels
fj label list

# Create a label
fj label create bug --color ff0000 -d "Something isn't working"

# Edit a label
fj label edit bug --name bugfix --color ee0000

# Delete a label
fj label delete bug

Milestones

# List milestones
fj milestone list
fj milestone list --state all

# View a milestone
fj milestone view "v1.0"

# Create a milestone with due date
fj milestone create "v2.0" -d "Next major release" --due 2026-06-01

# Edit a milestone
fj milestone edit "v2.0" --title "v2.0-rc1" --state closed

# Delete a milestone
fj milestone delete "v2.0"

Wiki

# List wiki pages
fj wiki list

# View a wiki page
fj wiki view "Home"

# Create a wiki page
fj wiki create "Setup Guide" -b "# Setup\n\nFollow these steps..."

# Create from file
fj wiki create "API Docs" --body-file docs/api.md

# Edit a wiki page
fj wiki edit "Home" -b "Updated content"

# Delete a wiki page
fj wiki delete "Old Page"

Repositories

# View repository details
fj repo view owner/repo

# List your repositories
fj repo list

# Create a repository
fj repo create my-repo
fj repo create my-repo -d "My project" --private --add-readme -g Go -l MIT

# Clone a repository
fj repo clone owner/repo

# Clone via SSH
fj repo clone owner/repo -p ssh

# Fork a repository
fj repo fork owner/repo

# Edit repository settings
fj repo edit owner/repo --public
fj repo edit owner/repo --private
fj repo edit owner/repo -d "New description" --homepage https://example.com
fj repo edit --default-branch develop
fj repo edit owner/repo --name new-name

# Rename a repository (shorthand)
fj repo rename new-name
fj repo rename new-name -R owner/old-name

Releases

# List releases
fj release list

# View a release (or use "latest")
fj release view v1.2.3

# Create a release with notes and optional assets
fj release create v1.2.3 -t "v1.2.3" -n "Release notes" ./dist/app.tar.gz

# Upload assets to an existing release
fj release upload v1.2.3 ./dist/app.tar.gz --clobber

# Delete a release (keeps the Git tag)
fj release delete v1.2.3

Forgejo Actions

# List workflows
fj actions workflow list

# View a workflow
fj actions workflow view ci.yml

# Run a workflow (trigger workflow_dispatch)
fj actions workflow run deploy.yml

# Run a workflow with inputs
fj actions workflow run deploy.yml -f environment=production -f version=1.2.3

# Run a workflow on a specific branch
fj actions workflow run deploy.yml -r feature-branch

# Enable or disable a workflow
fj actions workflow enable ci.yml
fj actions workflow disable ci.yml

# List workflow runs
fj actions run list

# View a specific run
fj actions run view 123

# View run with job details
fj actions run view 123 --verbose

# View run logs
fj actions run view 123 --log

# View specific job logs
fj actions run view 123 --job 456 --log

# Watch a run until completion
fj actions run watch 123

# Rerun a workflow run
fj actions run rerun 123

# Cancel a running workflow
fj actions run cancel 123

# List secrets
fj actions secret list

# Create a secret
fj actions secret create MY_SECRET

# Delete a secret
fj actions secret delete MY_SECRET

# List variables
fj actions variable list

# Get a variable
fj actions variable get MY_VAR

# Create a variable
fj actions variable create MY_VAR "value"

# Update a variable
fj actions variable update MY_VAR "new value"

# Delete a variable
fj actions variable delete MY_VAR

Raw API Access

# GET request (auto-detects owner/repo from git context)
fj api /repos/{owner}/{repo}/pulls

# POST with fields
fj api /repos/{owner}/{repo}/issues -X POST -f title="Bug report" -f body="Description"

# Explicit method and hostname
fj api /repos/myorg/myrepo/labels --hostname my-forgejo.example.com

# Read request body from file
fj api /repos/{owner}/{repo}/issues -X POST --input issue.json

# Read from stdin
echo '{"title":"test"}' | fj api /repos/{owner}/{repo}/issues -X POST --input -

# Include response headers
fj api /repos/{owner}/{repo} -i

# Suppress output (useful for DELETE)
fj api /repos/{owner}/{repo}/issues/123 -X DELETE --silent

Shell Completions and Man Pages

# Generate shell completion scripts
fj completion bash > /etc/bash_completion.d/fj
fj completion zsh > "${fpath[1]}/_fj"
fj completion fish > ~/.config/fish/completions/fj.fish

# Generate man pages to a directory
fj manpages --dir ~/.local/share/man/man1

JSON Output

Most list and view commands support --json for machine-readable output:

fj pr list --json
fj issue view 456 --json
fj release list --json
fj actions run list --json
fj actions workflow view ci.yml --json

# Get JSON output from PR comment/review
fj pr comment 123 -b "LGTM" --json
fj pr review 123 --approve -b "Ship it" --json

Structured Error Output

For machine consumption (ideal for AI agents and scripts), use --json-errors to get structured JSON errors on stderr:

# Errors are written to stderr as JSON
fj pr view 9999 --json-errors
# stderr: {"error":{"code":"not_found","message":"...","status":404}}

# Combine with --json for fully machine-readable I/O
fj pr list --json --json-errors

Configuration

Configuration is stored in ~/.config/fj/config.yaml:

hosts:
  forgejo.zerova.net:
    hostname: forgejo.zerova.net
    token: your_token_here
    user: your_username
    git_protocol: ssh
    match_dirs:
      - /              # catch-all: use this host when no git remote is detected
  codeberg.org:
    hostname: codeberg.org
    token: another_token
    user: another_username
    git_protocol: https
    match_dirs:
      - ~/repos/codeberg   # use this host for repos under this directory

Directory-Based Host Selection (match_dirs)

When you work with multiple Forgejo/Gitea instances, fj can automatically select the right host based on your current working directory — no --hostname flag needed.

Each host entry supports a match_dirs list of directory paths. When fj can't determine the host from a git remote, it finds the host whose match_dirs entry is the longest prefix match for your current directory.

hosts:
  work.example.com:
    # ...
    match_dirs:
      - ~/work          # any repo under ~/work uses this host
  personal.example.com:
    # ...
    match_dirs:
      - ~/personal
      - ~/side-projects  # multiple directories can map to the same host
  codeberg.org:
    # ...
    match_dirs:
      - /                # catch-all fallback (shortest prefix, lowest priority)
  • Paths support ~ expansion and symlink resolution
  • More specific (longer) paths always win over shorter ones
  • Use / as a catch-all to override the default codeberg.org fallback
  • On ties (same prefix length), the host appearing first in the config file wins

Environment Variables

  • FJ_HOST: Override the default instance (auto-detected from git remote if not set)
  • FJ_TOKEN: Provide authentication token

Hostname is resolved in this priority order:

  1. Command-specific flags (e.g., --hostname)
  2. FJ_HOST environment variable
  3. Auto-detected from git remote URL
  4. match_dirs lookup (longest prefix match against current directory)
  5. Default to codeberg.org

Command-line Flags

  • --hostname: Specify instance for a command (overrides auto-detection and environment variables)
  • --config: Use a custom config file

When working in a git repository, fj automatically detects the 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

fj is designed to work seamlessly with AI coding agents like Claude Code. Use --json and --json-errors for fully machine-readable I/O:

# Create PR from agent's changes
fj 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
)" --json

# Check PR status during development
fj pr list -R owner/repo --state open --json

# Review a PR diff, then approve
fj pr diff 123
fj pr review 123 --approve -b "LGTM" --json

# Post review feedback
fj pr comment 123 -b "Consider using a map here for O(1) lookup" --json

# Request changes with detailed feedback
fj pr review 123 --request-changes --body-file feedback.md --json

# Use raw API for anything not covered by commands
fj api /repos/{owner}/{repo}/topics --json-errors
fj api /repos/{owner}/{repo}/labels -X POST -f name=agent-reviewed -f color="#00ff00"

# Fully machine-readable error handling
fj pr view 9999 --json --json-errors 2>errors.json

Supported Instances

fj works with any Forgejo or Gitea instance, including:

  • Self-hosted Forgejo instances
  • Self-hosted Gitea instances
  • Codeberg.org

Contributing

Contributions are welcome! Please feel free to submit a Pull Request at forgejo.zerova.net/public/fj.

Missing Features / Roadmap

fj aims to be a drop-in replacement for gh when working with Forgejo and Gitea instances. While we've implemented the core features, some gh commands are not yet available:

Not Yet Implemented:

  • run delete - Delete a workflow run
  • run download - Download workflow run artifacts
  • pr checkout, pr close/reopen
  • pr checks, pr ready/draft
  • issue reopen, issue assign
  • release edit, release download, release generate-notes
  • repo delete

We welcome contributions to implement any of these features!

Acknowledgments

Based on fj by romaintb. Enhanced with agentic dev features for AI-assisted workflows.

License

MIT License