# 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. 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 cd stoke npm install npm link # makes the `stoke` binary available globally ``` Or run it directly without linking: ```bash node src/cli.js ``` ## Configuration Authentication state is stored in a JSON file: - Default: `~/.config/stoke/config.json` - Override with `--config ` or `STOKE_CONFIG_FILE` (or `FORGEJO_CONFIG_FILE` as a fallback) 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": "", "tokenId": 42 } ``` ## Environment variables | Variable | Purpose | | --- | --- | | `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 | | `XDG_CONFIG_HOME` | Followed when resolving the default config directory | | `GITHUB_TOKEN` | GitHub token used by `repo import` when `--github-token` is omitted | `FORGEJO_*` variants are still accepted as fallbacks for backward compatibility. ## Commands ### Global options ```text -c, --config path to configuration file -h, --help display help -V, --version display version ``` ### `stoke auth login` Authenticate and persist an access token. ```text Options: -u, --url Forgejo base URL (default: https://forgejo.heavyduty.builders) -n, --username account username or email -p, --password account password --password-file read account password from a file -t, --token use an existing personal access token instead of generating one --token-file read an existing personal access token from a file --token-name name for the generated token ``` Interactive example: ```bash stoke auth login # prompts for username and password ``` Non-interactive example using environment variables: ```bash export STOKE_USERNAME='kimi-reviewer-andresmgsl' export STOKE_PASSWORD='...' stoke auth login ``` Password file example (avoids shell history and special-character issues): ```bash chmod 600 /run/secrets/stoke-password stoke auth login -n kimi-reviewer-andresmgsl --password-file /run/secrets/stoke-password ``` Existing token example: ```bash stoke auth login -t ``` 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. ### `stoke auth logout` Revoke the stored token remotely and delete the local config. ```bash stoke auth logout ``` Flow: 1. Calls `DELETE /api/v1/users/{login}/tokens/{id}` using the stored token. 2. Removes `~/.config/stoke/config.json`. ### `stoke auth status` Display the currently authenticated user. ```bash stoke auth status ``` Calls `GET /api/v1/user` with the stored token. ### `stoke repo create` Create a new repository for the authenticated user. ```text Options: --name repository name (required) -d, --description repository description --private make the repository private --public make the repository public --auto-init initialize with a README (default: true) --default-branch default branch name (default: "main") ``` Example: ```bash stoke repo create --name stoke-test --private \ -d "Test repository created via stoke" ``` Calls `POST /api/v1/user/repos`. ### `stoke repo list` List repositories for the authenticated user. ```text Options: -l, --limit maximum repositories to display (default: 50; use 0 for all) ``` ```bash stoke repo list ``` Calls `GET /api/v1/user/repos` and auto-paginates. ### `stoke repo import` Import a remote repository into Forgejo, including git history, issues, pull requests, labels, milestones, releases and wiki. ```text Options: --from source clone URL (required) --name repository name in Forgejo (required) --service source service type: git, github, gitea, gitlab, ... (default: github) --owner Forgejo owner for the imported repo (default: current user) -d, --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 GitHub token (defaults to GITHUB_TOKEN or `gh auth token`) ``` Example used to mirror `heavy-duty/box`: ```bash stoke repo import \ --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`. ### `stoke repo import-batch` Import multiple repositories from a JSON manifest. ```text Options: -f, --file 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 stoke repo import-batch -f repos.json stoke repo import-batch -f repos.json --dry-run ``` Calls `POST /api/v1/repos/migrate` once per entry. ### `stoke issue list` List issues in a repository. ```text Options: -o, --owner repository owner (required) -r, --repo repository name (required) -s, --state open, closed, all (default: open) -t, --type issues or pulls (default: issues) -l, --limit maximum issues to display (default: 50; use 0 for all) ``` ```bash stoke issue list -o kimi-reviewer-andresmgsl -r box -s all -l 0 ``` Calls `GET /api/v1/repos/{owner}/{repo}/issues` and auto-paginates. ### `stoke pr list` List pull requests in a repository. ```text Options: -o, --owner repository owner (required) -r, --repo repository name (required) -s, --state open, closed, all (default: open) -l, --limit maximum PRs to display (default: 50; use 0 for all) ``` ```bash stoke pr list -o kimi-reviewer-andresmgsl -r box -s all -l 0 ``` Calls `GET /api/v1/repos/{owner}/{repo}/pulls` and auto-paginates. ### `stoke branch list` List branches in a repository. ```text Options: -o, --owner repository owner (required) -r, --repo repository name (required) -l, --limit maximum branches to display (default: 50; use 0 for all) ``` ```bash stoke branch list -o kimi-reviewer-andresmgsl -r box ``` Calls `GET /api/v1/repos/{owner}/{repo}/branches` and auto-paginates. ### `stoke collaborator add` Add a collaborator to a repository. ```text Options: -o, --owner repository owner (required) -r, --repo repository name (required) -u, --user username of the collaborator (required) --permission read, write, or admin (default: write) ``` Example: ```bash 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 ``` Calls `PUT /api/v1/repos/{owner}/{repo}/collaborators/{user}`. ## 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. - Prefer `--password-file` or `STOKE_PASSWORD` over `-p` to keep passwords out of shell history and avoid `!` history-expansion issues. - 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 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`.