rename fgj to fj

Module path, binary name, config dir, help text, and docs
all updated from fgj-sid/fgj to fj.
This commit is contained in:
sid 2026-04-26 08:16:52 -06:00
parent a6cf9a7096
commit bc43f6e5a5
34 changed files with 479 additions and 479 deletions

View file

@ -72,7 +72,7 @@ jobs:
- name: Build production binary
run: |
make build
echo "Binary built at: $(pwd)/bin/fgj"
echo "Binary built at: $(pwd)/bin/fj"
- name: Run functional tests
run: go test -v -race -tags=functional ./tests/functional/...

View file

@ -24,7 +24,7 @@ jobs:
- name: Build production binary
run: |
make build
echo "Binary built at: $(pwd)/bin/fgj"
echo "Binary built at: $(pwd)/bin/fj"
- name: Run functional tests
run: go test -v -race -tags=functional ./tests/functional/...

View file

@ -10,45 +10,45 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Added
#### Label Management
- `fgj label list` - List repository labels
- `fgj label create` - Create a label with color and description
- `fgj label edit` - Edit label name, color, or description
- `fgj label delete` - Delete a label
- `fj label list` - List repository labels
- `fj label create` - Create a label with color and description
- `fj label edit` - Edit label name, color, or description
- `fj label delete` - Delete a label
#### Milestone Management
- `fgj milestone list` - List milestones with state filtering
- `fgj milestone view` - View milestone details
- `fgj milestone create` - Create a milestone with description and due date
- `fgj milestone edit` - Edit milestone title, description, due date, or state
- `fgj milestone delete` - Delete a milestone
- `fj milestone list` - List milestones with state filtering
- `fj milestone view` - View milestone details
- `fj milestone create` - Create a milestone with description and due date
- `fj milestone edit` - Edit milestone title, description, due date, or state
- `fj milestone delete` - Delete a milestone
#### Wiki Management
- `fgj wiki list` - List wiki pages
- `fgj wiki view` - View wiki page content
- `fgj wiki create` - Create a wiki page from flag or file
- `fgj wiki edit` - Edit a wiki page
- `fgj wiki delete` - Delete a wiki page
- `fj wiki list` - List wiki pages
- `fj wiki view` - View wiki page content
- `fj wiki create` - Create a wiki page from flag or file
- `fj wiki edit` - Edit a wiki page
- `fj wiki delete` - Delete a wiki page
#### Issue Dependencies
- `fgj issue edit --add-dependency <number>` - Add issue dependency
- `fgj issue edit --remove-dependency <number>` - Remove issue dependency
- `fj issue edit --add-dependency <number>` - Add issue dependency
- `fj issue edit --remove-dependency <number>` - Remove issue dependency
## [0.3.0b] - 2026-03-21
### Added
#### Repository Management
- `fgj repo edit` - Edit repository settings (visibility, description, homepage, default branch)
- `fj repo edit` - Edit repository settings (visibility, description, homepage, default branch)
### Fixed
- `fgj repo create --public` flag was defined but never read; now properly wired up
- `fj repo create --public` flag was defined but never read; now properly wired up
## [0.3.0a] - 2026-03-21
### Added
#### Raw API Access
- `fgj api <endpoint>` - Make authenticated REST API requests to any Forgejo/Gitea endpoint
- `fj api <endpoint>` - Make authenticated REST API requests to any Forgejo/Gitea endpoint
- HTTP method selection (`--method`/`-X`), auto-switches to POST when fields are provided
- JSON field assembly (`--field`/`-f`) with type inference (bool, int, float, null, string)
- Raw string fields (`--raw-field`/`-F`)
@ -58,14 +58,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Response header display (`--include`/`-i`)
#### Pull Request Management
- `fgj pr diff <number>` - View the diff for a pull request
- `fj pr diff <number>` - View the diff for a pull request
- Colorized output (`--color auto/always/never`)
- Changed file names only (`--name-only`)
- Diffstat summary (`--stat`)
- `fgj pr comment <number>` - Add a comment to a pull request
- `fj pr comment <number>` - Add a comment to a pull request
- Body from flag (`--body`/`-b`) or file (`--body-file`, `-` for stdin)
- JSON output (`--json`)
- `fgj pr review <number>` - Submit a review on a pull request
- `fj pr review <number>` - Submit a review on a pull request
- Approve (`--approve`/`-a`), request changes (`--request-changes`/`-r`), or comment (`--comment`/`-c`)
- Body from flag or file
- JSON output (`--json`)
@ -81,30 +81,30 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Added
#### Forgejo Actions
- `fgj actions run watch <run-id>` - Poll a run until completion
- `fgj actions run rerun <run-id>` - Trigger a rerun of a workflow run
- `fgj actions run cancel <run-id>` - Cancel an in-progress workflow run
- `fgj actions workflow enable <workflow>` - Enable a workflow
- `fgj actions workflow disable <workflow>` - Disable a workflow
- `fj actions run watch <run-id>` - Poll a run until completion
- `fj actions run rerun <run-id>` - Trigger a rerun of a workflow run
- `fj actions run cancel <run-id>` - Cancel an in-progress workflow run
- `fj actions workflow enable <workflow>` - Enable a workflow
- `fj actions workflow disable <workflow>` - Disable a workflow
#### Repository Management
- `fgj repo create <name>` - Create a new repository with full option set: `--private`/`--public`, `--description`, `--add-readme`, `--gitignore`, `--license`, `--homepage`, `--clone`, `--team`
- `fj repo create <name>` - Create a new repository with full option set: `--private`/`--public`, `--description`, `--add-readme`, `--gitignore`, `--license`, `--homepage`, `--clone`, `--team`
#### Issue Management
- `fgj issue create -l <label>` - Assign labels when creating an issue
- `fgj issue edit --add-label` / `--remove-label` - Add or remove labels on existing issues
- `fgj issue close -c <comment>` - Close an issue with an optional comment
- `fj issue create -l <label>` - Assign labels when creating an issue
- `fj issue edit --add-label` / `--remove-label` - Add or remove labels on existing issues
- `fj issue close -c <comment>` - Close an issue with an optional comment
#### Workflow Management
- `fgj actions workflow list/view/run` - List, view, and trigger workflows
- `fj actions workflow list/view/run` - List, view, and trigger workflows
#### Auth Helpers
- `fgj auth token` - Print the stored token for the current host
- `fgj auth logout` - Remove authentication for a host
- `fj auth token` - Print the stored token for the current host
- `fj auth logout` - Remove authentication for a host
#### Shell Completions and Man Pages
- `fgj completion [bash|zsh|fish|powershell]` - Generate shell completion scripts
- `fgj manpages --dir <path>` - Generate man pages for all commands
- `fj completion [bash|zsh|fish|powershell]` - Generate shell completion scripts
- `fj manpages --dir <path>` - Generate man pages for all commands
#### JSON Output
- `--json` flag for all list and view commands: PRs, issues, releases, workflow runs, workflows
@ -117,17 +117,17 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Added
#### Release Management
- `fgj release list` - List releases for a repository
- `fgj release view` - View details of a specific release (supports "latest" keyword)
- `fgj release create` - Create new releases with optional asset uploads
- `fgj release upload` - Upload assets to existing releases with optional clobber support
- `fgj release delete` - Delete releases (preserves Git tags)
- `fj release list` - List releases for a repository
- `fj release view` - View details of a specific release (supports "latest" keyword)
- `fj release create` - Create new releases with optional asset uploads
- `fj release upload` - Upload assets to existing releases with optional clobber support
- `fj release delete` - Delete releases (preserves Git tags)
#### Issue Management
- `fgj issue edit` - Edit existing issues with support for updating title, body, and labels
- `fj issue edit` - Edit existing issues with support for updating title, body, and labels
#### Pull Request Management
- `fgj pr create --assignee` - Assign users when creating pull requests
- `fj pr create --assignee` - Assign users when creating pull requests
#### Repository Detection
- Automatic hostname detection from git remote URLs
@ -148,47 +148,47 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Added
#### Core Features
- Initial release of fgj - Forgejo CLI tool
- Initial release of fj - Forgejo CLI tool
- Multi-instance support for any Forgejo/Gitea instance
- Automatic repository detection from git context (optional `-R` flag)
- Secure authentication with personal access tokens
- Configuration management via `~/.config/fgj/config.yaml`
- Configuration management via `~/.config/fj/config.yaml`
#### Pull Request Management
- `fgj pr list` - List pull requests with filtering by state
- `fgj pr view` - View detailed pull request information
- `fgj pr create` - Create new pull requests
- `fgj pr merge` - Merge pull requests with configurable merge methods
- `fj pr list` - List pull requests with filtering by state
- `fj pr view` - View detailed pull request information
- `fj pr create` - Create new pull requests
- `fj pr merge` - Merge pull requests with configurable merge methods
#### Issue Management
- `fgj issue list` - List issues with state filtering
- `fgj issue view` - View detailed issue information
- `fgj issue create` - Create new issues
- `fgj issue comment` - Add comments to issues
- `fgj issue close` - Close issues
- `fj issue list` - List issues with state filtering
- `fj issue view` - View detailed issue information
- `fj issue create` - Create new issues
- `fj issue comment` - Add comments to issues
- `fj issue close` - Close issues
#### Repository Operations
- `fgj repo view` - View repository details
- `fgj repo list` - List user repositories
- `fgj repo clone` - Clone repositories with protocol selection (HTTPS/SSH)
- `fgj repo fork` - Fork repositories
- `fj repo view` - View repository details
- `fj repo list` - List user repositories
- `fj repo clone` - Clone repositories with protocol selection (HTTPS/SSH)
- `fj repo fork` - Fork repositories
#### Forgejo Actions Support
- `fgj actions run list` - List workflow runs with status and metadata
- `fgj actions run view` - View detailed run information, jobs, and logs
- `fj actions run list` - List workflow runs with status and metadata
- `fj actions run view` - View detailed run information, jobs, and logs
- Support for `--verbose`, `--log`, `--log-failed`, and `--job` flags
- `fgj actions secret list` - List repository secrets
- `fgj actions secret create` - Create repository secrets
- `fgj actions secret delete` - Delete repository secrets
- `fgj actions variable list` - List repository variables
- `fgj actions variable get` - Get variable values
- `fgj actions variable create` - Create repository variables
- `fgj actions variable update` - Update repository variables
- `fgj actions variable delete` - Delete repository variables
- `fj actions secret list` - List repository secrets
- `fj actions secret create` - Create repository secrets
- `fj actions secret delete` - Delete repository secrets
- `fj actions variable list` - List repository variables
- `fj actions variable get` - Get variable values
- `fj actions variable create` - Create repository variables
- `fj actions variable update` - Update repository variables
- `fj actions variable delete` - Delete repository variables
#### Authentication
- `fgj auth login` - Interactive authentication with Forgejo instances
- `fgj auth status` - Check authentication status
- `fj auth login` - Interactive authentication with Forgejo instances
- `fj auth status` - Check authentication status
- Environment variable support (`FGJ_HOST`, `FGJ_TOKEN`)
#### Development
@ -203,9 +203,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Cobra framework for CLI structure
- Viper for configuration management
[0.3.0c]: https://forgejo.zerova.net/public/fgj-sid/releases/tag/v0.3.0c
[0.3.0b]: https://forgejo.zerova.net/public/fgj-sid/releases/tag/v0.3.0b
[0.3.0a]: https://forgejo.zerova.net/public/fgj-sid/releases/tag/v0.3.0a
[0.3.0]: https://codeberg.org/romaintb/fgj/releases/tag/v0.3.0
[0.2.0]: https://codeberg.org/romaintb/fgj/releases/tag/v0.2.0
[0.1.0]: https://codeberg.org/romaintb/fgj/releases/tag/v0.1.0
[0.3.0c]: https://forgejo.zerova.net/public/fj/releases/tag/v0.3.0c
[0.3.0b]: https://forgejo.zerova.net/public/fj/releases/tag/v0.3.0b
[0.3.0a]: https://forgejo.zerova.net/public/fj/releases/tag/v0.3.0a
[0.3.0]: https://codeberg.org/romaintb/fj/releases/tag/v0.3.0
[0.2.0]: https://codeberg.org/romaintb/fj/releases/tag/v0.2.0
[0.1.0]: https://codeberg.org/romaintb/fj/releases/tag/v0.1.0

View file

@ -11,10 +11,10 @@ help:
@echo " make clean - Clean build artifacts"
build:
go build -o bin/fgj .
go build -o bin/fj .
install: build
install -Dm755 bin/fgj /usr/bin/fgj
install -Dm755 bin/fj /usr/bin/fj
run:
go run .

286
README.md
View file

@ -1,11 +1,11 @@
# fgj - Forgejo/Gitea CLI Tool
# 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)
`fgj` 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.
`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/fgj](https://codeberg.org/romaintb/fgj) and hosted at [forgejo.zerova.net/public/fgj-sid](https://forgejo.zerova.net/public/fgj-sid).
> 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
@ -19,7 +19,7 @@
- Issue dependencies (`--add-dependency`, `--remove-dependency`)
- Forgejo Actions (workflow runs, watch/rerun/cancel, enable/disable, secrets, variables)
- Releases (create, upload, delete)
- Raw API access (`fgj api`) for arbitrary REST calls
- 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
@ -33,22 +33,22 @@
### macOS (Homebrew)
```bash
brew tap sid/fgj-sid https://forgejo.zerova.net/sid/homebrew-fgj-sid.git
brew install fgj
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/fgj-sid@latest
go install forgejo.zerova.net/public/fj@latest
```
### From Source
```bash
git clone https://forgejo.zerova.net/public/fgj-sid.git
cd fgj-sid
go build -o fgj .
git clone https://forgejo.zerova.net/public/fj.git
cd fj
go build -o fj .
```
## Quick Start
@ -58,7 +58,7 @@ go build -o fgj .
First, authenticate with your Forgejo or Gitea instance:
```bash
fgj auth login
fj auth login
```
You'll be prompted for:
@ -74,34 +74,34 @@ To create a personal access token:
### 2. Check Authentication Status
```bash
fgj auth status
fj auth status
```
### Auth Helpers
```bash
# Print the stored token for the current host
fgj auth token
fj auth token
# Remove authentication for a host
fgj auth logout
fj auth logout
```
## Usage
### Repository Detection
`fgj` automatically detects the repository from your git context, similar to `gh`:
`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
fgj pr list # Automatically uses current repo
fgj issue list # Automatically uses current repo
fgj pr view 123 # Automatically uses current 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
fgj pr list -R owner/repo
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.
@ -110,307 +110,307 @@ The tool reads `.git/config` to find the origin remote and extract both the owne
```bash
# List pull requests (auto-detects repo and hostname from git)
fgj pr list
fj pr list
# Or specify explicitly
fgj pr list -R owner/repo
fj pr list -R owner/repo
# Filter by state
fgj pr list --state closed
fj pr list --state closed
# View a specific pull request
fgj pr view 123
fj pr view 123
# Create a pull request
fgj pr create -t "PR Title" -b "PR Description" -H feature-branch -B main
fj pr create -t "PR Title" -b "PR Description" -H feature-branch -B main
# Merge a pull request
fgj pr merge 123 --merge-method squash
fj pr merge 123 --merge-method squash
# View PR diff
fgj pr diff 123
fj pr diff 123
# View diff with color
fgj pr diff 123 --color always
fj pr diff 123 --color always
# Show only changed file names
fgj pr diff 123 --name-only
fj pr diff 123 --name-only
# Show diffstat summary
fgj pr diff 123 --stat
fj pr diff 123 --stat
# Comment on a pull request
fgj pr comment 123 -b "Looks good, minor nit on line 42"
fj pr comment 123 -b "Looks good, minor nit on line 42"
# Comment from a file
fgj pr comment 123 --body-file review-notes.md
fj pr comment 123 --body-file review-notes.md
# Approve a pull request
fgj pr review 123 --approve -b "LGTM"
fj pr review 123 --approve -b "LGTM"
# Request changes
fgj pr review 123 --request-changes -b "Please fix the error handling"
fj pr review 123 --request-changes -b "Please fix the error handling"
# Submit a review comment (neither approve nor request changes)
fgj pr review 123 --comment -b "Some observations"
fj pr review 123 --comment -b "Some observations"
```
### Issues
```bash
# List issues (auto-detects repo and hostname from git)
fgj issue list
fj issue list
# Or specify explicitly
fgj issue list -R owner/repo
fj issue list -R owner/repo
# Filter by state
fgj issue list --state all
fj issue list --state all
# View an issue
fgj issue view 456
fj issue view 456
# Create an issue
fgj issue create -t "Issue Title" -b "Issue Description"
fj 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
fj issue create -t "Issue Title" -b "Issue Description" -l bug -l enhancement
# Comment on an issue
fgj issue comment 456 -b "My comment"
fj issue comment 456 -b "My comment"
# Close an issue
fgj issue close 456
fj issue close 456
# Close an issue with a comment
fgj issue close 456 -c "Fixed in v2.0"
fj 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
fj issue edit 456 -t "New Title"
fj issue edit 456 --add-label priority --remove-label bug
# Manage issue dependencies
fgj issue edit 456 --add-dependency 123
fgj issue edit 456 --remove-dependency 123
fj issue edit 456 --add-dependency 123
fj issue edit 456 --remove-dependency 123
```
### Labels
```bash
# List labels
fgj label list
fj label list
# Create a label
fgj label create bug --color ff0000 -d "Something isn't working"
fj label create bug --color ff0000 -d "Something isn't working"
# Edit a label
fgj label edit bug --name bugfix --color ee0000
fj label edit bug --name bugfix --color ee0000
# Delete a label
fgj label delete bug
fj label delete bug
```
### Milestones
```bash
# List milestones
fgj milestone list
fgj milestone list --state all
fj milestone list
fj milestone list --state all
# View a milestone
fgj milestone view "v1.0"
fj milestone view "v1.0"
# Create a milestone with due date
fgj milestone create "v2.0" -d "Next major release" --due 2026-06-01
fj milestone create "v2.0" -d "Next major release" --due 2026-06-01
# Edit a milestone
fgj milestone edit "v2.0" --title "v2.0-rc1" --state closed
fj milestone edit "v2.0" --title "v2.0-rc1" --state closed
# Delete a milestone
fgj milestone delete "v2.0"
fj milestone delete "v2.0"
```
### Wiki
```bash
# List wiki pages
fgj wiki list
fj wiki list
# View a wiki page
fgj wiki view "Home"
fj wiki view "Home"
# Create a wiki page
fgj wiki create "Setup Guide" -b "# Setup\n\nFollow these steps..."
fj wiki create "Setup Guide" -b "# Setup\n\nFollow these steps..."
# Create from file
fgj wiki create "API Docs" --body-file docs/api.md
fj wiki create "API Docs" --body-file docs/api.md
# Edit a wiki page
fgj wiki edit "Home" -b "Updated content"
fj wiki edit "Home" -b "Updated content"
# Delete a wiki page
fgj wiki delete "Old Page"
fj wiki delete "Old Page"
```
### Repositories
```bash
# View repository details
fgj repo view owner/repo
fj repo view owner/repo
# List your repositories
fgj repo list
fj 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
fj repo create my-repo
fj repo create my-repo -d "My project" --private --add-readme -g Go -l MIT
# Clone a repository
fgj repo clone owner/repo
fj repo clone owner/repo
# Clone via SSH
fgj repo clone owner/repo -p ssh
fj repo clone owner/repo -p ssh
# Fork a repository
fgj repo fork owner/repo
fj repo fork owner/repo
# Edit repository settings
fgj repo edit owner/repo --public
fgj repo edit owner/repo --private
fgj repo edit owner/repo -d "New description" --homepage https://example.com
fgj repo edit --default-branch develop
fgj repo edit owner/repo --name new-name
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)
fgj repo rename new-name
fgj repo rename new-name -R owner/old-name
fj repo rename new-name
fj repo rename new-name -R owner/old-name
```
### Releases
```bash
# List releases
fgj release list
fj release list
# View a release (or use "latest")
fgj release view v1.2.3
fj 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
fj 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
fj release upload v1.2.3 ./dist/app.tar.gz --clobber
# Delete a release (keeps the Git tag)
fgj release delete v1.2.3
fj release delete v1.2.3
```
### Forgejo Actions
```bash
# List workflows
fgj actions workflow list
fj actions workflow list
# View a workflow
fgj actions workflow view ci.yml
fj actions workflow view ci.yml
# Run a workflow (trigger workflow_dispatch)
fgj actions workflow run deploy.yml
fj actions workflow run deploy.yml
# Run a workflow with inputs
fgj actions workflow run deploy.yml -f environment=production -f version=1.2.3
fj 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
fj 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
fj actions workflow enable ci.yml
fj actions workflow disable ci.yml
# List workflow runs
fgj actions run list
fj actions run list
# View a specific run
fgj actions run view 123
fj actions run view 123
# View run with job details
fgj actions run view 123 --verbose
fj actions run view 123 --verbose
# View run logs
fgj actions run view 123 --log
fj actions run view 123 --log
# View specific job logs
fgj actions run view 123 --job 456 --log
fj actions run view 123 --job 456 --log
# Watch a run until completion
fgj actions run watch 123
fj actions run watch 123
# Rerun a workflow run
fgj actions run rerun 123
fj actions run rerun 123
# Cancel a running workflow
fgj actions run cancel 123
fj actions run cancel 123
# List secrets
fgj actions secret list
fj actions secret list
# Create a secret
fgj actions secret create MY_SECRET
fj actions secret create MY_SECRET
# Delete a secret
fgj actions secret delete MY_SECRET
fj actions secret delete MY_SECRET
# List variables
fgj actions variable list
fj actions variable list
# Get a variable
fgj actions variable get MY_VAR
fj actions variable get MY_VAR
# Create a variable
fgj actions variable create MY_VAR "value"
fj actions variable create MY_VAR "value"
# Update a variable
fgj actions variable update MY_VAR "new value"
fj actions variable update MY_VAR "new value"
# Delete a variable
fgj actions variable delete MY_VAR
fj actions variable delete MY_VAR
```
### Raw API Access
```bash
# GET request (auto-detects owner/repo from git context)
fgj api /repos/{owner}/{repo}/pulls
fj api /repos/{owner}/{repo}/pulls
# POST with fields
fgj api /repos/{owner}/{repo}/issues -X POST -f title="Bug report" -f body="Description"
fj api /repos/{owner}/{repo}/issues -X POST -f title="Bug report" -f body="Description"
# Explicit method and hostname
fgj api /repos/myorg/myrepo/labels --hostname my-forgejo.example.com
fj api /repos/myorg/myrepo/labels --hostname my-forgejo.example.com
# Read request body from file
fgj api /repos/{owner}/{repo}/issues -X POST --input issue.json
fj api /repos/{owner}/{repo}/issues -X POST --input issue.json
# Read from stdin
echo '{"title":"test"}' | fgj api /repos/{owner}/{repo}/issues -X POST --input -
echo '{"title":"test"}' | fj api /repos/{owner}/{repo}/issues -X POST --input -
# Include response headers
fgj api /repos/{owner}/{repo} -i
fj api /repos/{owner}/{repo} -i
# Suppress output (useful for DELETE)
fgj api /repos/{owner}/{repo}/issues/123 -X DELETE --silent
fj api /repos/{owner}/{repo}/issues/123 -X DELETE --silent
```
## Shell Completions and Man Pages
```bash
# 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
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
fgj manpages --dir ~/.local/share/man/man1
fj manpages --dir ~/.local/share/man/man1
```
## JSON Output
@ -418,15 +418,15 @@ fgj manpages --dir ~/.local/share/man/man1
Most list and view commands support `--json` for machine-readable output:
```bash
fgj pr list --json
fgj issue view 456 --json
fgj release list --json
fgj actions run list --json
fgj actions workflow view ci.yml --json
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
fgj pr comment 123 -b "LGTM" --json
fgj pr review 123 --approve -b "Ship it" --json
fj pr comment 123 -b "LGTM" --json
fj pr review 123 --approve -b "Ship it" --json
```
### Structured Error Output
@ -435,16 +435,16 @@ For machine consumption (ideal for AI agents and scripts), use `--json-errors` t
```bash
# Errors are written to stderr as JSON
fgj pr view 9999 --json-errors
fj pr view 9999 --json-errors
# stderr: {"error":{"code":"not_found","message":"...","status":404}}
# Combine with --json for fully machine-readable I/O
fgj pr list --json --json-errors
fj pr list --json --json-errors
```
## Configuration
Configuration is stored in `~/.config/fgj/config.yaml`:
Configuration is stored in `~/.config/fj/config.yaml`:
```yaml
hosts:
@ -466,9 +466,9 @@ hosts:
### Directory-Based Host Selection (`match_dirs`)
When you work with multiple Forgejo/Gitea instances, `fgj` can automatically select the right host based on your current working directory — no `--hostname` flag needed.
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 `fgj` 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.
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:
@ -509,15 +509,15 @@ Hostname is resolved in this priority order:
- `--hostname`: Specify 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 instance from your origin remote URL, so you typically don't need to specify `--hostname` unless working with multiple instances.
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
`fgj` is designed to work seamlessly with AI coding agents like Claude Code. Use `--json` and `--json-errors` for fully machine-readable I/O:
`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
fgj pr create -R owner/repo -t "feat: add new feature" -b "$(cat <<EOF
fj pr create -R owner/repo -t "feat: add new feature" -b "$(cat <<EOF
## Summary
- Added new feature X
- Fixed bug Y
@ -527,29 +527,29 @@ EOF
)" --json
# Check PR status during development
fgj pr list -R owner/repo --state open --json
fj pr list -R owner/repo --state open --json
# Review a PR diff, then approve
fgj pr diff 123
fgj pr review 123 --approve -b "LGTM" --json
fj pr diff 123
fj pr review 123 --approve -b "LGTM" --json
# Post review feedback
fgj pr comment 123 -b "Consider using a map here for O(1) lookup" --json
fj pr comment 123 -b "Consider using a map here for O(1) lookup" --json
# Request changes with detailed feedback
fgj pr review 123 --request-changes --body-file feedback.md --json
fj pr review 123 --request-changes --body-file feedback.md --json
# Use raw API for anything not covered by commands
fgj api /repos/{owner}/{repo}/topics --json-errors
fgj api /repos/{owner}/{repo}/labels -X POST -f name=agent-reviewed -f color="#00ff00"
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
fgj pr view 9999 --json --json-errors 2>errors.json
fj pr view 9999 --json --json-errors 2>errors.json
```
## Supported Instances
`fgj` works with any Forgejo or Gitea instance, including:
`fj` works with any Forgejo or Gitea instance, including:
- Self-hosted Forgejo instances
- Self-hosted Gitea instances
@ -557,11 +557,11 @@ fgj pr view 9999 --json --json-errors 2>errors.json
## Contributing
Contributions are welcome! Please feel free to submit a Pull Request at [forgejo.zerova.net/public/fgj-sid](https://forgejo.zerova.net/public/fgj-sid).
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
`fgj` 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:
`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
@ -576,7 +576,7 @@ We welcome contributions to implement any of these features!
## Acknowledgments
Based on [fgj by romaintb](https://codeberg.org/romaintb/fgj). Enhanced with agentic dev features for AI-assisted workflows.
Based on [fj by romaintb](https://codeberg.org/romaintb/fj). Enhanced with agentic dev features for AI-assisted workflows.
## License

BIN
bin/fj Executable file

Binary file not shown.

View file

@ -10,8 +10,8 @@ import (
"code.gitea.io/sdk/gitea"
"github.com/spf13/cobra"
"forgejo.zerova.net/public/fgj-sid/internal/api"
"forgejo.zerova.net/public/fgj-sid/internal/config"
"forgejo.zerova.net/public/fj/internal/api"
"forgejo.zerova.net/public/fj/internal/config"
)
// ActionRun represents a workflow run
@ -99,13 +99,13 @@ var runListCmd = &cobra.Command{
Short: "List recent workflow runs",
Long: "List recent workflow runs for a repository.",
Example: ` # List recent workflow runs
fgj actions run list
fj actions run list
# List runs with a custom limit
fgj actions run list -L 50
fj actions run list -L 50
# Output as JSON
fgj actions run list --json`,
fj actions run list --json`,
RunE: runRunList,
}
@ -114,16 +114,16 @@ var runViewCmd = &cobra.Command{
Short: "View a workflow run",
Long: "View details about a specific workflow run.",
Example: ` # View a workflow run
fgj actions run view 123
fj actions run view 123
# View with job details
fgj actions run view 123 -v
fj actions run view 123 -v
# View logs for a specific job
fgj actions run view 123 --job 456 --log
fj actions run view 123 --job 456 --log
# View only failed logs
fgj actions run view 123 --log-failed`,
fj actions run view 123 --log-failed`,
Args: cobra.ExactArgs(1),
RunE: runRunView,
}
@ -133,10 +133,10 @@ var runWatchCmd = &cobra.Command{
Short: "Watch a workflow run",
Long: "Poll a workflow run until it completes.",
Example: ` # Watch a run until it completes
fgj actions run watch 123
fj actions run watch 123
# Watch with a custom polling interval
fgj actions run watch 123 -i 10s`,
fj actions run watch 123 -i 10s`,
Args: cobra.ExactArgs(1),
RunE: runRunWatch,
}
@ -146,7 +146,7 @@ var runRerunCmd = &cobra.Command{
Short: "Rerun a workflow run",
Long: "Trigger a rerun for a specific workflow run.",
Example: ` # Rerun a failed workflow run
fgj actions run rerun 123`,
fj actions run rerun 123`,
Args: cobra.ExactArgs(1),
RunE: runRunRerun,
}
@ -156,7 +156,7 @@ var runCancelCmd = &cobra.Command{
Short: "Cancel a workflow run",
Long: "Cancel a running workflow run.",
Example: ` # Cancel a running workflow
fgj actions run cancel 123`,
fj actions run cancel 123`,
Args: cobra.ExactArgs(1),
RunE: runRunCancel,
}
@ -173,13 +173,13 @@ var workflowListCmd = &cobra.Command{
Short: "List workflows",
Long: "List all workflows in a repository.",
Example: ` # List all workflows
fgj actions workflow list
fj actions workflow list
# List workflows as JSON
fgj actions workflow list --json
fj actions workflow list --json
# List workflows for a specific repo
fgj actions workflow list -R owner/repo`,
fj actions workflow list -R owner/repo`,
RunE: runWorkflowList,
}
@ -188,10 +188,10 @@ var workflowViewCmd = &cobra.Command{
Short: "View a workflow",
Long: "View details about a specific workflow. You can specify the workflow by name or filename.",
Example: ` # View a workflow by filename
fgj actions workflow view ci.yml
fj actions workflow view ci.yml
# View as JSON
fgj actions workflow view ci.yml --json`,
fj actions workflow view ci.yml --json`,
Args: cobra.ExactArgs(1),
RunE: runWorkflowView,
}
@ -201,10 +201,10 @@ var workflowRunCmd = &cobra.Command{
Short: "Run a workflow",
Long: "Trigger a workflow_dispatch event for a workflow. The workflow must support the workflow_dispatch trigger.",
Example: ` # Trigger a workflow on the default branch
fgj actions workflow run deploy.yml
fj actions workflow run deploy.yml
# Trigger on a specific branch with input parameters
fgj actions workflow run deploy.yml -r staging -f environment=staging -f version=1.2.3`,
fj actions workflow run deploy.yml -r staging -f environment=staging -f version=1.2.3`,
Args: cobra.ExactArgs(1),
RunE: runWorkflowRun,
}
@ -214,7 +214,7 @@ var workflowEnableCmd = &cobra.Command{
Short: "Enable a workflow",
Long: "Enable a workflow so it can be triggered.\n\nNote: This feature requires Forgejo 15.0+ or Gitea 1.24+.\nFor older versions, use the web UI to enable workflows.",
Example: ` # Enable a workflow
fgj actions workflow enable ci.yml`,
fj actions workflow enable ci.yml`,
Args: cobra.ExactArgs(1),
RunE: runWorkflowEnable,
}
@ -224,7 +224,7 @@ var workflowDisableCmd = &cobra.Command{
Short: "Disable a workflow",
Long: "Disable a workflow so it cannot be triggered.\n\nNote: This feature requires Forgejo 15.0+ or Gitea 1.24+.\nFor older versions, use the web UI to disable workflows.",
Example: ` # Disable a workflow
fgj actions workflow disable ci.yml`,
fj actions workflow disable ci.yml`,
Args: cobra.ExactArgs(1),
RunE: runWorkflowDisable,
}
@ -241,10 +241,10 @@ var actionsSecretListCmd = &cobra.Command{
Short: "List repository secrets",
Long: "List all secrets for a repository.",
Example: ` # List all secrets
fgj actions secret list
fj actions secret list
# List secrets for a specific repo
fgj actions secret list -R owner/repo`,
fj actions secret list -R owner/repo`,
RunE: runActionsSecretList,
}
@ -253,10 +253,10 @@ var actionsSecretCreateCmd = &cobra.Command{
Short: "Create or update a repository secret",
Long: "Create or update a secret for Forgejo Actions. The secret value will be read from stdin.",
Example: ` # Create a secret (will prompt for value)
fgj actions secret create DEPLOY_TOKEN
fj actions secret create DEPLOY_TOKEN
# Create a secret for a specific repo
fgj actions secret create API_KEY -R owner/repo`,
fj actions secret create API_KEY -R owner/repo`,
Args: cobra.ExactArgs(1),
RunE: runActionsSecretCreate,
}
@ -266,7 +266,7 @@ var actionsSecretDeleteCmd = &cobra.Command{
Short: "Delete a repository secret",
Long: "Delete a secret from Forgejo Actions.",
Example: ` # Delete a secret
fgj actions secret delete DEPLOY_TOKEN`,
fj actions secret delete DEPLOY_TOKEN`,
Args: cobra.ExactArgs(1),
RunE: runActionsSecretDelete,
}
@ -283,10 +283,10 @@ var actionsVariableListCmd = &cobra.Command{
Short: "List repository variables",
Long: "List all variables for a repository.",
Example: ` # List all variables
fgj actions variable list
fj actions variable list
# List variables for a specific repo
fgj actions variable list -R owner/repo`,
fj actions variable list -R owner/repo`,
RunE: runActionsVariableList,
}
@ -295,7 +295,7 @@ var actionsVariableGetCmd = &cobra.Command{
Short: "Get a repository variable",
Long: "Get the value of a specific repository variable.",
Example: ` # Get a variable value
fgj actions variable get ENVIRONMENT`,
fj actions variable get ENVIRONMENT`,
Args: cobra.ExactArgs(1),
RunE: runActionsVariableGet,
}
@ -305,10 +305,10 @@ var actionsVariableCreateCmd = &cobra.Command{
Short: "Create a repository variable",
Long: "Create a new variable for Forgejo Actions.",
Example: ` # Create a variable
fgj actions variable create ENVIRONMENT production
fj actions variable create ENVIRONMENT production
# Create a variable for a specific repo
fgj actions variable create NODE_VERSION 20 -R owner/repo`,
fj actions variable create NODE_VERSION 20 -R owner/repo`,
Args: cobra.ExactArgs(2),
RunE: runActionsVariableCreate,
}
@ -318,7 +318,7 @@ var actionsVariableUpdateCmd = &cobra.Command{
Short: "Update a repository variable",
Long: "Update an existing variable for Forgejo Actions.",
Example: ` # Update a variable
fgj actions variable update ENVIRONMENT staging`,
fj actions variable update ENVIRONMENT staging`,
Args: cobra.ExactArgs(2),
RunE: runActionsVariableUpdate,
}
@ -328,7 +328,7 @@ var actionsVariableDeleteCmd = &cobra.Command{
Short: "Delete a repository variable",
Long: "Delete a variable from Forgejo Actions.",
Example: ` # Delete a variable
fgj actions variable delete ENVIRONMENT`,
fj actions variable delete ENVIRONMENT`,
Args: cobra.ExactArgs(1),
RunE: runActionsVariableDelete,
}

View file

@ -7,7 +7,7 @@ import (
)
// Top-level aliases for "actions run" and "actions workflow" commands,
// matching gh CLI's command structure (e.g., "fgj run list" instead of "fgj actions run list").
// matching gh CLI's command structure (e.g., "fj run list" instead of "fj actions run list").
func init() {
// --- run alias ---

View file

@ -10,8 +10,8 @@ import (
"strconv"
"strings"
"forgejo.zerova.net/public/fgj-sid/internal/config"
"forgejo.zerova.net/public/fgj-sid/internal/git"
"forgejo.zerova.net/public/fj/internal/config"
"forgejo.zerova.net/public/fj/internal/git"
"github.com/spf13/cobra"
)
@ -26,16 +26,16 @@ detected from the current git repository.
If --field is used and no --method is specified, the method defaults to POST.`,
Example: ` # List pull requests for the current repository
fgj api /repos/{owner}/{repo}/pulls
fj api /repos/{owner}/{repo}/pulls
# Create an issue
fgj api /repos/{owner}/{repo}/issues --method POST --field title=Bug --field body="It broke"
fj api /repos/{owner}/{repo}/issues --method POST --field title=Bug --field body="It broke"
# Get a specific user
fgj api /users/johndoe
fj api /users/johndoe
# Use raw body from stdin
echo '{"title":"test"}' | fgj api /repos/{owner}/{repo}/issues --input -`,
echo '{"title":"test"}' | fj api /repos/{owner}/{repo}/issues --input -`,
Args: cobra.ExactArgs(1),
RunE: runAPI,
}

View file

@ -7,8 +7,8 @@ import (
"strings"
"syscall"
"forgejo.zerova.net/public/fgj-sid/internal/api"
"forgejo.zerova.net/public/fgj-sid/internal/config"
"forgejo.zerova.net/public/fj/internal/api"
"forgejo.zerova.net/public/fj/internal/config"
"github.com/spf13/cobra"
"github.com/spf13/viper"
"golang.org/x/term"
@ -16,7 +16,7 @@ import (
var authCmd = &cobra.Command{
Use: "auth",
Short: "Authenticate fgj with a Forgejo instance",
Short: "Authenticate fj with a Forgejo instance",
Long: "Manage authentication state for Forgejo instances.",
}
@ -132,7 +132,7 @@ func runAuthStatus(cmd *cobra.Command, args []string) error {
if len(cfg.Hosts) == 0 {
fmt.Fprintln(ios.Out, "Not authenticated with any Forgejo instances")
fmt.Fprintln(ios.Out, "Run 'fgj auth login' to authenticate")
fmt.Fprintln(ios.Out, "Run 'fj auth login' to authenticate")
return nil
}

View file

@ -11,7 +11,7 @@ import (
var completionCmd = &cobra.Command{
Use: "completion [bash|zsh|fish|powershell]",
Short: "Generate shell completion scripts",
Long: "Generate shell completion scripts for fgj.",
Long: "Generate shell completion scripts for fj.",
Args: cobra.MatchAll(cobra.ExactArgs(1), cobra.OnlyValidArgs),
ValidArgs: []string{"bash", "zsh", "fish", "powershell"},
DisableFlagsInUseLine: true,

View file

@ -6,7 +6,7 @@ import (
"fmt"
"strings"
"forgejo.zerova.net/public/fgj-sid/internal/api"
"forgejo.zerova.net/public/fj/internal/api"
)
// Error codes for structured error output.
@ -54,7 +54,7 @@ func ContextualError(err error) error {
if errors.As(err, &apiErr) {
switch {
case apiErr.StatusCode == 401 || apiErr.StatusCode == 403:
return fmt.Errorf("%w\nHint: Try authenticating with: fgj auth login", err)
return fmt.Errorf("%w\nHint: Try authenticating with: fj auth login", err)
case apiErr.StatusCode == 404:
return fmt.Errorf("%w\nHint: Resource not found. Check the repository and number are correct.", err)
}
@ -73,7 +73,7 @@ func ContextualError(err error) error {
switch {
case strings.Contains(msg, "401") || strings.Contains(msg, "403"):
if strings.Contains(msg, "authentication") || strings.Contains(msg, "unauthorized") || strings.Contains(msg, "forbidden") {
return fmt.Errorf("%w\nHint: Try authenticating with: fgj auth login", err)
return fmt.Errorf("%w\nHint: Try authenticating with: fj auth login", err)
}
}

View file

@ -1,5 +1,5 @@
package cmd
import "forgejo.zerova.net/public/fgj-sid/internal/iostreams"
import "forgejo.zerova.net/public/fj/internal/iostreams"
var ios = iostreams.New()

View file

@ -6,9 +6,9 @@ import (
"strings"
"code.gitea.io/sdk/gitea"
"forgejo.zerova.net/public/fgj-sid/internal/api"
"forgejo.zerova.net/public/fgj-sid/internal/config"
"forgejo.zerova.net/public/fgj-sid/internal/text"
"forgejo.zerova.net/public/fj/internal/api"
"forgejo.zerova.net/public/fj/internal/config"
"forgejo.zerova.net/public/fj/internal/text"
"github.com/spf13/cobra"
)
@ -23,13 +23,13 @@ var issueListCmd = &cobra.Command{
Short: "List issues",
Long: "List issues in a repository.",
Example: ` # List open issues
fgj issue list
fj issue list
# List closed issues for a specific repo
fgj issue list -s closed -R owner/repo
fj issue list -s closed -R owner/repo
# Output as JSON
fgj issue list --json`,
fj issue list --json`,
RunE: runIssueList,
}
@ -38,16 +38,16 @@ var issueViewCmd = &cobra.Command{
Short: "View an issue",
Long: "Display detailed information about an issue.",
Example: ` # View issue #42
fgj issue view 42
fj issue view 42
# View using URL
fgj issue view https://codeberg.org/owner/repo/issues/42
fj issue view https://codeberg.org/owner/repo/issues/42
# Open in browser
fgj issue view 42 --web
fj issue view 42 --web
# View an issue from a specific repo as JSON
fgj issue view 42 -R owner/repo --json`,
fj issue view 42 -R owner/repo --json`,
Args: cobra.ExactArgs(1),
RunE: runIssueView,
}
@ -57,10 +57,10 @@ var issueCreateCmd = &cobra.Command{
Short: "Create an issue",
Long: "Create a new issue.",
Example: ` # Create an issue with a title
fgj issue create -t "Fix login bug"
fj issue create -t "Fix login bug"
# Create an issue with title, body, and labels
fgj issue create -t "Add dark mode" -b "We need a dark theme" -l feature -l ui`,
fj issue create -t "Add dark mode" -b "We need a dark theme" -l feature -l ui`,
RunE: runIssueCreate,
}
@ -69,10 +69,10 @@ var issueCommentCmd = &cobra.Command{
Short: "Add a comment to an issue",
Long: "Add a comment to an existing issue.",
Example: ` # Add a comment to issue #42
fgj issue comment 42 -b "This is fixed in the latest release"
fj issue comment 42 -b "This is fixed in the latest release"
# Comment on an issue in a specific repo
fgj issue comment 10 -b "Confirmed on my end" -R owner/repo`,
fj issue comment 10 -b "Confirmed on my end" -R owner/repo`,
Args: cobra.ExactArgs(1),
RunE: runIssueComment,
}
@ -82,10 +82,10 @@ var issueCloseCmd = &cobra.Command{
Short: "Close an issue",
Long: "Close an existing issue.",
Example: ` # Close issue #42
fgj issue close 42
fj issue close 42
# Close with a comment
fgj issue close 42 -c "Fixed in commit abc1234"`,
fj issue close 42 -c "Fixed in commit abc1234"`,
Args: cobra.ExactArgs(1),
RunE: runIssueClose,
}
@ -95,7 +95,7 @@ var issueReopenCmd = &cobra.Command{
Short: "Reopen an issue",
Long: "Reopen a closed issue.",
Example: ` # Reopen issue #42
fgj issue reopen 42`,
fj issue reopen 42`,
Args: cobra.ExactArgs(1),
RunE: runIssueReopen,
}
@ -105,10 +105,10 @@ var issueDeleteCmd = &cobra.Command{
Short: "Delete an issue",
Long: "Delete an issue permanently.",
Example: ` # Delete issue #42
fgj issue delete 42
fj issue delete 42
# Delete without confirmation
fgj issue delete 42 -y`,
fj issue delete 42 -y`,
Args: cobra.ExactArgs(1),
RunE: runIssueDelete,
}
@ -118,16 +118,16 @@ var issueEditCmd = &cobra.Command{
Short: "Edit an issue",
Long: "Edit an existing issue's title, body, or state.",
Example: ` # Update the title of issue #42
fgj issue edit 42 -t "Updated title"
fj issue edit 42 -t "Updated title"
# Reopen a closed issue
fgj issue edit 42 -s open
fj issue edit 42 -s open
# Add and remove labels
fgj issue edit 42 --add-label bug --remove-label wontfix
fj issue edit 42 --add-label bug --remove-label wontfix
# Add a dependency
fgj issue edit 42 --add-dependency 10`,
fj issue edit 42 --add-dependency 10`,
Args: cobra.ExactArgs(1),
RunE: runIssueEdit,
}

View file

@ -5,8 +5,8 @@ import (
"strings"
"code.gitea.io/sdk/gitea"
"forgejo.zerova.net/public/fgj-sid/internal/api"
"forgejo.zerova.net/public/fgj-sid/internal/config"
"forgejo.zerova.net/public/fj/internal/api"
"forgejo.zerova.net/public/fj/internal/config"
"github.com/spf13/cobra"
)
@ -21,13 +21,13 @@ var labelListCmd = &cobra.Command{
Short: "List labels for a repository",
Long: "List all labels defined in a repository.",
Example: ` # List labels for the current repository
fgj label list
fj label list
# List labels for a specific repository
fgj label list -R owner/repo
fj label list -R owner/repo
# Output as JSON
fgj label list --json`,
fj label list --json`,
RunE: runLabelList,
}
@ -36,13 +36,13 @@ var labelCreateCmd = &cobra.Command{
Short: "Create a label",
Long: "Create a new label in a repository.",
Example: ` # Create a label with a color
fgj label create bug -c ff0000
fj label create bug -c ff0000
# Create a label with color and description
fgj label create feature -c 00ff00 -d "New feature request"
fj label create feature -c 00ff00 -d "New feature request"
# Create a label in a specific repository
fgj label create urgent -c ff0000 -R owner/repo`,
fj label create urgent -c ff0000 -R owner/repo`,
Args: cobra.ExactArgs(1),
RunE: runLabelCreate,
}
@ -52,13 +52,13 @@ var labelEditCmd = &cobra.Command{
Short: "Edit a label",
Long: "Edit an existing label in a repository.",
Example: ` # Rename a label
fgj label edit bug --name bugfix
fj label edit bug --name bugfix
# Change the color of a label
fgj label edit bug -c 00ff00
fj label edit bug -c 00ff00
# Update description
fgj label edit bug -d "Something is broken"`,
fj label edit bug -d "Something is broken"`,
Args: cobra.ExactArgs(1),
RunE: runLabelEdit,
}
@ -68,13 +68,13 @@ var labelDeleteCmd = &cobra.Command{
Short: "Delete a label",
Long: "Delete a label from a repository.",
Example: ` # Delete a label
fgj label delete bug
fj label delete bug
# Delete without confirmation
fgj label delete bug -y
fj label delete bug -y
# Delete a label from a specific repository
fgj label delete bug -R owner/repo`,
fj label delete bug -R owner/repo`,
Args: cobra.ExactArgs(1),
RunE: runLabelDelete,
}

View file

@ -12,7 +12,7 @@ import (
var manpagesCmd = &cobra.Command{
Use: "manpages",
Short: "Generate manpages",
Long: "Generate manpages for fgj commands.",
Long: "Generate manpages for fj commands.",
RunE: func(cmd *cobra.Command, args []string) error {
dir, _ := cmd.Flags().GetString("dir")
if dir == "" {

View file

@ -7,9 +7,9 @@ import (
"time"
"code.gitea.io/sdk/gitea"
"forgejo.zerova.net/public/fgj-sid/internal/api"
"forgejo.zerova.net/public/fgj-sid/internal/config"
"forgejo.zerova.net/public/fgj-sid/internal/text"
"forgejo.zerova.net/public/fj/internal/api"
"forgejo.zerova.net/public/fj/internal/config"
"forgejo.zerova.net/public/fj/internal/text"
"github.com/spf13/cobra"
)
@ -24,13 +24,13 @@ var milestoneListCmd = &cobra.Command{
Short: "List milestones",
Long: "List milestones in a repository.",
Example: ` # List open milestones
fgj milestone list
fj milestone list
# List all milestones for a specific repo
fgj milestone list -R owner/repo --state all
fj milestone list -R owner/repo --state all
# Output as JSON
fgj milestone list --json`,
fj milestone list --json`,
RunE: runMilestoneList,
}
@ -39,16 +39,16 @@ var milestoneViewCmd = &cobra.Command{
Short: "View a milestone",
Long: "Display detailed information about a milestone.",
Example: ` # View by ID
fgj milestone view 1
fj milestone view 1
# View by title
fgj milestone view "v1.0"
fj milestone view "v1.0"
# Open in browser
fgj milestone view "v1.0" --web
fj milestone view "v1.0" --web
# Output as JSON
fgj milestone view "v1.0" --json`,
fj milestone view "v1.0" --json`,
Args: cobra.ExactArgs(1),
RunE: runMilestoneView,
}
@ -58,13 +58,13 @@ var milestoneCreateCmd = &cobra.Command{
Short: "Create a milestone",
Long: "Create a new milestone.",
Example: ` # Create a simple milestone
fgj milestone create "v1.0"
fj milestone create "v1.0"
# Create with description and due date
fgj milestone create "v2.0" -d "Second release" --due 2026-06-01
fj milestone create "v2.0" -d "Second release" --due 2026-06-01
# Output as JSON
fgj milestone create "v1.0" --json`,
fj milestone create "v1.0" --json`,
Args: cobra.ExactArgs(1),
RunE: runMilestoneCreate,
}
@ -74,13 +74,13 @@ var milestoneEditCmd = &cobra.Command{
Short: "Edit a milestone",
Long: "Edit an existing milestone's title, description, due date, or state.",
Example: ` # Rename a milestone
fgj milestone edit "v1.0" --title "v1.1"
fj milestone edit "v1.0" --title "v1.1"
# Close a milestone
fgj milestone edit "v1.0" --state closed
fj milestone edit "v1.0" --state closed
# Update due date
fgj milestone edit 1 --due 2026-12-31`,
fj milestone edit 1 --due 2026-12-31`,
Args: cobra.ExactArgs(1),
RunE: runMilestoneEdit,
}
@ -90,13 +90,13 @@ var milestoneDeleteCmd = &cobra.Command{
Short: "Delete a milestone",
Long: "Delete an existing milestone.",
Example: ` # Delete by title
fgj milestone delete "v1.0"
fj milestone delete "v1.0"
# Delete by ID
fgj milestone delete 1
fj milestone delete 1
# Delete without confirmation
fgj milestone delete "v1.0" -y`,
fj milestone delete "v1.0" -y`,
Args: cobra.ExactArgs(1),
RunE: runMilestoneDelete,
}

View file

@ -7,10 +7,10 @@ import (
"strings"
"code.gitea.io/sdk/gitea"
"forgejo.zerova.net/public/fgj-sid/internal/api"
"forgejo.zerova.net/public/fgj-sid/internal/config"
gitpkg "forgejo.zerova.net/public/fgj-sid/internal/git"
"forgejo.zerova.net/public/fgj-sid/internal/text"
"forgejo.zerova.net/public/fj/internal/api"
"forgejo.zerova.net/public/fj/internal/config"
gitpkg "forgejo.zerova.net/public/fj/internal/git"
"forgejo.zerova.net/public/fj/internal/text"
"github.com/spf13/cobra"
)
@ -26,13 +26,13 @@ var prListCmd = &cobra.Command{
Short: "List pull requests",
Long: "List pull requests in a repository.",
Example: ` # List open pull requests
fgj pr list
fj pr list
# List all pull requests for a specific repo
fgj pr list -s all -R owner/repo
fj pr list -s all -R owner/repo
# Output as JSON
fgj pr list --json`,
fj pr list --json`,
RunE: runPRList,
}
@ -41,19 +41,19 @@ var prViewCmd = &cobra.Command{
Short: "View a pull request",
Long: "Display detailed information about a pull request.",
Example: ` # View pull request #5
fgj pr view 5
fj pr view 5
# View using URL
fgj pr view https://codeberg.org/owner/repo/pulls/5
fj pr view https://codeberg.org/owner/repo/pulls/5
# View PR for current branch
fgj pr view
fj pr view
# Open in browser
fgj pr view 5 --web
fj pr view 5 --web
# View as JSON
fgj pr view 5 --json`,
fj pr view 5 --json`,
Args: cobra.MaximumNArgs(1),
RunE: runPRView,
}
@ -63,13 +63,13 @@ var prCreateCmd = &cobra.Command{
Short: "Create a pull request",
Long: "Create a new pull request.",
Example: ` # Create a pull request from feature branch to main
fgj pr create -t "Add login page" -H feature/login
fj pr create -t "Add login page" -H feature/login
# Create with body and custom base branch
fgj pr create -t "Fix bug" -b "Closes #42" -H fix/bug -B develop
fj pr create -t "Fix bug" -b "Closes #42" -H fix/bug -B develop
# Create and self-assign
fgj pr create -t "Update docs" -H docs/update -a @me`,
fj pr create -t "Update docs" -H docs/update -a @me`,
RunE: runPRCreate,
}
@ -78,16 +78,16 @@ var prMergeCmd = &cobra.Command{
Short: "Merge a pull request",
Long: "Merge a pull request.",
Example: ` # Merge pull request #5
fgj pr merge 5
fj pr merge 5
# Squash merge
fgj pr merge 5 --merge-method squash
fj pr merge 5 --merge-method squash
# Rebase merge
fgj pr merge 5 --merge-method rebase
fj pr merge 5 --merge-method rebase
# Merge without confirmation
fgj pr merge 5 -y`,
fj pr merge 5 -y`,
Args: cobra.ExactArgs(1),
RunE: runPRMerge,
}
@ -97,10 +97,10 @@ var prCloseCmd = &cobra.Command{
Short: "Close a pull request",
Long: "Close a pull request without merging.",
Example: ` # Close PR #5
fgj pr close 5
fj pr close 5
# Close with a comment
fgj pr close 5 -c "Won't merge, superseded by #10"`,
fj pr close 5 -c "Won't merge, superseded by #10"`,
Args: cobra.ExactArgs(1),
RunE: runPRClose,
}
@ -110,7 +110,7 @@ var prReopenCmd = &cobra.Command{
Short: "Reopen a pull request",
Long: "Reopen a closed pull request.",
Example: ` # Reopen PR #5
fgj pr reopen 5`,
fj pr reopen 5`,
Args: cobra.ExactArgs(1),
RunE: runPRReopen,
}
@ -120,13 +120,13 @@ var prEditCmd = &cobra.Command{
Short: "Edit a pull request",
Long: "Edit a pull request's title, body, or metadata.",
Example: ` # Update the title of PR #5
fgj pr edit 5 -t "Updated title"
fj pr edit 5 -t "Updated title"
# Add assignees and labels
fgj pr edit 5 --add-assignee user1 --add-label bug
fj pr edit 5 --add-assignee user1 --add-label bug
# Remove a reviewer and set milestone
fgj pr edit 5 --remove-reviewer user2 --milestone "v1.0"`,
fj pr edit 5 --remove-reviewer user2 --milestone "v1.0"`,
Args: cobra.ExactArgs(1),
RunE: runPREdit,
}
@ -136,7 +136,7 @@ var prCheckoutCmd = &cobra.Command{
Short: "Check out a pull request locally",
Long: "Check out the head branch of a pull request.",
Example: ` # Check out PR #5
fgj pr checkout 5`,
fj pr checkout 5`,
Args: cobra.ExactArgs(1),
RunE: runPRCheckout,
}

View file

@ -4,9 +4,9 @@ import (
"fmt"
"code.gitea.io/sdk/gitea"
"forgejo.zerova.net/public/fgj-sid/internal/api"
"forgejo.zerova.net/public/fgj-sid/internal/config"
"forgejo.zerova.net/public/fgj-sid/internal/iostreams"
"forgejo.zerova.net/public/fj/internal/api"
"forgejo.zerova.net/public/fj/internal/config"
"forgejo.zerova.net/public/fj/internal/iostreams"
"github.com/spf13/cobra"
)
@ -15,10 +15,10 @@ var prChecksCmd = &cobra.Command{
Short: "Show CI status checks for a pull request",
Long: "Show the status of CI checks for a pull request.",
Example: ` # Show checks for PR #5
fgj pr checks 5
fj pr checks 5
# Output as JSON
fgj pr checks 5 --json`,
fj pr checks 5 --json`,
Args: cobra.ExactArgs(1),
RunE: runPRChecks,
}

View file

@ -4,8 +4,8 @@ import (
"fmt"
"strings"
"forgejo.zerova.net/public/fgj-sid/internal/api"
"forgejo.zerova.net/public/fgj-sid/internal/config"
"forgejo.zerova.net/public/fj/internal/api"
"forgejo.zerova.net/public/fj/internal/config"
"github.com/spf13/cobra"
)
@ -14,16 +14,16 @@ var prDiffCmd = &cobra.Command{
Short: "Show the diff for a pull request",
Long: "Fetch and display the diff for a pull request.",
Example: ` # View the diff for PR #123
fgj pr diff 123
fj pr diff 123
# Colorized diff output
fgj pr diff 123 --color always
fj pr diff 123 --color always
# Show only changed file names
fgj pr diff 123 --name-only
fj pr diff 123 --name-only
# Show diffstat summary
fgj pr diff 123 --stat`,
fj pr diff 123 --stat`,
Args: cobra.ExactArgs(1),
RunE: runPRDiff,
}

View file

@ -6,8 +6,8 @@ import (
"os"
"code.gitea.io/sdk/gitea"
"forgejo.zerova.net/public/fgj-sid/internal/api"
"forgejo.zerova.net/public/fgj-sid/internal/config"
"forgejo.zerova.net/public/fj/internal/api"
"forgejo.zerova.net/public/fj/internal/config"
"github.com/spf13/cobra"
)
@ -16,16 +16,16 @@ var prCommentCmd = &cobra.Command{
Short: "Add a comment to a pull request",
Long: "Add a comment to an existing pull request.",
Example: ` # Add a comment
fgj pr comment 123 -b "Looks good!"
fj pr comment 123 -b "Looks good!"
# Comment from a file
fgj pr comment 123 --body-file review-notes.md
fj pr comment 123 --body-file review-notes.md
# Comment from stdin
echo "LGTM" | fgj pr comment 123 --body-file -
echo "LGTM" | fj pr comment 123 --body-file -
# Output as JSON
fgj pr comment 123 -b "Nice work" --json`,
fj pr comment 123 -b "Nice work" --json`,
Args: cobra.ExactArgs(1),
RunE: runPRComment,
}
@ -35,16 +35,16 @@ var prReviewCmd = &cobra.Command{
Short: "Submit a review on a pull request",
Long: "Submit a review on a pull request. Exactly one of --approve, --request-changes, or --comment must be specified.",
Example: ` # Approve a PR
fgj pr review 123 --approve -b "LGTM"
fj pr review 123 --approve -b "LGTM"
# Request changes
fgj pr review 123 --request-changes -b "Please fix the error handling"
fj pr review 123 --request-changes -b "Please fix the error handling"
# Submit a review comment
fgj pr review 123 --comment -b "Some observations"
fj pr review 123 --comment -b "Some observations"
# Request changes with body from file
fgj pr review 123 --request-changes --body-file feedback.md`,
fj pr review 123 --request-changes --body-file feedback.md`,
Args: cobra.ExactArgs(1),
RunE: runPRReview,
}

View file

@ -9,9 +9,9 @@ import (
"time"
"code.gitea.io/sdk/gitea"
"forgejo.zerova.net/public/fgj-sid/internal/api"
"forgejo.zerova.net/public/fgj-sid/internal/config"
"forgejo.zerova.net/public/fgj-sid/internal/text"
"forgejo.zerova.net/public/fj/internal/api"
"forgejo.zerova.net/public/fj/internal/config"
"forgejo.zerova.net/public/fj/internal/text"
"github.com/spf13/cobra"
)
@ -27,13 +27,13 @@ var releaseListCmd = &cobra.Command{
Short: "List releases",
Long: "List releases in a repository.",
Example: ` # List releases
fgj release list
fj release list
# List only draft releases
fgj release list --draft
fj release list --draft
# Output as JSON with a custom limit
fgj release list --json --limit 10`,
fj release list --json --limit 10`,
RunE: runReleaseList,
}
@ -42,16 +42,16 @@ var releaseViewCmd = &cobra.Command{
Short: "View a release",
Long: "Display detailed information about a release.",
Example: ` # View a release by tag
fgj release view v1.0.0
fj release view v1.0.0
# View the latest release
fgj release view latest
fj release view latest
# Open in browser
fgj release view v1.0.0 --web
fj release view v1.0.0 --web
# Output as JSON
fgj release view v1.0.0 --json`,
fj release view v1.0.0 --json`,
Args: cobra.ExactArgs(1),
RunE: runReleaseView,
}
@ -61,16 +61,16 @@ var releaseCreateCmd = &cobra.Command{
Short: "Create a release",
Long: "Create a new release and optionally upload assets.",
Example: ` # Create a release
fgj release create v1.0.0
fj release create v1.0.0
# Create with title and notes
fgj release create v1.0.0 -t "First stable release" -n "Bug fixes and improvements"
fj release create v1.0.0 -t "First stable release" -n "Bug fixes and improvements"
# Create a draft prerelease with assets
fgj release create v2.0.0-rc1 --draft --prerelease dist/*.tar.gz
fj release create v2.0.0-rc1 --draft --prerelease dist/*.tar.gz
# Create from release notes file
fgj release create v1.0.0 -F CHANGELOG.md`,
fj release create v1.0.0 -F CHANGELOG.md`,
Args: cobra.MinimumNArgs(1),
RunE: runReleaseCreate,
}
@ -80,10 +80,10 @@ var releaseUploadCmd = &cobra.Command{
Short: "Upload release assets",
Long: "Upload assets to an existing release.",
Example: ` # Upload assets to a release
fgj release upload v1.0.0 dist/app-linux-amd64 dist/app-darwin-arm64
fj release upload v1.0.0 dist/app-linux-amd64 dist/app-darwin-arm64
# Upload to the latest release, overwriting existing assets
fgj release upload latest build/output.zip --clobber`,
fj release upload latest build/output.zip --clobber`,
Args: cobra.MinimumNArgs(2),
RunE: runReleaseUpload,
}
@ -93,13 +93,13 @@ var releaseDownloadCmd = &cobra.Command{
Short: "Download release assets",
Long: "Download assets from a release.",
Example: ` # Download all assets from a release
fgj release download v1.0.0
fj release download v1.0.0
# Download to a specific directory
fgj release download v1.0.0 -D ./downloads
fj release download v1.0.0 -D ./downloads
# Download a specific asset by name pattern
fgj release download v1.0.0 -p "*.tar.gz"`,
fj release download v1.0.0 -p "*.tar.gz"`,
Args: cobra.ExactArgs(1),
RunE: runReleaseDownload,
}
@ -109,13 +109,13 @@ var releaseDeleteCmd = &cobra.Command{
Short: "Delete a release",
Long: "Delete a release by tag, keeping its Git tag intact.",
Example: ` # Delete a release by tag
fgj release delete v1.0.0
fj release delete v1.0.0
# Delete the latest release
fgj release delete latest
fj release delete latest
# Delete without confirmation
fgj release delete v1.0.0 -y`,
fj release delete v1.0.0 -y`,
Args: cobra.ExactArgs(1),
RunE: runReleaseDelete,
}

View file

@ -8,9 +8,9 @@ import (
"strings"
"code.gitea.io/sdk/gitea"
"forgejo.zerova.net/public/fgj-sid/internal/api"
"forgejo.zerova.net/public/fgj-sid/internal/config"
"forgejo.zerova.net/public/fgj-sid/internal/text"
"forgejo.zerova.net/public/fj/internal/api"
"forgejo.zerova.net/public/fj/internal/config"
"forgejo.zerova.net/public/fj/internal/text"
"github.com/spf13/cobra"
)
@ -67,22 +67,22 @@ var repoEditCmd = &cobra.Command{
Short: "Edit repository settings",
Long: "Edit settings of an existing repository such as visibility, description, homepage, and default branch.",
Example: ` # Make a repository private
fgj repo edit owner/repo --private
fj repo edit owner/repo --private
# Make a repository public
fgj repo edit owner/repo --public
fj repo edit owner/repo --public
# Update description and homepage
fgj repo edit owner/repo -d "New description" --homepage https://example.com
fj repo edit owner/repo -d "New description" --homepage https://example.com
# Change default branch
fgj repo edit --default-branch develop
fj repo edit --default-branch develop
# Rename a repository
fgj repo edit owner/repo --name new-name
fj repo edit owner/repo --name new-name
# Edit current repo (auto-detected from git context)
fgj repo edit --public`,
fj repo edit --public`,
Args: cobra.MaximumNArgs(1),
RunE: runRepoEdit,
}
@ -90,12 +90,12 @@ var repoEditCmd = &cobra.Command{
var repoRenameCmd = &cobra.Command{
Use: "rename <new-name>",
Short: "Rename a repository",
Long: "Rename an existing repository. This is a shorthand for `fgj repo edit --name <new-name>`.",
Long: "Rename an existing repository. This is a shorthand for `fj repo edit --name <new-name>`.",
Example: ` # Rename current repo
fgj repo rename new-name
fj repo rename new-name
# Rename a specific repo
fgj repo rename new-name -R owner/old-name`,
fj repo rename new-name -R owner/old-name`,
Args: cobra.ExactArgs(1),
RunE: runRepoRename,
}

View file

@ -6,7 +6,7 @@ import (
"strconv"
"strings"
"forgejo.zerova.net/public/fgj-sid/internal/git"
"forgejo.zerova.net/public/fj/internal/git"
"github.com/spf13/cobra"
"github.com/spf13/viper"
)
@ -15,9 +15,9 @@ var cfgFile string
var jsonErrors bool
var rootCmd = &cobra.Command{
Use: "fgj",
Use: "fj",
Short: "Forgejo CLI tool - work seamlessly with Forgejo from the command line",
Long: `fgj is a command line tool for Forgejo instances (including Codeberg).
Long: `fj is a command line tool for Forgejo instances (including Codeberg).
It brings pull requests, issues, and other Forgejo concepts to the terminal.`,
Version: "0.3.1",
SilenceErrors: true,
@ -35,7 +35,7 @@ func Execute() error {
func init() {
cobra.OnInitialize(initConfig)
rootCmd.PersistentFlags().StringVar(&cfgFile, "config", "", "config file (default is $HOME/.config/fgj/config.yaml)")
rootCmd.PersistentFlags().StringVar(&cfgFile, "config", "", "config file (default is $HOME/.config/fj/config.yaml)")
rootCmd.PersistentFlags().BoolVar(&jsonErrors, "json-errors", false, "output errors as structured JSON to stderr")
rootCmd.PersistentFlags().String("hostname", "", "Forgejo instance hostname")
_ = viper.BindPFlag("hostname", rootCmd.PersistentFlags().Lookup("hostname"))
@ -51,7 +51,7 @@ func initConfig() {
os.Exit(1)
}
configDir := home + "/.config/fgj"
configDir := home + "/.config/fj"
_ = os.MkdirAll(configDir, 0755)
viper.AddConfigPath(configDir)

View file

@ -7,9 +7,9 @@ import (
"net/url"
"time"
"forgejo.zerova.net/public/fgj-sid/internal/api"
"forgejo.zerova.net/public/fgj-sid/internal/config"
"forgejo.zerova.net/public/fgj-sid/internal/text"
"forgejo.zerova.net/public/fj/internal/api"
"forgejo.zerova.net/public/fj/internal/config"
"forgejo.zerova.net/public/fj/internal/text"
"github.com/spf13/cobra"
)
@ -61,13 +61,13 @@ var wikiListCmd = &cobra.Command{
Short: "List wiki pages",
Long: "List all wiki pages for a repository.",
Example: ` # List wiki pages for the current repo
fgj wiki list
fj wiki list
# List wiki pages for a specific repo
fgj wiki list -R owner/repo
fj wiki list -R owner/repo
# Output as JSON
fgj wiki list --json`,
fj wiki list --json`,
RunE: runWikiList,
}
@ -76,16 +76,16 @@ var wikiViewCmd = &cobra.Command{
Short: "View a wiki page",
Long: "Display the content of a wiki page.",
Example: ` # View a wiki page
fgj wiki view Home
fj wiki view Home
# Open in browser
fgj wiki view Home --web
fj wiki view Home --web
# View a wiki page as JSON (includes content)
fgj wiki view Home --json
fj wiki view Home --json
# View a wiki page from a specific repo
fgj wiki view "Getting-Started" -R owner/repo`,
fj wiki view "Getting-Started" -R owner/repo`,
Args: cobra.ExactArgs(1),
RunE: runWikiView,
}
@ -95,16 +95,16 @@ var wikiCreateCmd = &cobra.Command{
Short: "Create a wiki page",
Long: "Create a new wiki page in the repository.",
Example: ` # Create a wiki page with inline content
fgj wiki create "Getting Started" -b "# Welcome\nThis is the getting started guide."
fj wiki create "Getting Started" -b "# Welcome\nThis is the getting started guide."
# Create a wiki page from a file
fgj wiki create "Setup Guide" --body-file setup.md
fj wiki create "Setup Guide" --body-file setup.md
# Create a wiki page from stdin
echo "# FAQ" | fgj wiki create FAQ --body-file -
echo "# FAQ" | fj wiki create FAQ --body-file -
# Output as JSON
fgj wiki create "New Page" -b "Content here" --json`,
fj wiki create "New Page" -b "Content here" --json`,
Args: cobra.ExactArgs(1),
RunE: runWikiCreate,
}
@ -114,16 +114,16 @@ var wikiEditCmd = &cobra.Command{
Short: "Edit a wiki page",
Long: "Edit an existing wiki page in the repository.",
Example: ` # Edit a wiki page with new content
fgj wiki edit Home -b "# Updated Home\nNew content here."
fj wiki edit Home -b "# Updated Home\nNew content here."
# Edit a wiki page from a file
fgj wiki edit "Setup Guide" --body-file updated-setup.md
fj wiki edit "Setup Guide" --body-file updated-setup.md
# Edit a wiki page from stdin
cat new-content.md | fgj wiki edit Home --body-file -
cat new-content.md | fj wiki edit Home --body-file -
# Output as JSON
fgj wiki edit Home -b "Updated content" --json`,
fj wiki edit Home -b "Updated content" --json`,
Args: cobra.ExactArgs(1),
RunE: runWikiEdit,
}
@ -133,13 +133,13 @@ var wikiDeleteCmd = &cobra.Command{
Short: "Delete a wiki page",
Long: "Delete a wiki page from the repository.",
Example: ` # Delete a wiki page
fgj wiki delete "Old Page"
fj wiki delete "Old Page"
# Delete without confirmation
fgj wiki delete "Old Page" -y
fj wiki delete "Old Page" -y
# Delete a wiki page from a specific repo
fgj wiki delete "Outdated Guide" -R owner/repo`,
fj wiki delete "Outdated Guide" -R owner/repo`,
Args: cobra.ExactArgs(1),
RunE: runWikiDelete,
}

2
go.mod
View file

@ -1,4 +1,4 @@
module forgejo.zerova.net/public/fgj-sid
module forgejo.zerova.net/public/fj
go 1.24.0

View file

@ -9,7 +9,7 @@ import (
"time"
"code.gitea.io/sdk/gitea"
"forgejo.zerova.net/public/fgj-sid/internal/config"
"forgejo.zerova.net/public/fj/internal/config"
)
var sharedHTTPClient = &http.Client{

View file

@ -3,7 +3,7 @@ package api
import (
"testing"
"forgejo.zerova.net/public/fgj-sid/internal/config"
"forgejo.zerova.net/public/fj/internal/config"
)
func TestClient_Hostname(t *testing.T) {

View file

@ -25,13 +25,13 @@ type HostConfig struct {
func GetConfigDir() (string, error) {
if xdgConfigHome := os.Getenv("XDG_CONFIG_HOME"); xdgConfigHome != "" {
return filepath.Join(xdgConfigHome, "fgj"), nil
return filepath.Join(xdgConfigHome, "fj"), nil
}
home, err := os.UserHomeDir()
if err != nil {
return "", err
}
return filepath.Join(home, ".config", "fgj"), nil
return filepath.Join(home, ".config", "fj"), nil
}
func GetConfigPath() (string, error) {

View file

@ -83,7 +83,7 @@ func TestGetConfigDir_XDG(t *testing.T) {
t.Fatalf("Unexpected error: %v", err)
}
expected := "/custom/config/fgj"
expected := "/custom/config/fj"
if dir != expected {
t.Errorf("Expected %q, got %q", expected, dir)
}
@ -439,7 +439,7 @@ func TestResolveHostByPath(t *testing.T) {
"forgejo.zerova.net": {
Hostname: "forgejo.zerova.net",
Token: "token1",
MatchDirs: []string{"/Users/sid/repos/fgj", "/Users/sid/repos/zerova"},
MatchDirs: []string{"/Users/sid/repos/fj", "/Users/sid/repos/zerova"},
},
"codeberg.org": {
Hostname: "codeberg.org",
@ -459,10 +459,10 @@ func TestResolveHostByPath(t *testing.T) {
cwd string
want string
}{
{"exact dir match", "/Users/sid/repos/fgj", "forgejo.zerova.net"},
{"nested dir match", "/Users/sid/repos/fgj/cmd/root.go", "forgejo.zerova.net"},
{"exact dir match", "/Users/sid/repos/fj", "forgejo.zerova.net"},
{"nested dir match", "/Users/sid/repos/fj/cmd/root.go", "forgejo.zerova.net"},
{"second match dir", "/Users/sid/repos/zerova/pkg", "forgejo.zerova.net"},
{"longest prefix wins over /", "/Users/sid/repos/fgj/internal", "forgejo.zerova.net"},
{"longest prefix wins over /", "/Users/sid/repos/fj/internal", "forgejo.zerova.net"},
{"/ as global catch-all", "/tmp", "codeberg.org"},
{"/ matches root itself", "/", "codeberg.org"},
{"no match_dirs host not selected", "/some/random/path", "codeberg.org"},
@ -512,7 +512,7 @@ func TestGetHost_MatchDirsIntegration(t *testing.T) {
"forgejo.zerova.net": {
Hostname: "forgejo.zerova.net",
Token: "token1",
MatchDirs: []string{"/Users/sid/repos/fgj"},
MatchDirs: []string{"/Users/sid/repos/fj"},
},
"codeberg.org": {
Hostname: "codeberg.org",
@ -522,7 +522,7 @@ func TestGetHost_MatchDirsIntegration(t *testing.T) {
}
// cwd match should resolve to forgejo.zerova.net
host, err := cfg.GetHost("", "", "/Users/sid/repos/fgj/cmd")
host, err := cfg.GetHost("", "", "/Users/sid/repos/fj/cmd")
if err != nil {
t.Fatalf("unexpected error: %v", err)
}

View file

@ -17,41 +17,41 @@ func TestParseRemoteURL(t *testing.T) {
}{
{
name: "HTTPS URL with .git",
url: "https://codeberg.org/romaintb/fgj.git",
url: "https://codeberg.org/romaintb/fj.git",
wantOwner: "romaintb",
wantName: "fgj",
wantName: "fj",
wantHost: "codeberg.org",
wantErr: false,
},
{
name: "HTTPS URL without .git",
url: "https://codeberg.org/romaintb/fgj",
url: "https://codeberg.org/romaintb/fj",
wantOwner: "romaintb",
wantName: "fgj",
wantName: "fj",
wantHost: "codeberg.org",
wantErr: false,
},
{
name: "SSH URL with .git",
url: "git@codeberg.org:romaintb/fgj.git",
url: "git@codeberg.org:romaintb/fj.git",
wantOwner: "romaintb",
wantName: "fgj",
wantName: "fj",
wantHost: "codeberg.org",
wantErr: false,
},
{
name: "SSH URL without .git",
url: "git@codeberg.org:romaintb/fgj",
url: "git@codeberg.org:romaintb/fj",
wantOwner: "romaintb",
wantName: "fgj",
wantName: "fj",
wantHost: "codeberg.org",
wantErr: false,
},
{
name: "SSH protocol URL",
url: "ssh://git@codeberg.org/romaintb/fgj.git",
url: "ssh://git@codeberg.org/romaintb/fj.git",
wantOwner: "romaintb",
wantName: "fgj",
wantName: "fj",
wantHost: "codeberg.org",
wantErr: false,
},

View file

@ -4,7 +4,7 @@ import (
"fmt"
"os"
"forgejo.zerova.net/public/fgj-sid/cmd"
"forgejo.zerova.net/public/fj/cmd"
)
func main() {

View file

@ -228,15 +228,15 @@ func (env *TestEnv) CleanupRepo(owner, repoName string) {
}
}
// GetBinaryPath returns the path to the built fgj binary
// GetBinaryPath returns the path to the built fj binary
func (env *TestEnv) GetBinaryPath() string {
binaryPath := os.Getenv("FGJ_BINARY_PATH")
if binaryPath == "" {
// Look for the binary in common locations
candidates := []string{
"./bin/fgj",
"bin/fgj",
"/home/romain/work/fgj/bin/fgj",
"./bin/fj",
"bin/fj",
"/home/romain/work/fj/bin/fj",
}
for _, candidate := range candidates {
if _, err := os.Stat(candidate); err == nil {
@ -248,7 +248,7 @@ func (env *TestEnv) GetBinaryPath() string {
}
}
// If no binary found, return default (will error when executed)
binaryPath = "./bin/fgj"
binaryPath = "./bin/fj"
}
return binaryPath
}

View file

@ -199,7 +199,7 @@ func TestCLIIssueCreate(t *testing.T) {
"-R", fmt.Sprintf("%s/%s", env.Owner, env.RepoName),
"issue", "create",
"-t", "[FGJ E2E Test] CLI Created Issue",
"-b", "Created directly via fgj CLI",
"-b", "Created directly via fj CLI",
)
if result.ExitCode != 0 {
@ -624,14 +624,14 @@ func TestCLIPRComment(t *testing.T) {
"-R", fmt.Sprintf("%s/%s", env.Owner, env.RepoName),
"pr", "comment",
fmt.Sprintf("%d", issueNum),
"-b", "Automated test comment via fgj pr comment",
"-b", "Automated test comment via fj pr comment",
)
if result.ExitCode != 0 {
t.Fatalf("pr comment failed with exit code %d: %s", result.ExitCode, result.Stderr)
}
t.Logf("Successfully commented on issue #%d via fgj pr comment", issueNum)
t.Logf("Successfully commented on issue #%d via fj pr comment", issueNum)
}
// ===== CLI Repo Commands =====
@ -678,14 +678,14 @@ func TestCLIRepoList(t *testing.T) {
func TestCLIRepoCreate(t *testing.T) {
env := NewTestEnv(t)
repoName := fmt.Sprintf("fgj-test-create-%d", time.Now().UnixNano())
repoName := fmt.Sprintf("fj-test-create-%d", time.Now().UnixNano())
defer env.CleanupRepo(env.Owner, repoName)
result := env.RunCLI(
"--hostname", env.Hostname,
"repo", "create", repoName,
"--public",
"-d", "Created by fgj functional test",
"-d", "Created by fj functional test",
)
if result.ExitCode != 0 {
@ -703,8 +703,8 @@ func TestCLIRepoCreate(t *testing.T) {
if repo.Private {
t.Fatalf("expected public repo, got private")
}
if repo.Description != "Created by fgj functional test" {
t.Fatalf("expected description %q, got %q", "Created by fgj functional test", repo.Description)
if repo.Description != "Created by fj functional test" {
t.Fatalf("expected description %q, got %q", "Created by fj functional test", repo.Description)
}
t.Logf("Successfully created repository %s via CLI", repo.FullName)
@ -756,7 +756,7 @@ func TestCLIRepoClone(t *testing.T) {
env := NewTestEnv(t)
tmpDir := t.TempDir()
clonePath := fmt.Sprintf("%s/fgj-clone", tmpDir)
clonePath := fmt.Sprintf("%s/fj-clone", tmpDir)
result := env.RunCLI(
"--hostname", env.Hostname,
@ -799,13 +799,13 @@ func TestCLIReleaseList(t *testing.T) {
func TestCLIReleaseCreateUploadDelete(t *testing.T) {
env := NewTestEnv(t)
tag := fmt.Sprintf("fgj-test-%d", time.Now().UnixNano())
tag := fmt.Sprintf("fj-test-%d", time.Now().UnixNano())
title := "FGJ CLI Release Test"
notes := "Release created by functional tests"
tmpDir := t.TempDir()
assetPath := fmt.Sprintf("%s/asset.txt", tmpDir)
if err := os.WriteFile(assetPath, []byte("fgj release asset"), 0600); err != nil {
if err := os.WriteFile(assetPath, []byte("fj release asset"), 0600); err != nil {
t.Fatalf("failed to create asset file: %v", err)
}
@ -863,7 +863,7 @@ func TestCLIReleaseView(t *testing.T) {
env := NewTestEnv(t)
// Create a release to view
tag := fmt.Sprintf("fgj-view-test-%d", time.Now().UnixNano())
tag := fmt.Sprintf("fj-view-test-%d", time.Now().UnixNano())
createResult := env.RunCLI(
"--hostname", env.Hostname,
@ -1149,7 +1149,7 @@ func TestCLIAPIGet(t *testing.T) {
t.Fatalf("expected repo name %q in JSON output, got %v", env.RepoName, data["name"])
}
t.Logf("Successfully retrieved repo info via fgj api GET")
t.Logf("Successfully retrieved repo info via fj api GET")
}
func TestCLIAPIPostAndDelete(t *testing.T) {
@ -1162,7 +1162,7 @@ func TestCLIAPIPostAndDelete(t *testing.T) {
"api", endpoint,
"-X", "POST",
"-f", "title=[FGJ E2E Test] API Post Test",
"-f", "body=Created via fgj api command",
"-f", "body=Created via fj api command",
)
if result.ExitCode != 0 {
@ -1182,7 +1182,7 @@ func TestCLIAPIPostAndDelete(t *testing.T) {
issueNum := int64(issueNumber)
defer env.CleanupIssue(issueNum)
t.Logf("Successfully created issue #%d via fgj api POST", issueNum)
t.Logf("Successfully created issue #%d via fj api POST", issueNum)
}
// ===== Structured Error Output =====