- Go 99.2%
- Makefile 0.8%
| Filename | Latest commit message | Latest commit date |
|---|---|---|
| .forgejo | ||
| .handoff | ||
| cmd/fja | ||
| docs | ||
| internal | ||
| .gitignore | ||
| go.mod | ||
| LICENSE | ||
| Makefile | ||
| README.ja.md | ||
| README.md | ||
forgejo-agent / fja
fja is a small CLI for AI agents to inspect and diagnose Forgejo release Actions reliably.
- Writes exactly one JSON object to standard output for both success and failure
- Distinguishes configuration, authentication, API, Git, and CI outcomes with exit codes
- Never exposes tokens in JSON, errors, or URLs
- Handles timeouts and cancellation for every HTTP operation
- Uses only the Go standard library; no external Go modules
Requirements
- Go 1.26.4 or later
- Git
- A Forgejo API token
HTTPS Forgejo instances are expected in normal use. HTTP connections are permitted only for local testing when explicitly enabled with --allow-http.
Forgejo version support
fja supports Forgejo v16 native Actions run-log downloads. On v15, ci status and ci watch remain available and ci logs falls back to the Release-asset diagnostic summary.
Installation
make install
This installs fja to ~/bin/fja. Ensure that ~/bin is on your PATH.
To build it to a specific path:
make build OUTPUT="$HOME/bin/fja"
Configuration
Store credentials outside a project-local .env file, at:
~/.config/fja/config.env
FJA_URL=https://forgejo.example.com
FJA_TOKEN=xxxxxxxx
FJA_TIMEOUT=10m
FJA_POLL_INTERVAL=5s
File mode 0600 is recommended.
chmod 600 ~/.config/fja/config.env
fja config check
Configuration precedence is: command-line options, environment variables, the configuration file, then inference from Git origin. The environment variables FJA_URL, FJA_TOKEN, FJA_REPO, FJA_TIMEOUT, and FJA_POLL_INTERVAL are supported. FORGEJO_URL and FORGEJO_TOKEN are compatibility fallbacks.
In most cases, do not write FJA_REPO to global configuration. ci commands resolve their target in this order: --repo, FJA_REPO, then the Git origin of the current project. This avoids accidental operations across projects.
--token is also available, but it may remain visible in the process list. Use an environment variable or the configuration file instead.
Commands
Run fja doc to obtain the complete, machine-readable command reference, including JSON RPC input and response contracts. --help for a subcommand links to the same reference.
fja version
fja config check
fja ci status [--repo OWNER/REPO] [--tag TAG] [--run-id ID]
fja ci watch [--repo OWNER/REPO] [--tag TAG] [--run-id ID]
[--interval 5s] [--timeout 10m]
fja ci logs [--repo OWNER/REPO] [--tag TAG] [--run-id ID] [--save DIR]
fja ci artifacts [--repo OWNER/REPO] [--tag TAG] [--run-id ID] [--name NAME]
fja ci artifact download --repo OWNER/REPO --artifact-id ID [--save DIR]
fja json
fja release assets --tag TAG
fja release download --tag TAG --asset NAME [--save DIR]
fja release verify --tag TAG --asset NAME [--save DIR]
Check CI status
fja ci status --tag v0.1.5
fja ci status --run-id 31
fja ci status --repo OWNER/REPO
Run selection rules:
- When
--run-idis present, fetch that run directly. - When
--tagis present, match in order against the tag ref, the tag commit SHA, and run metadata. - Otherwise, fetch the latest run for the target repository.
The matching method and confidence are included in data.match. fja also revalidates Forgejo API candidates so that it does not accidentally select a run for a different tag. When available, it reads actions/tasks too and adds task_id, task name, workflow_id, and related fields to the result.
Wait for CI completion
fja ci watch --tag v0.1.5
fja ci watch --run-id 31 --interval 2s --timeout 15m
By default, it polls every five seconds for up to ten minutes. It emits no intermediate output; standard output contains only the final JSON result. Ctrl+C also returns a JSON-formatted cancellation result.
For a failed run, fja adds only failure-summary lookup metadata. It never embeds log text in a JSON response.
Retrieve a CI failure summary
fja ci logs --tag v0.1.5
fja ci logs --run-id 31 --save ./ci-logs
On Forgejo v16, ci logs downloads the native run-log ZIP and saves its log files to --save DIR, or ./fja-logs/<run-id> by default. JSON reports metadata only. On v15, it falls back to ci-failure-summary.json from the matching Release. data.source identifies native_actions_run_logs or release_asset_workaround.
--tag and --run-id cannot be used together. Existing log files are never overwritten.
Even after a successful retrieval, ci logs returns exit code 0 for a failed target run.
JSON RPC interface
fja json reads exactly one JSON request from standard input and writes the normal single JSON response. Supported commands are version, config.check, ci.status, ci.watch, ci.logs, ci.artifacts, ci.artifact.download, release.assets, release.download, and release.verify.
printf '%s\n' '{"command":"ci.logs","repo":"OWNER/REPO","run_id":123}' | fja json
Request fields correspond to CLI options: url, token, repo, tag, name, run_id, artifact_id, timeout, interval, save, asset, and allow_http.
JSON output
Success example:
{
"ok": true,
"command": "ci.status",
"data": {
"repository": "OWNER/REPO",
"run_id": 31,
"status": "completed",
"conclusion": "success"
},
"meta": {
"version": "v0.1.5",
"timestamp": "2026-06-30T15:00:00+09:00"
}
}
Failure example:
{
"ok": false,
"command": "ci.watch",
"error": {
"code": "ci_failed",
"message": "Workflow run completed with failure",
"details": {
"repository": "OWNER/REPO",
"run_id": 32,
"status": "completed",
"conclusion": "failure"
}
},
"meta": {
"version": "v0.1.5",
"timestamp": "2026-06-30T15:05:00+09:00"
}
}
Exit codes
| Code | Meaning | Main error.code |
|---|---|---|
| 0 | Success | - |
| 2 | Invalid argument or usage | usage_error |
| 3 | Configuration error | config_error |
| 4 | Authentication error | authentication_failed |
| 5 | API communication error | api_communication_error |
| 6 | Forgejo API error | forgejo_api_error, etc. |
| 7 | Git operation error | git_error |
| 20 | CI failure | ci_failed |
| 21 | CI cancellation | ci_cancelled |
| 22 | CI timeout | ci_timeout |
| 23 | No matching run | run_not_found |
| 24 | Unsupported or indeterminate API | unsupported_api |
| 25 | CI skipped | ci_skipped |
For a private repository, Forgejo may return 404 for any of an unsupported API, a nonexistent repository, or insufficient permissions. The unsupported_api error's details include the target endpoint, repository, whether a token was configured, and caveats about the determination.
Forgejo API limitations
Forgejo v16 exposes a public API for run-log ZIP downloads, which fja ci logs uses without HTML scraping. The v15 Release-asset path remains a compatibility fallback.
The research and confirmed endpoints are documented in docs/FORGEJO_API_RESEARCH.md. On Forgejo installations with API differences, fja returns unsupported features as structured errors.
Development and testing
go fmt ./...
go vet ./...
go test ./...
make build
Or:
make fmt vet test build
The normal go test ./... suite includes local simulated E2E tests that build the real fja binary and run a mock Forgejo API with httptest.Server. E2E testing against a real Forgejo instance is explicitly enabled:
FJA_E2E=1 \
FJA_E2E_REPO=OWNER/REPO \
FJA_E2E_RUN_ID=123 \
make e2e
FJA_URL and FJA_TOKEN are read from the environment or ~/.config/fja/config.env.
To exercise the read-only Release asset flow (list, download, and SHA256SUMS verification), also provide a published asset whose checksum is present in that Release:
FJA_E2E=1 FJA_E2E_REPO=OWNER/REPO FJA_E2E_TAG=v1.2.3 \
FJA_E2E_RELEASE_ASSET=fja-linux-amd64 make e2e
Releases
Pushing a v* tag makes Forgejo Actions run the test suite and publish these binaries to a Forgejo Release:
- macOS arm64
- Linux amd64
- Linux arm64
SHA256SUMS(SHA-256 checksums for the three binaries)
The portable release workflow runs go test ./..., go vet ./..., builds all three targets, and uploads SHA256SUMS. It uses Forgejo's automatic forgejo.token context to create the Release. Rerunning the workflow for the same tag replaces existing assets with the same names. No additional reference-environment checks are currently required before tagging.
To cross-build locally:
make cross