# fj - Forgejo/Gitea CLI Tool

[![Go Version](https://img.shields.io/badge/Go-1.23+-00ADD8?style=flat-square&logo=go)](https://golang.org)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg?style=flat-square)](LICENSE)

`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](https://codeberg.org/romaintb/fj) and hosted at [forgejo.zerova.net/public/fj](https://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)

```bash
brew tap sid/fj https://forgejo.zerova.net/sid/homebrew-fj.git
brew install fj
```

### Using Go Install

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

### From Source

```bash
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:

```bash
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

```bash
fj auth status
```

### Auth Helpers

```bash
# 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`:

```bash
# 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

```bash
# 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

```bash
# 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

```bash
# 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

```bash
# 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

```bash
# 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

```bash
# 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

```bash
# 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

```bash
# 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

```bash
# 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

```bash
# 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:

```bash
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:

```bash
# 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`:

```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.

```yaml
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

- `FGJ_HOST`: Override the default instance (auto-detected from git remote if not set)
- `FGJ_TOKEN`: Provide authentication token

Hostname is resolved in this priority order:
1. Command-specific flags (e.g., `--hostname`)
2. `FGJ_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:

```bash
# 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](https://codeberg.org)

## Contributing

Contributions are welcome! Please feel free to submit a Pull Request at [forgejo.zerova.net/public/fj](https://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](https://codeberg.org/romaintb/fj). Enhanced with agentic dev features for AI-assisted workflows.

## License

MIT License
