2026-07-22 15:16:42 +00:00
# stoke
> CLI for the heavy-duty forge (Forgejo). Named after the act of feeding and tending a fire — the operator's hand on the forge.
2026-07-22 15:03:32 +00:00
A command-line interface for [Forgejo ](https://forgejo.org/ ), built with [Commander.js ](https://github.com/tj/commander.js/ ). It targets the instance at `https://forgejo.heavyduty.builders` and is designed so that every real operation performed against Forgejo becomes a new CLI command.
## Requirements
- Node.js >= 18 (uses the global `fetch` API)
- npm
## Installation
```bash
2026-07-22 15:16:42 +00:00
cd stoke
2026-07-22 15:03:32 +00:00
npm install
2026-07-22 15:16:42 +00:00
npm link # makes the `stoke` binary available globally
2026-07-22 15:03:32 +00:00
```
Or run it directly without linking:
```bash
node src/cli.js < command >
```
## Configuration
Authentication state is stored in a JSON file:
2026-07-22 15:16:42 +00:00
- Default: `~/.config/stoke/config.json`
- Override with `--config <path>` or `STOKE_CONFIG_FILE` (or `FORGEJO_CONFIG_FILE` as a fallback)
2026-07-22 15:03:32 +00:00
The configuration directory is created with permissions `0700` and the file with `0600` so only the owner can read the token.
Example stored config:
```json
{
"url": "https://forgejo.heavyduty.builders",
"login": "kimi-reviewer-andresmgsl",
"username": "kimi-reviewer-andresmgsl",
"email": "andres+4@heavyduty.builders",
"token": "< sha1 > ",
"tokenId": 42
}
```
## Environment variables
| Variable | Purpose |
| --- | --- |
2026-07-22 15:16:42 +00:00
| `STOKE_URL` | Default Forgejo base URL |
| `STOKE_USERNAME` | Default username/email for `auth login` |
| `STOKE_PASSWORD` | Default password for `auth login` |
| `STOKE_CONFIG_FILE` | Path to the config file |
| `STOKE_CONFIG_DIR` | Directory for the config file |
2026-07-22 15:03:32 +00:00
| `XDG_CONFIG_HOME` | Followed when resolving the default config directory |
| `GITHUB_TOKEN` | GitHub token used by `repo import` when `--github-token` is omitted |
2026-07-22 15:16:42 +00:00
`FORGEJO_*` variants are still accepted as fallbacks for backward compatibility.
2026-07-22 15:03:32 +00:00
## Commands
### Global options
```text
-c, --config < path > path to configuration file
-h, --help display help
-V, --version display version
```
2026-07-22 15:16:42 +00:00
### `stoke auth login`
2026-07-22 15:03:32 +00:00
Authenticate and persist an access token.
```text
Options:
-u, --url < url > Forgejo base URL (default: https://forgejo.heavyduty.builders)
-n, --username < username > account username or email
-p, --password < password > account password
--password-file < path > read account password from a file
-t, --token < token > use an existing personal access token instead of generating one
--token-file < path > read an existing personal access token from a file
--token-name < name > name for the generated token
```
Interactive example:
```bash
2026-07-22 15:16:42 +00:00
stoke auth login
2026-07-22 15:03:32 +00:00
# prompts for username and password
```
Non-interactive example using environment variables:
```bash
2026-07-22 15:16:42 +00:00
export STOKE_USERNAME='kimi-reviewer-andresmgsl'
export STOKE_PASSWORD='...'
stoke auth login
2026-07-22 15:03:32 +00:00
```
Password file example (avoids shell history and special-character issues):
```bash
2026-07-22 15:16:42 +00:00
chmod 600 /run/secrets/stoke-password
stoke auth login -n kimi-reviewer-andresmgsl --password-file /run/secrets/stoke-password
2026-07-22 15:03:32 +00:00
```
Existing token example:
```bash
2026-07-22 15:16:42 +00:00
stoke auth login -t < personal-access-token >
2026-07-22 15:03:32 +00:00
```
Flow:
1. Calls `GET /api/v1/user` to verify credentials and resolve the canonical `login` name.
2. Calls `POST /api/v1/users/{login}/tokens` to generate a personal access token.
3. Requests the standard non-admin scopes: `read/write` for `activitypub` , `issue` , `misc` , `organization` , `package` , `repository` , and `user` .
4. Writes the token, token id, user details and URL to the config file.
2026-07-22 15:16:42 +00:00
### `stoke auth logout`
2026-07-22 15:03:32 +00:00
Revoke the stored token remotely and delete the local config.
```bash
2026-07-22 15:16:42 +00:00
stoke auth logout
2026-07-22 15:03:32 +00:00
```
Flow:
1. Calls `DELETE /api/v1/users/{login}/tokens/{id}` using the stored token.
2026-07-22 15:16:42 +00:00
2. Removes `~/.config/stoke/config.json` .
2026-07-22 15:03:32 +00:00
2026-07-22 15:16:42 +00:00
### `stoke auth status`
2026-07-22 15:03:32 +00:00
Display the currently authenticated user.
```bash
2026-07-22 15:16:42 +00:00
stoke auth status
2026-07-22 15:03:32 +00:00
```
Calls `GET /api/v1/user` with the stored token.
2026-07-22 15:16:42 +00:00
### `stoke repo create`
2026-07-22 15:03:32 +00:00
Create a new repository for the authenticated user.
```text
Options:
--name < name > repository name (required)
-d, --description < description > repository description
--private make the repository private
--public make the repository public
--auto-init initialize with a README (default: true)
--default-branch < branch > default branch name (default: "main")
```
Example:
```bash
2026-07-22 15:16:42 +00:00
stoke repo create --name stoke-test --private \
-d "Test repository created via stoke"
2026-07-22 15:03:32 +00:00
```
Calls `POST /api/v1/user/repos` .
2026-07-22 15:16:42 +00:00
### `stoke repo list`
2026-07-22 15:03:32 +00:00
List repositories for the authenticated user.
```text
Options:
-l, --limit < number > maximum repositories to display (default: 50; use 0 for all)
```
```bash
2026-07-22 15:16:42 +00:00
stoke repo list
2026-07-22 15:03:32 +00:00
```
Calls `GET /api/v1/user/repos` and auto-paginates.
2026-07-22 15:16:42 +00:00
### `stoke repo import`
2026-07-22 15:03:32 +00:00
Import a remote repository into Forgejo, including git history, issues, pull requests, labels, milestones, releases and wiki.
```text
Options:
--from < clone-addr > source clone URL (required)
--name < name > repository name in Forgejo (required)
--service < service > source service type: git, github, gitea, gitlab, ... (default: github)
--owner < owner > Forgejo owner for the imported repo (default: current user)
-d, --description < description > repository description
--private / --public visibility
--issues migrate issues (default)
--no-issues skip issues
--labels migrate labels (default)
--no-labels skip labels
--milestones migrate milestones (default)
--no-milestones skip milestones
--pull-requests migrate pull requests (default)
--no-pull-requests skip pull requests
--releases migrate releases (default)
--no-releases skip releases
--wiki migrate wiki (default)
--no-wiki skip wiki
--lfs migrate LFS objects
--github-token < token > GitHub token (defaults to GITHUB_TOKEN or `gh auth token` )
```
Example used to mirror `heavy-duty/box` :
```bash
2026-07-22 15:16:42 +00:00
stoke repo import \
2026-07-22 15:03:32 +00:00
--from https://github.com/heavy-duty/box.git \
--name box \
--service github \
--public \
--issues --labels --milestones --pull-requests --releases --wiki
```
Calls `POST /api/v1/repos/migrate` .
2026-07-22 15:16:42 +00:00
### `stoke repo import-batch`
2026-07-22 15:03:32 +00:00
Import multiple repositories from a JSON manifest.
```text
Options:
-f, --file < path > path to JSON manifest (required)
--dry-run print the manifest without importing
```
Manifest format:
```json
[
{
"name": "rig",
"from": "https://github.com/heavy-duty/rig.git",
"service": "github",
"private": false,
"issues": true,
"labels": true,
"milestones": true,
"pull_requests": true,
"releases": true,
"wiki": true
}
]
```
Example:
```bash
2026-07-22 15:16:42 +00:00
stoke repo import-batch -f repos.json
stoke repo import-batch -f repos.json --dry-run
2026-07-22 15:03:32 +00:00
```
Calls `POST /api/v1/repos/migrate` once per entry.
2026-07-22 15:16:42 +00:00
### `stoke issue list`
2026-07-22 15:03:32 +00:00
List issues in a repository.
```text
Options:
-o, --owner < owner > repository owner (required)
-r, --repo < repo > repository name (required)
-s, --state < state > open, closed, all (default: open)
-t, --type < type > issues or pulls (default: issues)
-l, --limit < number > maximum issues to display (default: 50; use 0 for all)
```
```bash
2026-07-22 15:16:42 +00:00
stoke issue list -o kimi-reviewer-andresmgsl -r box -s all -l 0
2026-07-22 15:03:32 +00:00
```
Calls `GET /api/v1/repos/{owner}/{repo}/issues` and auto-paginates.
2026-07-22 15:16:42 +00:00
### `stoke pr list`
2026-07-22 15:03:32 +00:00
List pull requests in a repository.
```text
Options:
-o, --owner < owner > repository owner (required)
-r, --repo < repo > repository name (required)
-s, --state < state > open, closed, all (default: open)
-l, --limit < number > maximum PRs to display (default: 50; use 0 for all)
```
```bash
2026-07-22 15:16:42 +00:00
stoke pr list -o kimi-reviewer-andresmgsl -r box -s all -l 0
2026-07-22 15:03:32 +00:00
```
Calls `GET /api/v1/repos/{owner}/{repo}/pulls` and auto-paginates.
2026-07-22 15:16:42 +00:00
### `stoke branch list`
2026-07-22 15:03:32 +00:00
List branches in a repository.
```text
Options:
-o, --owner < owner > repository owner (required)
-r, --repo < repo > repository name (required)
-l, --limit < number > maximum branches to display (default: 50; use 0 for all)
```
```bash
2026-07-22 15:16:42 +00:00
stoke branch list -o kimi-reviewer-andresmgsl -r box
2026-07-22 15:03:32 +00:00
```
Calls `GET /api/v1/repos/{owner}/{repo}/branches` and auto-paginates.
2026-07-22 15:16:42 +00:00
### `stoke collaborator add`
2026-07-22 15:06:35 +00:00
Add a collaborator to a repository.
```text
Options:
-o, --owner < owner > repository owner (required)
-r, --repo < repo > repository name (required)
-u, --user < username > username of the collaborator (required)
--permission < permission > read, write, or admin (default: write)
```
Example:
```bash
2026-07-22 15:16:42 +00:00
stoke collaborator add -o kimi-reviewer-andresmgsl -r infra -u andres --permission admin
stoke collaborator add -o kimi-reviewer-andresmgsl -r infra -u dan --permission admin
2026-07-22 15:06:35 +00:00
```
Calls `PUT /api/v1/repos/{owner}/{repo}/collaborators/{user}` .
2026-07-22 15:03:32 +00:00
## Architecture
```text
src/
├── cli.js # Commander program, commands and user I/O
├── api.js # Forgejo API client (fetch wrapper)
└── config.js # Secure filesystem-based config storage
```
- `cli.js` defines commands and options, handles prompts and prints results.
- `api.js` encapsulates all HTTP calls to Forgejo. It supports both Basic auth (for token generation) and token auth (for all other calls).
- `config.js` reads/writes JSON config and enforces restrictive file permissions.
## Security notes
- Tokens are stored on disk with `0600` permissions.
- Passwords are never persisted; they are only used to generate a token.
2026-07-22 15:16:42 +00:00
- Prefer `--password-file` or `STOKE_PASSWORD` over `-p` to keep passwords out of shell history and avoid `!` history-expansion issues.
2026-07-22 15:03:32 +00:00
- The generated token name includes the hostname and a timestamp to avoid collisions.
## Verification: heavy-duty repository imports
The heavy-duty repositories were imported into Forgejo under `https://forgejo.heavyduty.builders/kimi-reviewer-andresmgsl` .
| Repository | Visibility | Branches | Commits | Open issues | Total issues | PRs | Labels | Milestones | Releases |
| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- |
| `box` | public | 1 | 306 | 11 | 65 | 92 | 21 | 0 | 4 |
| `rig` | public | 1 | 198 | 10 | 49 | 59 | 21 | 0 | 3 |
| `cast` | public | 1 | 194 | 7 | 67 | 75 | 21 | 0 | 3 |
| `infra` | private | 1 | 86 | 2 | 8 | 22 | 9 | 0 | 0 |
| `handbook` | private | 1 | 29 | 28 | 28 | 13 | 9 | 5 | 0 |
| `incubator` | private | 1 | 1,326 | 1 | 1 | 23 | 9 | 0 | 0 |
Counts match GitHub for all repositories. Git history, issues, pull requests, labels, milestones and releases are included. Discussions are not enabled on any of the source repositories. The `cast` wiki is enabled on GitHub but contains no pages, so nothing was migrated.
## Next steps
2026-07-22 15:16:42 +00:00
The CLI is authenticated and the first full repository import is complete. Every subsequent Forgejo task you need will be added as a new command under `stoke` .