2026-07-22 15:03:32 +00:00
#!/usr/bin/env node
Audit: fix auth/config bugs, add issue/pr create, tests and docs
Fixes found during a full audit of the CLI:
- auth logout: remote token revocation always failed with 401 because
Forgejo only accepts Basic auth on the token endpoints. Logout now
asks for (or accepts) the account password, supports --password,
--password-file and --local-only, and clearly reports when the token
is left active.
- Silent password prompt actually echoed the password on a TTY:
overriding rl.write does not suppress readline echo. Switched to the
callback readline module and mute _writeToOutput instead (the
readline/promises interface does not honor that hook).
- Global --config flag was silently ignored: config paths were resolved
at require time, before the preAction hook set STOKE_CONFIG_FILE.
Paths are now resolved lazily on every access.
- XDG_CONFIG_HOME handling put the config in $XDG_CONFIG_HOME/.config/stoke;
per the XDG spec it now resolves to $XDG_CONFIG_HOME/stoke.
- repo create: --auto-init defaulted to true with no way to disable it;
added --no-auto-init.
- repo import/import-batch: a GitHub token was required even for
non-GitHub services (e.g. --service git), making those imports fail
without gh/GITHUB_TOKEN. Tokens are now only auto-resolved for the
github service; batch imports resolve per entry and memoize.
- Branding leftovers: 'Run: forgejo auth login' hint and
forgejo-cli/1.0.0 User-Agent now say stoke (UA tracks pkg.version).
- Added request timeouts (30s default, 10m for migrations).
- --limit and --team-id are validated as integers instead of silently
misbehaving on garbage (NaN made -l show all results).
New commands (per the repo's every-operation-becomes-a-command design):
- stoke issue create (title/body/body-file/assignees)
- stoke pr create (head/base/title/body/body-file)
Tests and metadata:
- New test suite on the built-in node:test runner (25 tests) covering
config resolution/persistence, the API client with a mocked fetch,
and end-to-end CLI behavior. npm test previously matched no files.
- package.json: engines >=22.12.0 (required by commander@15 — the
README claimed Node 18), repository, keywords, author; version 1.1.0.
- README: corrected Node requirement, documented repo rename (was
missing), issue create, pr create, logout options and revocation
caveat, --no-auto-init, XDG behavior, import token rules, testing.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-22 19:28:23 +00:00
const { Command , InvalidArgumentError } = require ( 'commander' ) ;
const readline = require ( 'node:readline' ) ;
2026-07-22 15:03:32 +00:00
const fs = require ( 'node:fs' ) ;
const { execSync } = require ( 'node:child_process' ) ;
const { stdin : input , stdout : output } = require ( 'node:process' ) ;
Audit: fix auth/config bugs, add issue/pr create, tests and docs
Fixes found during a full audit of the CLI:
- auth logout: remote token revocation always failed with 401 because
Forgejo only accepts Basic auth on the token endpoints. Logout now
asks for (or accepts) the account password, supports --password,
--password-file and --local-only, and clearly reports when the token
is left active.
- Silent password prompt actually echoed the password on a TTY:
overriding rl.write does not suppress readline echo. Switched to the
callback readline module and mute _writeToOutput instead (the
readline/promises interface does not honor that hook).
- Global --config flag was silently ignored: config paths were resolved
at require time, before the preAction hook set STOKE_CONFIG_FILE.
Paths are now resolved lazily on every access.
- XDG_CONFIG_HOME handling put the config in $XDG_CONFIG_HOME/.config/stoke;
per the XDG spec it now resolves to $XDG_CONFIG_HOME/stoke.
- repo create: --auto-init defaulted to true with no way to disable it;
added --no-auto-init.
- repo import/import-batch: a GitHub token was required even for
non-GitHub services (e.g. --service git), making those imports fail
without gh/GITHUB_TOKEN. Tokens are now only auto-resolved for the
github service; batch imports resolve per entry and memoize.
- Branding leftovers: 'Run: forgejo auth login' hint and
forgejo-cli/1.0.0 User-Agent now say stoke (UA tracks pkg.version).
- Added request timeouts (30s default, 10m for migrations).
- --limit and --team-id are validated as integers instead of silently
misbehaving on garbage (NaN made -l show all results).
New commands (per the repo's every-operation-becomes-a-command design):
- stoke issue create (title/body/body-file/assignees)
- stoke pr create (head/base/title/body/body-file)
Tests and metadata:
- New test suite on the built-in node:test runner (25 tests) covering
config resolution/persistence, the API client with a mocked fetch,
and end-to-end CLI behavior. npm test previously matched no files.
- package.json: engines >=22.12.0 (required by commander@15 — the
README claimed Node 18), repository, keywords, author; version 1.1.0.
- README: corrected Node requirement, documented repo rename (was
missing), issue create, pr create, logout options and revocation
caveat, --no-auto-init, XDG behavior, import token rules, testing.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-22 19:28:23 +00:00
const { loadConfig , saveConfig , clearConfig , getConfigPath } = require ( './config' ) ;
2026-07-22 15:03:32 +00:00
const { ForgejoClient } = require ( './api' ) ;
const pkg = require ( '../package.json' ) ;
const program = new Command ( ) ;
program
2026-07-22 15:16:42 +00:00
. name ( 'stoke' )
. description ( 'CLI for the heavy-duty forge (https://forgejo.heavyduty.builders)' )
2026-07-22 15:03:32 +00:00
. version ( pkg . version )
. configureOutput ( { outputError : ( str , write ) => write ( ` Error: ${ str } ` ) } ) ;
program
. option ( '-c, --config <path>' , 'path to configuration file' )
. hook ( 'preAction' , ( thisCommand ) => {
if ( thisCommand . opts ( ) . config ) {
2026-07-22 15:16:42 +00:00
process . env . STOKE _CONFIG _FILE = thisCommand . opts ( ) . config ;
2026-07-22 15:03:32 +00:00
}
} ) ;
Audit: fix auth/config bugs, add issue/pr create, tests and docs
Fixes found during a full audit of the CLI:
- auth logout: remote token revocation always failed with 401 because
Forgejo only accepts Basic auth on the token endpoints. Logout now
asks for (or accepts) the account password, supports --password,
--password-file and --local-only, and clearly reports when the token
is left active.
- Silent password prompt actually echoed the password on a TTY:
overriding rl.write does not suppress readline echo. Switched to the
callback readline module and mute _writeToOutput instead (the
readline/promises interface does not honor that hook).
- Global --config flag was silently ignored: config paths were resolved
at require time, before the preAction hook set STOKE_CONFIG_FILE.
Paths are now resolved lazily on every access.
- XDG_CONFIG_HOME handling put the config in $XDG_CONFIG_HOME/.config/stoke;
per the XDG spec it now resolves to $XDG_CONFIG_HOME/stoke.
- repo create: --auto-init defaulted to true with no way to disable it;
added --no-auto-init.
- repo import/import-batch: a GitHub token was required even for
non-GitHub services (e.g. --service git), making those imports fail
without gh/GITHUB_TOKEN. Tokens are now only auto-resolved for the
github service; batch imports resolve per entry and memoize.
- Branding leftovers: 'Run: forgejo auth login' hint and
forgejo-cli/1.0.0 User-Agent now say stoke (UA tracks pkg.version).
- Added request timeouts (30s default, 10m for migrations).
- --limit and --team-id are validated as integers instead of silently
misbehaving on garbage (NaN made -l show all results).
New commands (per the repo's every-operation-becomes-a-command design):
- stoke issue create (title/body/body-file/assignees)
- stoke pr create (head/base/title/body/body-file)
Tests and metadata:
- New test suite on the built-in node:test runner (25 tests) covering
config resolution/persistence, the API client with a mocked fetch,
and end-to-end CLI behavior. npm test previously matched no files.
- package.json: engines >=22.12.0 (required by commander@15 — the
README claimed Node 18), repository, keywords, author; version 1.1.0.
- README: corrected Node requirement, documented repo rename (was
missing), issue create, pr create, logout options and revocation
caveat, --no-auto-init, XDG behavior, import token rules, testing.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-22 19:28:23 +00:00
// Uses the callback readline module: it echoes keystrokes through the
// overridable _writeToOutput hook, which the readline/promises interface
// does not honor, so muting that hook is what actually keeps passwords
// off the terminal.
function prompt ( question , silent = false ) {
return new Promise ( ( resolve ) => {
const rl = readline . createInterface ( { input , output } ) ;
if ( silent ) {
rl . _writeToOutput = ( ) => { } ;
output . write ( question ) ;
rl . question ( '' , ( answer ) => {
output . write ( '\n' ) ;
rl . close ( ) ;
resolve ( answer ) ;
} ) ;
} else {
rl . question ( question , ( answer ) => {
rl . close ( ) ;
resolve ( answer ) ;
} ) ;
}
} ) ;
2026-07-22 15:03:32 +00:00
}
function readSecretFile ( filePath , label ) {
try {
return fs . readFileSync ( filePath , 'utf8' ) . replace ( /\r?\n$/ , '' ) ;
} catch ( err ) {
throw new Error ( ` Could not read ${ label } file ${ filePath } : ${ err . message } ` ) ;
}
}
Audit: fix auth/config bugs, add issue/pr create, tests and docs
Fixes found during a full audit of the CLI:
- auth logout: remote token revocation always failed with 401 because
Forgejo only accepts Basic auth on the token endpoints. Logout now
asks for (or accepts) the account password, supports --password,
--password-file and --local-only, and clearly reports when the token
is left active.
- Silent password prompt actually echoed the password on a TTY:
overriding rl.write does not suppress readline echo. Switched to the
callback readline module and mute _writeToOutput instead (the
readline/promises interface does not honor that hook).
- Global --config flag was silently ignored: config paths were resolved
at require time, before the preAction hook set STOKE_CONFIG_FILE.
Paths are now resolved lazily on every access.
- XDG_CONFIG_HOME handling put the config in $XDG_CONFIG_HOME/.config/stoke;
per the XDG spec it now resolves to $XDG_CONFIG_HOME/stoke.
- repo create: --auto-init defaulted to true with no way to disable it;
added --no-auto-init.
- repo import/import-batch: a GitHub token was required even for
non-GitHub services (e.g. --service git), making those imports fail
without gh/GITHUB_TOKEN. Tokens are now only auto-resolved for the
github service; batch imports resolve per entry and memoize.
- Branding leftovers: 'Run: forgejo auth login' hint and
forgejo-cli/1.0.0 User-Agent now say stoke (UA tracks pkg.version).
- Added request timeouts (30s default, 10m for migrations).
- --limit and --team-id are validated as integers instead of silently
misbehaving on garbage (NaN made -l show all results).
New commands (per the repo's every-operation-becomes-a-command design):
- stoke issue create (title/body/body-file/assignees)
- stoke pr create (head/base/title/body/body-file)
Tests and metadata:
- New test suite on the built-in node:test runner (25 tests) covering
config resolution/persistence, the API client with a mocked fetch,
and end-to-end CLI behavior. npm test previously matched no files.
- package.json: engines >=22.12.0 (required by commander@15 — the
README claimed Node 18), repository, keywords, author; version 1.1.0.
- README: corrected Node requirement, documented repo rename (was
missing), issue create, pr create, logout options and revocation
caveat, --no-auto-init, XDG behavior, import token rules, testing.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-22 19:28:23 +00:00
function readBodyOption ( options ) {
if ( options . bodyFile ) {
try {
return fs . readFileSync ( options . bodyFile , 'utf8' ) ;
} catch ( err ) {
throw new Error ( ` Could not read body file ${ options . bodyFile } : ${ err . message } ` ) ;
}
}
return options . body ;
}
function parseLimit ( value ) {
const n = Number ( value ) ;
if ( ! Number . isInteger ( n ) || n < 0 ) {
throw new InvalidArgumentError ( 'Limit must be a non-negative integer (0 means all).' ) ;
}
return n ;
}
function parseId ( value ) {
const n = Number ( value ) ;
if ( ! Number . isInteger ( n ) || n <= 0 ) {
throw new InvalidArgumentError ( 'Id must be a positive integer.' ) ;
}
return n ;
}
2026-07-22 15:03:32 +00:00
function makeTokenName ( ) {
const host = require ( 'node:os' ) . hostname ( ) || 'unknown' ;
2026-07-22 15:16:42 +00:00
return ` stoke- ${ host } - ${ Date . now ( ) } ` ;
2026-07-22 15:03:32 +00:00
}
const DEFAULT _TOKEN _SCOPES = [
'read:activitypub' , 'write:activitypub' ,
'read:issue' , 'write:issue' ,
'read:misc' , 'write:misc' ,
'read:organization' , 'write:organization' ,
'read:package' , 'write:package' ,
'read:repository' , 'write:repository' ,
'read:user' , 'write:user' ,
] ;
function printErrorAndExit ( err ) {
console . error ( ` Authentication failed: ${ err . message } ` ) ;
if ( err . status ) {
console . error ( ` HTTP status: ${ err . status } ` ) ;
}
if ( err . body && err . body . url ) {
console . error ( ` URL: ${ err . body . url } ` ) ;
}
process . exit ( 1 ) ;
}
const auth = program
. command ( 'auth' )
. description ( 'Manage Forgejo authentication' ) ;
auth
. command ( 'login' )
. description ( 'Authenticate against a Forgejo instance and store an access token' )
2026-07-22 15:16:42 +00:00
. option ( '-u, --url <url>' , 'Forgejo base URL' , process . env . STOKE _URL || process . env . FORGEJO _URL || 'https://forgejo.heavyduty.builders' )
. option ( '-n, --username <username>' , 'account username or email' , process . env . STOKE _USERNAME || process . env . FORGEJO _USERNAME )
. option ( '-p, --password <password>' , 'account password' , process . env . STOKE _PASSWORD || process . env . FORGEJO _PASSWORD )
2026-07-22 15:03:32 +00:00
. option ( '--password-file <path>' , 'read account password from a file' )
. option ( '-t, --token <token>' , 'use an existing personal access token instead of generating one' )
. option ( '--token-file <path>' , 'read an existing personal access token from a file' )
. option ( '--token-name <name>' , 'name for the generated personal access token' , makeTokenName ( ) )
. action ( async ( options ) => {
try {
let { url , username , password , passwordFile , token , tokenFile , tokenName } = options ;
if ( tokenFile ) token = readSecretFile ( tokenFile , 'token' ) ;
if ( passwordFile ) password = readSecretFile ( passwordFile , 'password' ) ;
if ( ! username && ! token ) {
username = await prompt ( 'Username or email: ' ) ;
}
if ( ! password && ! token ) {
password = await prompt ( 'Password: ' , true ) ;
}
const client = new ForgejoClient ( url ) ;
let config = { url } ;
if ( token ) {
// Validate the supplied token and resolve the login name.
const tokenClient = new ForgejoClient ( url , token ) ;
const me = await tokenClient . get ( '/user' ) ;
config = {
url ,
login : me . login ,
username : me . username || me . login ,
email : me . email ,
token ,
tokenId : null ,
} ;
console . log ( ` Authenticated as ${ me . login } using provided token. ` ) ;
} else {
// Verify username/password and get the canonical login name.
const me = await client . verifyBasicAuth ( username , password ) ;
const login = me . login ;
const tokenRes = await client . createToken ( login , password , tokenName , DEFAULT _TOKEN _SCOPES ) ;
if ( ! tokenRes . sha1 ) {
throw new Error ( 'Token generation succeeded but no token value was returned.' ) ;
}
config = {
url ,
login ,
username : me . username || login ,
email : me . email ,
token : tokenRes . sha1 ,
tokenId : tokenRes . id ,
} ;
console . log ( ` Authenticated as ${ login } . Token " ${ tokenRes . name } " created. ` ) ;
}
saveConfig ( config ) ;
Audit: fix auth/config bugs, add issue/pr create, tests and docs
Fixes found during a full audit of the CLI:
- auth logout: remote token revocation always failed with 401 because
Forgejo only accepts Basic auth on the token endpoints. Logout now
asks for (or accepts) the account password, supports --password,
--password-file and --local-only, and clearly reports when the token
is left active.
- Silent password prompt actually echoed the password on a TTY:
overriding rl.write does not suppress readline echo. Switched to the
callback readline module and mute _writeToOutput instead (the
readline/promises interface does not honor that hook).
- Global --config flag was silently ignored: config paths were resolved
at require time, before the preAction hook set STOKE_CONFIG_FILE.
Paths are now resolved lazily on every access.
- XDG_CONFIG_HOME handling put the config in $XDG_CONFIG_HOME/.config/stoke;
per the XDG spec it now resolves to $XDG_CONFIG_HOME/stoke.
- repo create: --auto-init defaulted to true with no way to disable it;
added --no-auto-init.
- repo import/import-batch: a GitHub token was required even for
non-GitHub services (e.g. --service git), making those imports fail
without gh/GITHUB_TOKEN. Tokens are now only auto-resolved for the
github service; batch imports resolve per entry and memoize.
- Branding leftovers: 'Run: forgejo auth login' hint and
forgejo-cli/1.0.0 User-Agent now say stoke (UA tracks pkg.version).
- Added request timeouts (30s default, 10m for migrations).
- --limit and --team-id are validated as integers instead of silently
misbehaving on garbage (NaN made -l show all results).
New commands (per the repo's every-operation-becomes-a-command design):
- stoke issue create (title/body/body-file/assignees)
- stoke pr create (head/base/title/body/body-file)
Tests and metadata:
- New test suite on the built-in node:test runner (25 tests) covering
config resolution/persistence, the API client with a mocked fetch,
and end-to-end CLI behavior. npm test previously matched no files.
- package.json: engines >=22.12.0 (required by commander@15 — the
README claimed Node 18), repository, keywords, author; version 1.1.0.
- README: corrected Node requirement, documented repo rename (was
missing), issue create, pr create, logout options and revocation
caveat, --no-auto-init, XDG behavior, import token rules, testing.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-22 19:28:23 +00:00
console . log ( ` Credentials stored in ${ getConfigPath ( ) } ` ) ;
2026-07-22 15:03:32 +00:00
} catch ( err ) {
printErrorAndExit ( err ) ;
}
} ) ;
auth
. command ( 'logout' )
. description ( 'Revoke the stored access token and remove local configuration' )
Audit: fix auth/config bugs, add issue/pr create, tests and docs
Fixes found during a full audit of the CLI:
- auth logout: remote token revocation always failed with 401 because
Forgejo only accepts Basic auth on the token endpoints. Logout now
asks for (or accepts) the account password, supports --password,
--password-file and --local-only, and clearly reports when the token
is left active.
- Silent password prompt actually echoed the password on a TTY:
overriding rl.write does not suppress readline echo. Switched to the
callback readline module and mute _writeToOutput instead (the
readline/promises interface does not honor that hook).
- Global --config flag was silently ignored: config paths were resolved
at require time, before the preAction hook set STOKE_CONFIG_FILE.
Paths are now resolved lazily on every access.
- XDG_CONFIG_HOME handling put the config in $XDG_CONFIG_HOME/.config/stoke;
per the XDG spec it now resolves to $XDG_CONFIG_HOME/stoke.
- repo create: --auto-init defaulted to true with no way to disable it;
added --no-auto-init.
- repo import/import-batch: a GitHub token was required even for
non-GitHub services (e.g. --service git), making those imports fail
without gh/GITHUB_TOKEN. Tokens are now only auto-resolved for the
github service; batch imports resolve per entry and memoize.
- Branding leftovers: 'Run: forgejo auth login' hint and
forgejo-cli/1.0.0 User-Agent now say stoke (UA tracks pkg.version).
- Added request timeouts (30s default, 10m for migrations).
- --limit and --team-id are validated as integers instead of silently
misbehaving on garbage (NaN made -l show all results).
New commands (per the repo's every-operation-becomes-a-command design):
- stoke issue create (title/body/body-file/assignees)
- stoke pr create (head/base/title/body/body-file)
Tests and metadata:
- New test suite on the built-in node:test runner (25 tests) covering
config resolution/persistence, the API client with a mocked fetch,
and end-to-end CLI behavior. npm test previously matched no files.
- package.json: engines >=22.12.0 (required by commander@15 — the
README claimed Node 18), repository, keywords, author; version 1.1.0.
- README: corrected Node requirement, documented repo rename (was
missing), issue create, pr create, logout options and revocation
caveat, --no-auto-init, XDG behavior, import token rules, testing.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-22 19:28:23 +00:00
. option ( '-p, --password <password>' , 'account password, needed to revoke the token remotely' , process . env . STOKE _PASSWORD || process . env . FORGEJO _PASSWORD )
. option ( '--password-file <path>' , 'read account password from a file' )
. option ( '--local-only' , 'skip remote token revocation and only delete the local config' , false )
. action ( async ( options ) => {
2026-07-22 15:03:32 +00:00
try {
const config = loadConfig ( ) ;
if ( ! config || ! config . token ) {
console . log ( 'No active session.' ) ;
return ;
}
Audit: fix auth/config bugs, add issue/pr create, tests and docs
Fixes found during a full audit of the CLI:
- auth logout: remote token revocation always failed with 401 because
Forgejo only accepts Basic auth on the token endpoints. Logout now
asks for (or accepts) the account password, supports --password,
--password-file and --local-only, and clearly reports when the token
is left active.
- Silent password prompt actually echoed the password on a TTY:
overriding rl.write does not suppress readline echo. Switched to the
callback readline module and mute _writeToOutput instead (the
readline/promises interface does not honor that hook).
- Global --config flag was silently ignored: config paths were resolved
at require time, before the preAction hook set STOKE_CONFIG_FILE.
Paths are now resolved lazily on every access.
- XDG_CONFIG_HOME handling put the config in $XDG_CONFIG_HOME/.config/stoke;
per the XDG spec it now resolves to $XDG_CONFIG_HOME/stoke.
- repo create: --auto-init defaulted to true with no way to disable it;
added --no-auto-init.
- repo import/import-batch: a GitHub token was required even for
non-GitHub services (e.g. --service git), making those imports fail
without gh/GITHUB_TOKEN. Tokens are now only auto-resolved for the
github service; batch imports resolve per entry and memoize.
- Branding leftovers: 'Run: forgejo auth login' hint and
forgejo-cli/1.0.0 User-Agent now say stoke (UA tracks pkg.version).
- Added request timeouts (30s default, 10m for migrations).
- --limit and --team-id are validated as integers instead of silently
misbehaving on garbage (NaN made -l show all results).
New commands (per the repo's every-operation-becomes-a-command design):
- stoke issue create (title/body/body-file/assignees)
- stoke pr create (head/base/title/body/body-file)
Tests and metadata:
- New test suite on the built-in node:test runner (25 tests) covering
config resolution/persistence, the API client with a mocked fetch,
and end-to-end CLI behavior. npm test previously matched no files.
- package.json: engines >=22.12.0 (required by commander@15 — the
README claimed Node 18), repository, keywords, author; version 1.1.0.
- README: corrected Node requirement, documented repo rename (was
missing), issue create, pr create, logout options and revocation
caveat, --no-auto-init, XDG behavior, import token rules, testing.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-22 19:28:23 +00:00
// Forgejo only accepts Basic auth on the token endpoints, so remote
// revocation needs the account password (a token cannot revoke itself).
if ( config . tokenId && ! options . localOnly ) {
let password = options . password ;
if ( options . passwordFile ) password = readSecretFile ( options . passwordFile , 'password' ) ;
if ( ! password && input . isTTY ) {
password = await prompt ( ` Password for ${ config . login } (empty to skip revocation): ` , true ) ;
}
if ( password ) {
const client = new ForgejoClient ( config . url ) ;
try {
await client . deleteToken ( config . username || config . login , password , config . login , config . tokenId ) ;
console . log ( ` Revoked token ${ config . tokenId } on ${ config . url } . ` ) ;
} catch ( err ) {
console . error ( ` Warning: could not revoke remote token: ${ err . message } ` ) ;
}
} else {
console . log ( ` Skipping remote revocation (no password provided). Token ${ config . tokenId } stays active on ${ config . url } ; revoke it from the web UI under Settings > Applications. ` ) ;
2026-07-22 15:03:32 +00:00
}
}
clearConfig ( ) ;
console . log ( 'Local credentials removed.' ) ;
} catch ( err ) {
console . error ( ` Logout failed: ${ err . message } ` ) ;
process . exit ( 1 ) ;
}
} ) ;
auth
. command ( 'status' )
. description ( 'Show the current authentication status' )
. action ( async ( ) => {
try {
const config = loadConfig ( ) ;
if ( ! config || ! config . token ) {
console . log ( 'Not authenticated.' ) ;
return ;
}
const client = ForgejoClient . fromConfig ( config ) ;
const me = await client . get ( '/user' ) ;
console . log ( 'Instance: ' , config . url ) ;
console . log ( 'Login: ' , me . login ) ;
console . log ( 'Username: ' , me . username ) ;
console . log ( 'Email: ' , me . email ) ;
Audit: fix auth/config bugs, add issue/pr create, tests and docs
Fixes found during a full audit of the CLI:
- auth logout: remote token revocation always failed with 401 because
Forgejo only accepts Basic auth on the token endpoints. Logout now
asks for (or accepts) the account password, supports --password,
--password-file and --local-only, and clearly reports when the token
is left active.
- Silent password prompt actually echoed the password on a TTY:
overriding rl.write does not suppress readline echo. Switched to the
callback readline module and mute _writeToOutput instead (the
readline/promises interface does not honor that hook).
- Global --config flag was silently ignored: config paths were resolved
at require time, before the preAction hook set STOKE_CONFIG_FILE.
Paths are now resolved lazily on every access.
- XDG_CONFIG_HOME handling put the config in $XDG_CONFIG_HOME/.config/stoke;
per the XDG spec it now resolves to $XDG_CONFIG_HOME/stoke.
- repo create: --auto-init defaulted to true with no way to disable it;
added --no-auto-init.
- repo import/import-batch: a GitHub token was required even for
non-GitHub services (e.g. --service git), making those imports fail
without gh/GITHUB_TOKEN. Tokens are now only auto-resolved for the
github service; batch imports resolve per entry and memoize.
- Branding leftovers: 'Run: forgejo auth login' hint and
forgejo-cli/1.0.0 User-Agent now say stoke (UA tracks pkg.version).
- Added request timeouts (30s default, 10m for migrations).
- --limit and --team-id are validated as integers instead of silently
misbehaving on garbage (NaN made -l show all results).
New commands (per the repo's every-operation-becomes-a-command design):
- stoke issue create (title/body/body-file/assignees)
- stoke pr create (head/base/title/body/body-file)
Tests and metadata:
- New test suite on the built-in node:test runner (25 tests) covering
config resolution/persistence, the API client with a mocked fetch,
and end-to-end CLI behavior. npm test previously matched no files.
- package.json: engines >=22.12.0 (required by commander@15 — the
README claimed Node 18), repository, keywords, author; version 1.1.0.
- README: corrected Node requirement, documented repo rename (was
missing), issue create, pr create, logout options and revocation
caveat, --no-auto-init, XDG behavior, import token rules, testing.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-22 19:28:23 +00:00
console . log ( 'Token path: ' , getConfigPath ( ) ) ;
2026-07-22 15:03:32 +00:00
} catch ( err ) {
console . error ( ` Status check failed: ${ err . message } ` ) ;
process . exit ( 1 ) ;
}
} ) ;
Audit: fix auth/config bugs, add issue/pr create, tests and docs
Fixes found during a full audit of the CLI:
- auth logout: remote token revocation always failed with 401 because
Forgejo only accepts Basic auth on the token endpoints. Logout now
asks for (or accepts) the account password, supports --password,
--password-file and --local-only, and clearly reports when the token
is left active.
- Silent password prompt actually echoed the password on a TTY:
overriding rl.write does not suppress readline echo. Switched to the
callback readline module and mute _writeToOutput instead (the
readline/promises interface does not honor that hook).
- Global --config flag was silently ignored: config paths were resolved
at require time, before the preAction hook set STOKE_CONFIG_FILE.
Paths are now resolved lazily on every access.
- XDG_CONFIG_HOME handling put the config in $XDG_CONFIG_HOME/.config/stoke;
per the XDG spec it now resolves to $XDG_CONFIG_HOME/stoke.
- repo create: --auto-init defaulted to true with no way to disable it;
added --no-auto-init.
- repo import/import-batch: a GitHub token was required even for
non-GitHub services (e.g. --service git), making those imports fail
without gh/GITHUB_TOKEN. Tokens are now only auto-resolved for the
github service; batch imports resolve per entry and memoize.
- Branding leftovers: 'Run: forgejo auth login' hint and
forgejo-cli/1.0.0 User-Agent now say stoke (UA tracks pkg.version).
- Added request timeouts (30s default, 10m for migrations).
- --limit and --team-id are validated as integers instead of silently
misbehaving on garbage (NaN made -l show all results).
New commands (per the repo's every-operation-becomes-a-command design):
- stoke issue create (title/body/body-file/assignees)
- stoke pr create (head/base/title/body/body-file)
Tests and metadata:
- New test suite on the built-in node:test runner (25 tests) covering
config resolution/persistence, the API client with a mocked fetch,
and end-to-end CLI behavior. npm test previously matched no files.
- package.json: engines >=22.12.0 (required by commander@15 — the
README claimed Node 18), repository, keywords, author; version 1.1.0.
- README: corrected Node requirement, documented repo rename (was
missing), issue create, pr create, logout options and revocation
caveat, --no-auto-init, XDG behavior, import token rules, testing.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-22 19:28:23 +00:00
// Resolve the auth token to send to the source service of a migration.
// GitHub needs one in practice (rate limits, private repos); for any other
// service a token is optional and only used when explicitly provided.
let cachedGhToken ;
function resolveSourceToken ( tokenOption , service ) {
2026-07-22 15:03:32 +00:00
if ( tokenOption ) return tokenOption ;
Audit: fix auth/config bugs, add issue/pr create, tests and docs
Fixes found during a full audit of the CLI:
- auth logout: remote token revocation always failed with 401 because
Forgejo only accepts Basic auth on the token endpoints. Logout now
asks for (or accepts) the account password, supports --password,
--password-file and --local-only, and clearly reports when the token
is left active.
- Silent password prompt actually echoed the password on a TTY:
overriding rl.write does not suppress readline echo. Switched to the
callback readline module and mute _writeToOutput instead (the
readline/promises interface does not honor that hook).
- Global --config flag was silently ignored: config paths were resolved
at require time, before the preAction hook set STOKE_CONFIG_FILE.
Paths are now resolved lazily on every access.
- XDG_CONFIG_HOME handling put the config in $XDG_CONFIG_HOME/.config/stoke;
per the XDG spec it now resolves to $XDG_CONFIG_HOME/stoke.
- repo create: --auto-init defaulted to true with no way to disable it;
added --no-auto-init.
- repo import/import-batch: a GitHub token was required even for
non-GitHub services (e.g. --service git), making those imports fail
without gh/GITHUB_TOKEN. Tokens are now only auto-resolved for the
github service; batch imports resolve per entry and memoize.
- Branding leftovers: 'Run: forgejo auth login' hint and
forgejo-cli/1.0.0 User-Agent now say stoke (UA tracks pkg.version).
- Added request timeouts (30s default, 10m for migrations).
- --limit and --team-id are validated as integers instead of silently
misbehaving on garbage (NaN made -l show all results).
New commands (per the repo's every-operation-becomes-a-command design):
- stoke issue create (title/body/body-file/assignees)
- stoke pr create (head/base/title/body/body-file)
Tests and metadata:
- New test suite on the built-in node:test runner (25 tests) covering
config resolution/persistence, the API client with a mocked fetch,
and end-to-end CLI behavior. npm test previously matched no files.
- package.json: engines >=22.12.0 (required by commander@15 — the
README claimed Node 18), repository, keywords, author; version 1.1.0.
- README: corrected Node requirement, documented repo rename (was
missing), issue create, pr create, logout options and revocation
caveat, --no-auto-init, XDG behavior, import token rules, testing.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-22 19:28:23 +00:00
if ( service !== 'github' ) return undefined ;
2026-07-22 15:03:32 +00:00
if ( process . env . GITHUB _TOKEN ) return process . env . GITHUB _TOKEN ;
Audit: fix auth/config bugs, add issue/pr create, tests and docs
Fixes found during a full audit of the CLI:
- auth logout: remote token revocation always failed with 401 because
Forgejo only accepts Basic auth on the token endpoints. Logout now
asks for (or accepts) the account password, supports --password,
--password-file and --local-only, and clearly reports when the token
is left active.
- Silent password prompt actually echoed the password on a TTY:
overriding rl.write does not suppress readline echo. Switched to the
callback readline module and mute _writeToOutput instead (the
readline/promises interface does not honor that hook).
- Global --config flag was silently ignored: config paths were resolved
at require time, before the preAction hook set STOKE_CONFIG_FILE.
Paths are now resolved lazily on every access.
- XDG_CONFIG_HOME handling put the config in $XDG_CONFIG_HOME/.config/stoke;
per the XDG spec it now resolves to $XDG_CONFIG_HOME/stoke.
- repo create: --auto-init defaulted to true with no way to disable it;
added --no-auto-init.
- repo import/import-batch: a GitHub token was required even for
non-GitHub services (e.g. --service git), making those imports fail
without gh/GITHUB_TOKEN. Tokens are now only auto-resolved for the
github service; batch imports resolve per entry and memoize.
- Branding leftovers: 'Run: forgejo auth login' hint and
forgejo-cli/1.0.0 User-Agent now say stoke (UA tracks pkg.version).
- Added request timeouts (30s default, 10m for migrations).
- --limit and --team-id are validated as integers instead of silently
misbehaving on garbage (NaN made -l show all results).
New commands (per the repo's every-operation-becomes-a-command design):
- stoke issue create (title/body/body-file/assignees)
- stoke pr create (head/base/title/body/body-file)
Tests and metadata:
- New test suite on the built-in node:test runner (25 tests) covering
config resolution/persistence, the API client with a mocked fetch,
and end-to-end CLI behavior. npm test previously matched no files.
- package.json: engines >=22.12.0 (required by commander@15 — the
README claimed Node 18), repository, keywords, author; version 1.1.0.
- README: corrected Node requirement, documented repo rename (was
missing), issue create, pr create, logout options and revocation
caveat, --no-auto-init, XDG behavior, import token rules, testing.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-22 19:28:23 +00:00
if ( cachedGhToken ) return cachedGhToken ;
2026-07-22 15:03:32 +00:00
try {
Audit: fix auth/config bugs, add issue/pr create, tests and docs
Fixes found during a full audit of the CLI:
- auth logout: remote token revocation always failed with 401 because
Forgejo only accepts Basic auth on the token endpoints. Logout now
asks for (or accepts) the account password, supports --password,
--password-file and --local-only, and clearly reports when the token
is left active.
- Silent password prompt actually echoed the password on a TTY:
overriding rl.write does not suppress readline echo. Switched to the
callback readline module and mute _writeToOutput instead (the
readline/promises interface does not honor that hook).
- Global --config flag was silently ignored: config paths were resolved
at require time, before the preAction hook set STOKE_CONFIG_FILE.
Paths are now resolved lazily on every access.
- XDG_CONFIG_HOME handling put the config in $XDG_CONFIG_HOME/.config/stoke;
per the XDG spec it now resolves to $XDG_CONFIG_HOME/stoke.
- repo create: --auto-init defaulted to true with no way to disable it;
added --no-auto-init.
- repo import/import-batch: a GitHub token was required even for
non-GitHub services (e.g. --service git), making those imports fail
without gh/GITHUB_TOKEN. Tokens are now only auto-resolved for the
github service; batch imports resolve per entry and memoize.
- Branding leftovers: 'Run: forgejo auth login' hint and
forgejo-cli/1.0.0 User-Agent now say stoke (UA tracks pkg.version).
- Added request timeouts (30s default, 10m for migrations).
- --limit and --team-id are validated as integers instead of silently
misbehaving on garbage (NaN made -l show all results).
New commands (per the repo's every-operation-becomes-a-command design):
- stoke issue create (title/body/body-file/assignees)
- stoke pr create (head/base/title/body/body-file)
Tests and metadata:
- New test suite on the built-in node:test runner (25 tests) covering
config resolution/persistence, the API client with a mocked fetch,
and end-to-end CLI behavior. npm test previously matched no files.
- package.json: engines >=22.12.0 (required by commander@15 — the
README claimed Node 18), repository, keywords, author; version 1.1.0.
- README: corrected Node requirement, documented repo rename (was
missing), issue create, pr create, logout options and revocation
caveat, --no-auto-init, XDG behavior, import token rules, testing.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-22 19:28:23 +00:00
cachedGhToken = execSync ( 'gh auth token' , { encoding : 'utf8' , timeout : 10000 } ) . trim ( ) ;
return cachedGhToken ;
2026-07-22 15:03:32 +00:00
} catch {
Audit: fix auth/config bugs, add issue/pr create, tests and docs
Fixes found during a full audit of the CLI:
- auth logout: remote token revocation always failed with 401 because
Forgejo only accepts Basic auth on the token endpoints. Logout now
asks for (or accepts) the account password, supports --password,
--password-file and --local-only, and clearly reports when the token
is left active.
- Silent password prompt actually echoed the password on a TTY:
overriding rl.write does not suppress readline echo. Switched to the
callback readline module and mute _writeToOutput instead (the
readline/promises interface does not honor that hook).
- Global --config flag was silently ignored: config paths were resolved
at require time, before the preAction hook set STOKE_CONFIG_FILE.
Paths are now resolved lazily on every access.
- XDG_CONFIG_HOME handling put the config in $XDG_CONFIG_HOME/.config/stoke;
per the XDG spec it now resolves to $XDG_CONFIG_HOME/stoke.
- repo create: --auto-init defaulted to true with no way to disable it;
added --no-auto-init.
- repo import/import-batch: a GitHub token was required even for
non-GitHub services (e.g. --service git), making those imports fail
without gh/GITHUB_TOKEN. Tokens are now only auto-resolved for the
github service; batch imports resolve per entry and memoize.
- Branding leftovers: 'Run: forgejo auth login' hint and
forgejo-cli/1.0.0 User-Agent now say stoke (UA tracks pkg.version).
- Added request timeouts (30s default, 10m for migrations).
- --limit and --team-id are validated as integers instead of silently
misbehaving on garbage (NaN made -l show all results).
New commands (per the repo's every-operation-becomes-a-command design):
- stoke issue create (title/body/body-file/assignees)
- stoke pr create (head/base/title/body/body-file)
Tests and metadata:
- New test suite on the built-in node:test runner (25 tests) covering
config resolution/persistence, the API client with a mocked fetch,
and end-to-end CLI behavior. npm test previously matched no files.
- package.json: engines >=22.12.0 (required by commander@15 — the
README claimed Node 18), repository, keywords, author; version 1.1.0.
- README: corrected Node requirement, documented repo rename (was
missing), issue create, pr create, logout options and revocation
caveat, --no-auto-init, XDG behavior, import token rules, testing.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-22 19:28:23 +00:00
throw new Error ( "No GitHub token found. Set --github-token, GITHUB_TOKEN, or ensure 'gh auth token' works." ) ;
2026-07-22 15:03:32 +00:00
}
}
function normalizeBool ( value , defaultValue ) {
return value === undefined ? defaultValue : Boolean ( value ) ;
}
const repo = program
. command ( 'repo' )
. description ( 'Manage repositories' ) ;
repo
. command ( 'list' )
. description ( 'List repositories for the authenticated user' )
Audit: fix auth/config bugs, add issue/pr create, tests and docs
Fixes found during a full audit of the CLI:
- auth logout: remote token revocation always failed with 401 because
Forgejo only accepts Basic auth on the token endpoints. Logout now
asks for (or accepts) the account password, supports --password,
--password-file and --local-only, and clearly reports when the token
is left active.
- Silent password prompt actually echoed the password on a TTY:
overriding rl.write does not suppress readline echo. Switched to the
callback readline module and mute _writeToOutput instead (the
readline/promises interface does not honor that hook).
- Global --config flag was silently ignored: config paths were resolved
at require time, before the preAction hook set STOKE_CONFIG_FILE.
Paths are now resolved lazily on every access.
- XDG_CONFIG_HOME handling put the config in $XDG_CONFIG_HOME/.config/stoke;
per the XDG spec it now resolves to $XDG_CONFIG_HOME/stoke.
- repo create: --auto-init defaulted to true with no way to disable it;
added --no-auto-init.
- repo import/import-batch: a GitHub token was required even for
non-GitHub services (e.g. --service git), making those imports fail
without gh/GITHUB_TOKEN. Tokens are now only auto-resolved for the
github service; batch imports resolve per entry and memoize.
- Branding leftovers: 'Run: forgejo auth login' hint and
forgejo-cli/1.0.0 User-Agent now say stoke (UA tracks pkg.version).
- Added request timeouts (30s default, 10m for migrations).
- --limit and --team-id are validated as integers instead of silently
misbehaving on garbage (NaN made -l show all results).
New commands (per the repo's every-operation-becomes-a-command design):
- stoke issue create (title/body/body-file/assignees)
- stoke pr create (head/base/title/body/body-file)
Tests and metadata:
- New test suite on the built-in node:test runner (25 tests) covering
config resolution/persistence, the API client with a mocked fetch,
and end-to-end CLI behavior. npm test previously matched no files.
- package.json: engines >=22.12.0 (required by commander@15 — the
README claimed Node 18), repository, keywords, author; version 1.1.0.
- README: corrected Node requirement, documented repo rename (was
missing), issue create, pr create, logout options and revocation
caveat, --no-auto-init, XDG behavior, import token rules, testing.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-22 19:28:23 +00:00
. option ( '-l, --limit <number>' , 'maximum repositories to return (0 for all)' , parseLimit , 50 )
2026-07-22 15:03:32 +00:00
. action ( async ( options ) => {
try {
const config = loadConfig ( ) ;
const client = ForgejoClient . fromConfig ( config ) ;
const repos = await client . listRepos ( ) ;
Audit: fix auth/config bugs, add issue/pr create, tests and docs
Fixes found during a full audit of the CLI:
- auth logout: remote token revocation always failed with 401 because
Forgejo only accepts Basic auth on the token endpoints. Logout now
asks for (or accepts) the account password, supports --password,
--password-file and --local-only, and clearly reports when the token
is left active.
- Silent password prompt actually echoed the password on a TTY:
overriding rl.write does not suppress readline echo. Switched to the
callback readline module and mute _writeToOutput instead (the
readline/promises interface does not honor that hook).
- Global --config flag was silently ignored: config paths were resolved
at require time, before the preAction hook set STOKE_CONFIG_FILE.
Paths are now resolved lazily on every access.
- XDG_CONFIG_HOME handling put the config in $XDG_CONFIG_HOME/.config/stoke;
per the XDG spec it now resolves to $XDG_CONFIG_HOME/stoke.
- repo create: --auto-init defaulted to true with no way to disable it;
added --no-auto-init.
- repo import/import-batch: a GitHub token was required even for
non-GitHub services (e.g. --service git), making those imports fail
without gh/GITHUB_TOKEN. Tokens are now only auto-resolved for the
github service; batch imports resolve per entry and memoize.
- Branding leftovers: 'Run: forgejo auth login' hint and
forgejo-cli/1.0.0 User-Agent now say stoke (UA tracks pkg.version).
- Added request timeouts (30s default, 10m for migrations).
- --limit and --team-id are validated as integers instead of silently
misbehaving on garbage (NaN made -l show all results).
New commands (per the repo's every-operation-becomes-a-command design):
- stoke issue create (title/body/body-file/assignees)
- stoke pr create (head/base/title/body/body-file)
Tests and metadata:
- New test suite on the built-in node:test runner (25 tests) covering
config resolution/persistence, the API client with a mocked fetch,
and end-to-end CLI behavior. npm test previously matched no files.
- package.json: engines >=22.12.0 (required by commander@15 — the
README claimed Node 18), repository, keywords, author; version 1.1.0.
- README: corrected Node requirement, documented repo rename (was
missing), issue create, pr create, logout options and revocation
caveat, --no-auto-init, XDG behavior, import token rules, testing.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-22 19:28:23 +00:00
const display = options . limit > 0 ? repos . slice ( 0 , options . limit ) : repos ;
2026-07-22 15:03:32 +00:00
if ( ! display . length ) {
console . log ( 'No repositories found.' ) ;
return ;
}
for ( const r of display ) {
const vis = r . private ? 'private' : 'public' ;
console . log ( ` ${ r . full _name } [ ${ vis } ] ${ r . html _url } ` ) ;
}
if ( repos . length > display . length ) {
console . log ( ` ...and ${ repos . length - display . length } more (use -l 0 for all). ` ) ;
}
} catch ( err ) {
console . error ( ` Failed to list repositories: ${ err . message } ` ) ;
process . exit ( 1 ) ;
}
} ) ;
repo
. command ( 'create' )
. description ( 'Create a new repository for the authenticated user' )
. requiredOption ( '--name <name>' , 'repository name' )
. option ( '-d, --description <description>' , 'repository description' , '' )
. option ( '--private' , 'make the repository private' , false )
. option ( '--public' , 'make the repository public' )
Audit: fix auth/config bugs, add issue/pr create, tests and docs
Fixes found during a full audit of the CLI:
- auth logout: remote token revocation always failed with 401 because
Forgejo only accepts Basic auth on the token endpoints. Logout now
asks for (or accepts) the account password, supports --password,
--password-file and --local-only, and clearly reports when the token
is left active.
- Silent password prompt actually echoed the password on a TTY:
overriding rl.write does not suppress readline echo. Switched to the
callback readline module and mute _writeToOutput instead (the
readline/promises interface does not honor that hook).
- Global --config flag was silently ignored: config paths were resolved
at require time, before the preAction hook set STOKE_CONFIG_FILE.
Paths are now resolved lazily on every access.
- XDG_CONFIG_HOME handling put the config in $XDG_CONFIG_HOME/.config/stoke;
per the XDG spec it now resolves to $XDG_CONFIG_HOME/stoke.
- repo create: --auto-init defaulted to true with no way to disable it;
added --no-auto-init.
- repo import/import-batch: a GitHub token was required even for
non-GitHub services (e.g. --service git), making those imports fail
without gh/GITHUB_TOKEN. Tokens are now only auto-resolved for the
github service; batch imports resolve per entry and memoize.
- Branding leftovers: 'Run: forgejo auth login' hint and
forgejo-cli/1.0.0 User-Agent now say stoke (UA tracks pkg.version).
- Added request timeouts (30s default, 10m for migrations).
- --limit and --team-id are validated as integers instead of silently
misbehaving on garbage (NaN made -l show all results).
New commands (per the repo's every-operation-becomes-a-command design):
- stoke issue create (title/body/body-file/assignees)
- stoke pr create (head/base/title/body/body-file)
Tests and metadata:
- New test suite on the built-in node:test runner (25 tests) covering
config resolution/persistence, the API client with a mocked fetch,
and end-to-end CLI behavior. npm test previously matched no files.
- package.json: engines >=22.12.0 (required by commander@15 — the
README claimed Node 18), repository, keywords, author; version 1.1.0.
- README: corrected Node requirement, documented repo rename (was
missing), issue create, pr create, logout options and revocation
caveat, --no-auto-init, XDG behavior, import token rules, testing.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-22 19:28:23 +00:00
. option ( '--auto-init' , 'initialize with a README (default)' )
. option ( '--no-auto-init' , 'create an empty repository without a README' )
2026-07-22 15:03:32 +00:00
. option ( '--default-branch <branch>' , 'default branch name' , 'main' )
. action ( async ( options ) => {
try {
const config = loadConfig ( ) ;
const client = ForgejoClient . fromConfig ( config ) ;
const isPrivate = options . public ? false : options . private ;
const payload = {
name : options . name ,
description : options . description ,
private : isPrivate ,
Audit: fix auth/config bugs, add issue/pr create, tests and docs
Fixes found during a full audit of the CLI:
- auth logout: remote token revocation always failed with 401 because
Forgejo only accepts Basic auth on the token endpoints. Logout now
asks for (or accepts) the account password, supports --password,
--password-file and --local-only, and clearly reports when the token
is left active.
- Silent password prompt actually echoed the password on a TTY:
overriding rl.write does not suppress readline echo. Switched to the
callback readline module and mute _writeToOutput instead (the
readline/promises interface does not honor that hook).
- Global --config flag was silently ignored: config paths were resolved
at require time, before the preAction hook set STOKE_CONFIG_FILE.
Paths are now resolved lazily on every access.
- XDG_CONFIG_HOME handling put the config in $XDG_CONFIG_HOME/.config/stoke;
per the XDG spec it now resolves to $XDG_CONFIG_HOME/stoke.
- repo create: --auto-init defaulted to true with no way to disable it;
added --no-auto-init.
- repo import/import-batch: a GitHub token was required even for
non-GitHub services (e.g. --service git), making those imports fail
without gh/GITHUB_TOKEN. Tokens are now only auto-resolved for the
github service; batch imports resolve per entry and memoize.
- Branding leftovers: 'Run: forgejo auth login' hint and
forgejo-cli/1.0.0 User-Agent now say stoke (UA tracks pkg.version).
- Added request timeouts (30s default, 10m for migrations).
- --limit and --team-id are validated as integers instead of silently
misbehaving on garbage (NaN made -l show all results).
New commands (per the repo's every-operation-becomes-a-command design):
- stoke issue create (title/body/body-file/assignees)
- stoke pr create (head/base/title/body/body-file)
Tests and metadata:
- New test suite on the built-in node:test runner (25 tests) covering
config resolution/persistence, the API client with a mocked fetch,
and end-to-end CLI behavior. npm test previously matched no files.
- package.json: engines >=22.12.0 (required by commander@15 — the
README claimed Node 18), repository, keywords, author; version 1.1.0.
- README: corrected Node requirement, documented repo rename (was
missing), issue create, pr create, logout options and revocation
caveat, --no-auto-init, XDG behavior, import token rules, testing.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-22 19:28:23 +00:00
auto _init : normalizeBool ( options . autoInit , true ) ,
2026-07-22 15:03:32 +00:00
default _branch : options . defaultBranch ,
} ;
const result = await client . createRepo ( payload ) ;
console . log ( ` Repository created: ${ result . full _name } ` ) ;
console . log ( ` URL: ${ result . html _url } ` ) ;
console . log ( ` Clone (SSH): ${ result . ssh _url } ` ) ;
console . log ( ` Clone (HTTP): ${ result . clone _url } ` ) ;
} catch ( err ) {
console . error ( ` Repository creation failed: ${ err . message } ` ) ;
if ( err . status ) console . error ( ` HTTP status: ${ err . status } ` ) ;
process . exit ( 1 ) ;
}
} ) ;
repo
. command ( 'import' )
. description ( 'Import a remote repository (GitHub, GitLab, plain git, etc.)' )
. requiredOption ( '--from <clone-addr>' , 'source clone URL, e.g. https://github.com/owner/repo.git' )
. requiredOption ( '--name <name>' , 'name for the imported repository' )
. option ( '--service <service>' , 'source service type' , 'github' )
. option ( '--owner <owner>' , 'Forgejo owner for the imported repository' )
. option ( '-d, --description <description>' , 'repository description' )
. option ( '--private' , 'make the repository private' , false )
. option ( '--public' , 'make the repository public' )
. option ( '--issues' , 'migrate issues' , true )
. option ( '--no-issues' , 'skip migrating issues' )
. option ( '--labels' , 'migrate labels' , true )
. option ( '--no-labels' , 'skip migrating labels' )
. option ( '--milestones' , 'migrate milestones' , true )
. option ( '--no-milestones' , 'skip migrating milestones' )
. option ( '--pull-requests' , 'migrate pull requests' , true )
. option ( '--no-pull-requests' , 'skip migrating pull requests' )
. option ( '--releases' , 'migrate releases' , true )
. option ( '--no-releases' , 'skip migrating releases' )
. option ( '--wiki' , 'migrate wiki' , true )
. option ( '--no-wiki' , 'skip migrating wiki' )
. option ( '--lfs' , 'migrate LFS objects' , false )
Audit: fix auth/config bugs, add issue/pr create, tests and docs
Fixes found during a full audit of the CLI:
- auth logout: remote token revocation always failed with 401 because
Forgejo only accepts Basic auth on the token endpoints. Logout now
asks for (or accepts) the account password, supports --password,
--password-file and --local-only, and clearly reports when the token
is left active.
- Silent password prompt actually echoed the password on a TTY:
overriding rl.write does not suppress readline echo. Switched to the
callback readline module and mute _writeToOutput instead (the
readline/promises interface does not honor that hook).
- Global --config flag was silently ignored: config paths were resolved
at require time, before the preAction hook set STOKE_CONFIG_FILE.
Paths are now resolved lazily on every access.
- XDG_CONFIG_HOME handling put the config in $XDG_CONFIG_HOME/.config/stoke;
per the XDG spec it now resolves to $XDG_CONFIG_HOME/stoke.
- repo create: --auto-init defaulted to true with no way to disable it;
added --no-auto-init.
- repo import/import-batch: a GitHub token was required even for
non-GitHub services (e.g. --service git), making those imports fail
without gh/GITHUB_TOKEN. Tokens are now only auto-resolved for the
github service; batch imports resolve per entry and memoize.
- Branding leftovers: 'Run: forgejo auth login' hint and
forgejo-cli/1.0.0 User-Agent now say stoke (UA tracks pkg.version).
- Added request timeouts (30s default, 10m for migrations).
- --limit and --team-id are validated as integers instead of silently
misbehaving on garbage (NaN made -l show all results).
New commands (per the repo's every-operation-becomes-a-command design):
- stoke issue create (title/body/body-file/assignees)
- stoke pr create (head/base/title/body/body-file)
Tests and metadata:
- New test suite on the built-in node:test runner (25 tests) covering
config resolution/persistence, the API client with a mocked fetch,
and end-to-end CLI behavior. npm test previously matched no files.
- package.json: engines >=22.12.0 (required by commander@15 — the
README claimed Node 18), repository, keywords, author; version 1.1.0.
- README: corrected Node requirement, documented repo rename (was
missing), issue create, pr create, logout options and revocation
caveat, --no-auto-init, XDG behavior, import token rules, testing.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-22 19:28:23 +00:00
. option ( '--github-token <token>' , 'source service token (for GitHub defaults to GITHUB_TOKEN or "gh auth token")' )
2026-07-22 15:03:32 +00:00
. action ( async ( options ) => {
try {
const config = loadConfig ( ) ;
const client = ForgejoClient . fromConfig ( config ) ;
const isPrivate = options . public ? false : options . private ;
Audit: fix auth/config bugs, add issue/pr create, tests and docs
Fixes found during a full audit of the CLI:
- auth logout: remote token revocation always failed with 401 because
Forgejo only accepts Basic auth on the token endpoints. Logout now
asks for (or accepts) the account password, supports --password,
--password-file and --local-only, and clearly reports when the token
is left active.
- Silent password prompt actually echoed the password on a TTY:
overriding rl.write does not suppress readline echo. Switched to the
callback readline module and mute _writeToOutput instead (the
readline/promises interface does not honor that hook).
- Global --config flag was silently ignored: config paths were resolved
at require time, before the preAction hook set STOKE_CONFIG_FILE.
Paths are now resolved lazily on every access.
- XDG_CONFIG_HOME handling put the config in $XDG_CONFIG_HOME/.config/stoke;
per the XDG spec it now resolves to $XDG_CONFIG_HOME/stoke.
- repo create: --auto-init defaulted to true with no way to disable it;
added --no-auto-init.
- repo import/import-batch: a GitHub token was required even for
non-GitHub services (e.g. --service git), making those imports fail
without gh/GITHUB_TOKEN. Tokens are now only auto-resolved for the
github service; batch imports resolve per entry and memoize.
- Branding leftovers: 'Run: forgejo auth login' hint and
forgejo-cli/1.0.0 User-Agent now say stoke (UA tracks pkg.version).
- Added request timeouts (30s default, 10m for migrations).
- --limit and --team-id are validated as integers instead of silently
misbehaving on garbage (NaN made -l show all results).
New commands (per the repo's every-operation-becomes-a-command design):
- stoke issue create (title/body/body-file/assignees)
- stoke pr create (head/base/title/body/body-file)
Tests and metadata:
- New test suite on the built-in node:test runner (25 tests) covering
config resolution/persistence, the API client with a mocked fetch,
and end-to-end CLI behavior. npm test previously matched no files.
- package.json: engines >=22.12.0 (required by commander@15 — the
README claimed Node 18), repository, keywords, author; version 1.1.0.
- README: corrected Node requirement, documented repo rename (was
missing), issue create, pr create, logout options and revocation
caveat, --no-auto-init, XDG behavior, import token rules, testing.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-22 19:28:23 +00:00
const token = resolveSourceToken ( options . githubToken , options . service ) ;
2026-07-22 15:03:32 +00:00
const payload = {
clone _addr : options . from ,
repo _name : options . name ,
repo _owner : options . owner || config . login ,
service : options . service ,
description : options . description || undefined ,
private : isPrivate ,
issues : normalizeBool ( options . issues , true ) ,
labels : normalizeBool ( options . labels , true ) ,
milestones : normalizeBool ( options . milestones , true ) ,
pull _requests : normalizeBool ( options . pullRequests , true ) ,
releases : normalizeBool ( options . releases , true ) ,
wiki : normalizeBool ( options . wiki , true ) ,
lfs : normalizeBool ( options . lfs , false ) ,
auth _token : token ,
} ;
// Remove undefined fields
Object . keys ( payload ) . forEach ( ( key ) => {
if ( payload [ key ] === undefined ) delete payload [ key ] ;
} ) ;
const result = await client . migrateRepo ( payload ) ;
console . log ( ` Repository imported: ${ result . full _name } ` ) ;
console . log ( ` URL: ${ result . html _url } ` ) ;
console . log ( ` Clone (SSH): ${ result . ssh _url } ` ) ;
console . log ( ` Clone (HTTP): ${ result . clone _url } ` ) ;
console . log ( ` Empty: ${ result . empty } ` ) ;
} catch ( err ) {
console . error ( ` Repository import failed: ${ err . message } ` ) ;
if ( err . status ) console . error ( ` HTTP status: ${ err . status } ` ) ;
process . exit ( 1 ) ;
}
} ) ;
2026-07-22 15:16:42 +00:00
repo
. command ( 'rename' )
. description ( 'Rename a repository' )
. requiredOption ( '-o, --owner <owner>' , 'repository owner' )
. requiredOption ( '-r, --repo <repo>' , 'current repository name' )
. requiredOption ( '--name <new-name>' , 'new repository name' )
. action ( async ( options ) => {
try {
const config = loadConfig ( ) ;
const client = ForgejoClient . fromConfig ( config ) ;
const result = await client . renameRepo ( options . owner , options . repo , options . name ) ;
console . log ( ` Repository renamed to ${ result . full _name } ` ) ;
console . log ( ` URL: ${ result . html _url } ` ) ;
} catch ( err ) {
console . error ( ` Repository rename failed: ${ err . message } ` ) ;
if ( err . status ) console . error ( ` HTTP status: ${ err . status } ` ) ;
process . exit ( 1 ) ;
}
} ) ;
2026-07-22 15:03:32 +00:00
repo
. command ( 'import-batch' )
. description ( 'Import multiple repositories from a JSON manifest' )
. requiredOption ( '-f, --file <path>' , 'path to JSON manifest' )
. option ( '--dry-run' , 'print the manifest without importing' , false )
. action ( async ( options ) => {
try {
const config = loadConfig ( ) ;
const client = ForgejoClient . fromConfig ( config ) ;
const raw = fs . readFileSync ( options . file , 'utf8' ) ;
const manifest = JSON . parse ( raw ) ;
if ( ! Array . isArray ( manifest ) ) {
throw new Error ( 'Manifest must be a JSON array' ) ;
}
if ( options . dryRun ) {
console . log ( JSON . stringify ( manifest , null , 2 ) ) ;
return ;
}
const results = [ ] ;
for ( const item of manifest ) {
const name = item . name || item . repo _name ;
const from = item . from || item . clone _addr ;
if ( ! name || ! from ) {
console . error ( ` Skipping invalid manifest entry: ${ JSON . stringify ( item ) } ` ) ;
continue ;
}
Audit: fix auth/config bugs, add issue/pr create, tests and docs
Fixes found during a full audit of the CLI:
- auth logout: remote token revocation always failed with 401 because
Forgejo only accepts Basic auth on the token endpoints. Logout now
asks for (or accepts) the account password, supports --password,
--password-file and --local-only, and clearly reports when the token
is left active.
- Silent password prompt actually echoed the password on a TTY:
overriding rl.write does not suppress readline echo. Switched to the
callback readline module and mute _writeToOutput instead (the
readline/promises interface does not honor that hook).
- Global --config flag was silently ignored: config paths were resolved
at require time, before the preAction hook set STOKE_CONFIG_FILE.
Paths are now resolved lazily on every access.
- XDG_CONFIG_HOME handling put the config in $XDG_CONFIG_HOME/.config/stoke;
per the XDG spec it now resolves to $XDG_CONFIG_HOME/stoke.
- repo create: --auto-init defaulted to true with no way to disable it;
added --no-auto-init.
- repo import/import-batch: a GitHub token was required even for
non-GitHub services (e.g. --service git), making those imports fail
without gh/GITHUB_TOKEN. Tokens are now only auto-resolved for the
github service; batch imports resolve per entry and memoize.
- Branding leftovers: 'Run: forgejo auth login' hint and
forgejo-cli/1.0.0 User-Agent now say stoke (UA tracks pkg.version).
- Added request timeouts (30s default, 10m for migrations).
- --limit and --team-id are validated as integers instead of silently
misbehaving on garbage (NaN made -l show all results).
New commands (per the repo's every-operation-becomes-a-command design):
- stoke issue create (title/body/body-file/assignees)
- stoke pr create (head/base/title/body/body-file)
Tests and metadata:
- New test suite on the built-in node:test runner (25 tests) covering
config resolution/persistence, the API client with a mocked fetch,
and end-to-end CLI behavior. npm test previously matched no files.
- package.json: engines >=22.12.0 (required by commander@15 — the
README claimed Node 18), repository, keywords, author; version 1.1.0.
- README: corrected Node requirement, documented repo rename (was
missing), issue create, pr create, logout options and revocation
caveat, --no-auto-init, XDG behavior, import token rules, testing.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-22 19:28:23 +00:00
const service = item . service || 'github' ;
2026-07-22 15:03:32 +00:00
const isPrivate = item . public ? false : Boolean ( item . private ) ;
const payload = {
clone _addr : from ,
repo _name : name ,
repo _owner : item . owner || item . repo _owner || config . login ,
Audit: fix auth/config bugs, add issue/pr create, tests and docs
Fixes found during a full audit of the CLI:
- auth logout: remote token revocation always failed with 401 because
Forgejo only accepts Basic auth on the token endpoints. Logout now
asks for (or accepts) the account password, supports --password,
--password-file and --local-only, and clearly reports when the token
is left active.
- Silent password prompt actually echoed the password on a TTY:
overriding rl.write does not suppress readline echo. Switched to the
callback readline module and mute _writeToOutput instead (the
readline/promises interface does not honor that hook).
- Global --config flag was silently ignored: config paths were resolved
at require time, before the preAction hook set STOKE_CONFIG_FILE.
Paths are now resolved lazily on every access.
- XDG_CONFIG_HOME handling put the config in $XDG_CONFIG_HOME/.config/stoke;
per the XDG spec it now resolves to $XDG_CONFIG_HOME/stoke.
- repo create: --auto-init defaulted to true with no way to disable it;
added --no-auto-init.
- repo import/import-batch: a GitHub token was required even for
non-GitHub services (e.g. --service git), making those imports fail
without gh/GITHUB_TOKEN. Tokens are now only auto-resolved for the
github service; batch imports resolve per entry and memoize.
- Branding leftovers: 'Run: forgejo auth login' hint and
forgejo-cli/1.0.0 User-Agent now say stoke (UA tracks pkg.version).
- Added request timeouts (30s default, 10m for migrations).
- --limit and --team-id are validated as integers instead of silently
misbehaving on garbage (NaN made -l show all results).
New commands (per the repo's every-operation-becomes-a-command design):
- stoke issue create (title/body/body-file/assignees)
- stoke pr create (head/base/title/body/body-file)
Tests and metadata:
- New test suite on the built-in node:test runner (25 tests) covering
config resolution/persistence, the API client with a mocked fetch,
and end-to-end CLI behavior. npm test previously matched no files.
- package.json: engines >=22.12.0 (required by commander@15 — the
README claimed Node 18), repository, keywords, author; version 1.1.0.
- README: corrected Node requirement, documented repo rename (was
missing), issue create, pr create, logout options and revocation
caveat, --no-auto-init, XDG behavior, import token rules, testing.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-22 19:28:23 +00:00
service ,
2026-07-22 15:03:32 +00:00
description : item . description || undefined ,
private : isPrivate ,
issues : normalizeBool ( item . issues , true ) ,
labels : normalizeBool ( item . labels , true ) ,
milestones : normalizeBool ( item . milestones , true ) ,
pull _requests : normalizeBool ( item . pull _requests , true ) ,
releases : normalizeBool ( item . releases , true ) ,
wiki : normalizeBool ( item . wiki , true ) ,
lfs : normalizeBool ( item . lfs , false ) ,
Audit: fix auth/config bugs, add issue/pr create, tests and docs
Fixes found during a full audit of the CLI:
- auth logout: remote token revocation always failed with 401 because
Forgejo only accepts Basic auth on the token endpoints. Logout now
asks for (or accepts) the account password, supports --password,
--password-file and --local-only, and clearly reports when the token
is left active.
- Silent password prompt actually echoed the password on a TTY:
overriding rl.write does not suppress readline echo. Switched to the
callback readline module and mute _writeToOutput instead (the
readline/promises interface does not honor that hook).
- Global --config flag was silently ignored: config paths were resolved
at require time, before the preAction hook set STOKE_CONFIG_FILE.
Paths are now resolved lazily on every access.
- XDG_CONFIG_HOME handling put the config in $XDG_CONFIG_HOME/.config/stoke;
per the XDG spec it now resolves to $XDG_CONFIG_HOME/stoke.
- repo create: --auto-init defaulted to true with no way to disable it;
added --no-auto-init.
- repo import/import-batch: a GitHub token was required even for
non-GitHub services (e.g. --service git), making those imports fail
without gh/GITHUB_TOKEN. Tokens are now only auto-resolved for the
github service; batch imports resolve per entry and memoize.
- Branding leftovers: 'Run: forgejo auth login' hint and
forgejo-cli/1.0.0 User-Agent now say stoke (UA tracks pkg.version).
- Added request timeouts (30s default, 10m for migrations).
- --limit and --team-id are validated as integers instead of silently
misbehaving on garbage (NaN made -l show all results).
New commands (per the repo's every-operation-becomes-a-command design):
- stoke issue create (title/body/body-file/assignees)
- stoke pr create (head/base/title/body/body-file)
Tests and metadata:
- New test suite on the built-in node:test runner (25 tests) covering
config resolution/persistence, the API client with a mocked fetch,
and end-to-end CLI behavior. npm test previously matched no files.
- package.json: engines >=22.12.0 (required by commander@15 — the
README claimed Node 18), repository, keywords, author; version 1.1.0.
- README: corrected Node requirement, documented repo rename (was
missing), issue create, pr create, logout options and revocation
caveat, --no-auto-init, XDG behavior, import token rules, testing.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-22 19:28:23 +00:00
auth _token : resolveSourceToken ( item . github _token , service ) ,
2026-07-22 15:03:32 +00:00
} ;
Object . keys ( payload ) . forEach ( ( key ) => {
if ( payload [ key ] === undefined ) delete payload [ key ] ;
} ) ;
try {
const result = await client . migrateRepo ( payload ) ;
console . log ( ` Imported: ${ result . full _name } -> ${ result . html _url } ` ) ;
results . push ( { name , status : 'ok' , url : result . html _url } ) ;
} catch ( err ) {
console . error ( ` Failed to import ${ name } : ${ err . message } ` ) ;
results . push ( { name , status : 'failed' , error : err . message } ) ;
}
}
const ok = results . filter ( ( r ) => r . status === 'ok' ) . length ;
console . log ( ` \n Batch complete: ${ ok } / ${ results . length } imported. ` ) ;
if ( ok < results . length ) process . exit ( 1 ) ;
} catch ( err ) {
console . error ( ` Batch import failed: ${ err . message } ` ) ;
process . exit ( 1 ) ;
}
} ) ;
2026-07-22 17:47:48 +00:00
repo
. command ( 'transfer' )
. description ( 'Transfer a repository to a new owner (user or organization)' )
. requiredOption ( '-o, --owner <owner>' , 'current repository owner' )
. requiredOption ( '-r, --repo <repo>' , 'repository name' )
. requiredOption ( '--to <new-owner>' , 'new owner (username or organization name)' )
. action ( async ( options ) => {
try {
const config = loadConfig ( ) ;
const client = ForgejoClient . fromConfig ( config ) ;
const result = await client . transferRepo ( options . owner , options . repo , options . to ) ;
console . log ( ` Repository transferred: ${ result . full _name } ` ) ;
console . log ( ` URL: ${ result . html _url } ` ) ;
} catch ( err ) {
console . error ( ` Repository transfer failed: ${ err . message } ` ) ;
if ( err . status ) console . error ( ` HTTP status: ${ err . status } ` ) ;
process . exit ( 1 ) ;
}
} ) ;
2026-07-22 15:03:32 +00:00
const issue = program
. command ( 'issue' )
. description ( 'Manage issues' ) ;
issue
. command ( 'list' )
. description ( 'List issues in a repository' )
. requiredOption ( '-o, --owner <owner>' , 'repository owner' )
. requiredOption ( '-r, --repo <repo>' , 'repository name' )
. option ( '-s, --state <state>' , 'issue state: open, closed, all' , 'open' )
. option ( '-t, --type <type>' , 'issue type filter: issues, pulls' , 'issues' )
Audit: fix auth/config bugs, add issue/pr create, tests and docs
Fixes found during a full audit of the CLI:
- auth logout: remote token revocation always failed with 401 because
Forgejo only accepts Basic auth on the token endpoints. Logout now
asks for (or accepts) the account password, supports --password,
--password-file and --local-only, and clearly reports when the token
is left active.
- Silent password prompt actually echoed the password on a TTY:
overriding rl.write does not suppress readline echo. Switched to the
callback readline module and mute _writeToOutput instead (the
readline/promises interface does not honor that hook).
- Global --config flag was silently ignored: config paths were resolved
at require time, before the preAction hook set STOKE_CONFIG_FILE.
Paths are now resolved lazily on every access.
- XDG_CONFIG_HOME handling put the config in $XDG_CONFIG_HOME/.config/stoke;
per the XDG spec it now resolves to $XDG_CONFIG_HOME/stoke.
- repo create: --auto-init defaulted to true with no way to disable it;
added --no-auto-init.
- repo import/import-batch: a GitHub token was required even for
non-GitHub services (e.g. --service git), making those imports fail
without gh/GITHUB_TOKEN. Tokens are now only auto-resolved for the
github service; batch imports resolve per entry and memoize.
- Branding leftovers: 'Run: forgejo auth login' hint and
forgejo-cli/1.0.0 User-Agent now say stoke (UA tracks pkg.version).
- Added request timeouts (30s default, 10m for migrations).
- --limit and --team-id are validated as integers instead of silently
misbehaving on garbage (NaN made -l show all results).
New commands (per the repo's every-operation-becomes-a-command design):
- stoke issue create (title/body/body-file/assignees)
- stoke pr create (head/base/title/body/body-file)
Tests and metadata:
- New test suite on the built-in node:test runner (25 tests) covering
config resolution/persistence, the API client with a mocked fetch,
and end-to-end CLI behavior. npm test previously matched no files.
- package.json: engines >=22.12.0 (required by commander@15 — the
README claimed Node 18), repository, keywords, author; version 1.1.0.
- README: corrected Node requirement, documented repo rename (was
missing), issue create, pr create, logout options and revocation
caveat, --no-auto-init, XDG behavior, import token rules, testing.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-22 19:28:23 +00:00
. option ( '-l, --limit <number>' , 'maximum issues to return (0 for all)' , parseLimit , 50 )
2026-07-22 15:03:32 +00:00
. action ( async ( options ) => {
try {
const config = loadConfig ( ) ;
const client = ForgejoClient . fromConfig ( config ) ;
const issues = await client . listIssues ( options . owner , options . repo , {
state : options . state ,
type : options . type ,
} ) ;
Audit: fix auth/config bugs, add issue/pr create, tests and docs
Fixes found during a full audit of the CLI:
- auth logout: remote token revocation always failed with 401 because
Forgejo only accepts Basic auth on the token endpoints. Logout now
asks for (or accepts) the account password, supports --password,
--password-file and --local-only, and clearly reports when the token
is left active.
- Silent password prompt actually echoed the password on a TTY:
overriding rl.write does not suppress readline echo. Switched to the
callback readline module and mute _writeToOutput instead (the
readline/promises interface does not honor that hook).
- Global --config flag was silently ignored: config paths were resolved
at require time, before the preAction hook set STOKE_CONFIG_FILE.
Paths are now resolved lazily on every access.
- XDG_CONFIG_HOME handling put the config in $XDG_CONFIG_HOME/.config/stoke;
per the XDG spec it now resolves to $XDG_CONFIG_HOME/stoke.
- repo create: --auto-init defaulted to true with no way to disable it;
added --no-auto-init.
- repo import/import-batch: a GitHub token was required even for
non-GitHub services (e.g. --service git), making those imports fail
without gh/GITHUB_TOKEN. Tokens are now only auto-resolved for the
github service; batch imports resolve per entry and memoize.
- Branding leftovers: 'Run: forgejo auth login' hint and
forgejo-cli/1.0.0 User-Agent now say stoke (UA tracks pkg.version).
- Added request timeouts (30s default, 10m for migrations).
- --limit and --team-id are validated as integers instead of silently
misbehaving on garbage (NaN made -l show all results).
New commands (per the repo's every-operation-becomes-a-command design):
- stoke issue create (title/body/body-file/assignees)
- stoke pr create (head/base/title/body/body-file)
Tests and metadata:
- New test suite on the built-in node:test runner (25 tests) covering
config resolution/persistence, the API client with a mocked fetch,
and end-to-end CLI behavior. npm test previously matched no files.
- package.json: engines >=22.12.0 (required by commander@15 — the
README claimed Node 18), repository, keywords, author; version 1.1.0.
- README: corrected Node requirement, documented repo rename (was
missing), issue create, pr create, logout options and revocation
caveat, --no-auto-init, XDG behavior, import token rules, testing.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-22 19:28:23 +00:00
const display = options . limit > 0 ? issues . slice ( 0 , options . limit ) : issues ;
2026-07-22 15:03:32 +00:00
if ( ! display . length ) {
console . log ( 'No issues found.' ) ;
return ;
}
for ( const i of display ) {
console . log ( ` # ${ i . number } [ ${ i . state } ] ${ i . title } ` ) ;
}
if ( issues . length > display . length ) {
console . log ( ` ...and ${ issues . length - display . length } more (use -l 0 for all). ` ) ;
}
} catch ( err ) {
console . error ( ` Failed to list issues: ${ err . message } ` ) ;
process . exit ( 1 ) ;
}
} ) ;
Audit: fix auth/config bugs, add issue/pr create, tests and docs
Fixes found during a full audit of the CLI:
- auth logout: remote token revocation always failed with 401 because
Forgejo only accepts Basic auth on the token endpoints. Logout now
asks for (or accepts) the account password, supports --password,
--password-file and --local-only, and clearly reports when the token
is left active.
- Silent password prompt actually echoed the password on a TTY:
overriding rl.write does not suppress readline echo. Switched to the
callback readline module and mute _writeToOutput instead (the
readline/promises interface does not honor that hook).
- Global --config flag was silently ignored: config paths were resolved
at require time, before the preAction hook set STOKE_CONFIG_FILE.
Paths are now resolved lazily on every access.
- XDG_CONFIG_HOME handling put the config in $XDG_CONFIG_HOME/.config/stoke;
per the XDG spec it now resolves to $XDG_CONFIG_HOME/stoke.
- repo create: --auto-init defaulted to true with no way to disable it;
added --no-auto-init.
- repo import/import-batch: a GitHub token was required even for
non-GitHub services (e.g. --service git), making those imports fail
without gh/GITHUB_TOKEN. Tokens are now only auto-resolved for the
github service; batch imports resolve per entry and memoize.
- Branding leftovers: 'Run: forgejo auth login' hint and
forgejo-cli/1.0.0 User-Agent now say stoke (UA tracks pkg.version).
- Added request timeouts (30s default, 10m for migrations).
- --limit and --team-id are validated as integers instead of silently
misbehaving on garbage (NaN made -l show all results).
New commands (per the repo's every-operation-becomes-a-command design):
- stoke issue create (title/body/body-file/assignees)
- stoke pr create (head/base/title/body/body-file)
Tests and metadata:
- New test suite on the built-in node:test runner (25 tests) covering
config resolution/persistence, the API client with a mocked fetch,
and end-to-end CLI behavior. npm test previously matched no files.
- package.json: engines >=22.12.0 (required by commander@15 — the
README claimed Node 18), repository, keywords, author; version 1.1.0.
- README: corrected Node requirement, documented repo rename (was
missing), issue create, pr create, logout options and revocation
caveat, --no-auto-init, XDG behavior, import token rules, testing.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-22 19:28:23 +00:00
issue
. command ( 'create' )
. description ( 'Create an issue in a repository' )
. requiredOption ( '-o, --owner <owner>' , 'repository owner' )
. requiredOption ( '-r, --repo <repo>' , 'repository name' )
. requiredOption ( '-t, --title <title>' , 'issue title' )
. option ( '-b, --body <body>' , 'issue body (markdown)' )
. option ( '--body-file <path>' , 'read the issue body from a file' )
. option ( '--assignee <username...>' , 'assign the issue to one or more users' )
. action ( async ( options ) => {
try {
const config = loadConfig ( ) ;
const client = ForgejoClient . fromConfig ( config ) ;
const payload = {
title : options . title ,
body : readBodyOption ( options ) || '' ,
} ;
if ( options . assignee && options . assignee . length ) {
payload . assignees = options . assignee ;
}
const result = await client . createIssue ( options . owner , options . repo , payload ) ;
console . log ( ` Issue created: # ${ result . number } ${ result . title } ` ) ;
console . log ( ` URL: ${ result . html _url } ` ) ;
} catch ( err ) {
console . error ( ` Issue creation failed: ${ err . message } ` ) ;
if ( err . status ) console . error ( ` HTTP status: ${ err . status } ` ) ;
process . exit ( 1 ) ;
}
} ) ;
2026-07-22 15:03:32 +00:00
const pr = program
. command ( 'pr' )
. description ( 'Manage pull requests' ) ;
pr
. command ( 'list' )
. description ( 'List pull requests in a repository' )
. requiredOption ( '-o, --owner <owner>' , 'repository owner' )
. requiredOption ( '-r, --repo <repo>' , 'repository name' )
. option ( '-s, --state <state>' , 'PR state: open, closed, all' , 'open' )
Audit: fix auth/config bugs, add issue/pr create, tests and docs
Fixes found during a full audit of the CLI:
- auth logout: remote token revocation always failed with 401 because
Forgejo only accepts Basic auth on the token endpoints. Logout now
asks for (or accepts) the account password, supports --password,
--password-file and --local-only, and clearly reports when the token
is left active.
- Silent password prompt actually echoed the password on a TTY:
overriding rl.write does not suppress readline echo. Switched to the
callback readline module and mute _writeToOutput instead (the
readline/promises interface does not honor that hook).
- Global --config flag was silently ignored: config paths were resolved
at require time, before the preAction hook set STOKE_CONFIG_FILE.
Paths are now resolved lazily on every access.
- XDG_CONFIG_HOME handling put the config in $XDG_CONFIG_HOME/.config/stoke;
per the XDG spec it now resolves to $XDG_CONFIG_HOME/stoke.
- repo create: --auto-init defaulted to true with no way to disable it;
added --no-auto-init.
- repo import/import-batch: a GitHub token was required even for
non-GitHub services (e.g. --service git), making those imports fail
without gh/GITHUB_TOKEN. Tokens are now only auto-resolved for the
github service; batch imports resolve per entry and memoize.
- Branding leftovers: 'Run: forgejo auth login' hint and
forgejo-cli/1.0.0 User-Agent now say stoke (UA tracks pkg.version).
- Added request timeouts (30s default, 10m for migrations).
- --limit and --team-id are validated as integers instead of silently
misbehaving on garbage (NaN made -l show all results).
New commands (per the repo's every-operation-becomes-a-command design):
- stoke issue create (title/body/body-file/assignees)
- stoke pr create (head/base/title/body/body-file)
Tests and metadata:
- New test suite on the built-in node:test runner (25 tests) covering
config resolution/persistence, the API client with a mocked fetch,
and end-to-end CLI behavior. npm test previously matched no files.
- package.json: engines >=22.12.0 (required by commander@15 — the
README claimed Node 18), repository, keywords, author; version 1.1.0.
- README: corrected Node requirement, documented repo rename (was
missing), issue create, pr create, logout options and revocation
caveat, --no-auto-init, XDG behavior, import token rules, testing.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-22 19:28:23 +00:00
. option ( '-l, --limit <number>' , 'maximum pull requests to return (0 for all)' , parseLimit , 50 )
2026-07-22 15:03:32 +00:00
. action ( async ( options ) => {
try {
const config = loadConfig ( ) ;
const client = ForgejoClient . fromConfig ( config ) ;
const pulls = await client . listPullRequests ( options . owner , options . repo , {
state : options . state ,
} ) ;
Audit: fix auth/config bugs, add issue/pr create, tests and docs
Fixes found during a full audit of the CLI:
- auth logout: remote token revocation always failed with 401 because
Forgejo only accepts Basic auth on the token endpoints. Logout now
asks for (or accepts) the account password, supports --password,
--password-file and --local-only, and clearly reports when the token
is left active.
- Silent password prompt actually echoed the password on a TTY:
overriding rl.write does not suppress readline echo. Switched to the
callback readline module and mute _writeToOutput instead (the
readline/promises interface does not honor that hook).
- Global --config flag was silently ignored: config paths were resolved
at require time, before the preAction hook set STOKE_CONFIG_FILE.
Paths are now resolved lazily on every access.
- XDG_CONFIG_HOME handling put the config in $XDG_CONFIG_HOME/.config/stoke;
per the XDG spec it now resolves to $XDG_CONFIG_HOME/stoke.
- repo create: --auto-init defaulted to true with no way to disable it;
added --no-auto-init.
- repo import/import-batch: a GitHub token was required even for
non-GitHub services (e.g. --service git), making those imports fail
without gh/GITHUB_TOKEN. Tokens are now only auto-resolved for the
github service; batch imports resolve per entry and memoize.
- Branding leftovers: 'Run: forgejo auth login' hint and
forgejo-cli/1.0.0 User-Agent now say stoke (UA tracks pkg.version).
- Added request timeouts (30s default, 10m for migrations).
- --limit and --team-id are validated as integers instead of silently
misbehaving on garbage (NaN made -l show all results).
New commands (per the repo's every-operation-becomes-a-command design):
- stoke issue create (title/body/body-file/assignees)
- stoke pr create (head/base/title/body/body-file)
Tests and metadata:
- New test suite on the built-in node:test runner (25 tests) covering
config resolution/persistence, the API client with a mocked fetch,
and end-to-end CLI behavior. npm test previously matched no files.
- package.json: engines >=22.12.0 (required by commander@15 — the
README claimed Node 18), repository, keywords, author; version 1.1.0.
- README: corrected Node requirement, documented repo rename (was
missing), issue create, pr create, logout options and revocation
caveat, --no-auto-init, XDG behavior, import token rules, testing.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-22 19:28:23 +00:00
const display = options . limit > 0 ? pulls . slice ( 0 , options . limit ) : pulls ;
2026-07-22 15:03:32 +00:00
if ( ! display . length ) {
console . log ( 'No pull requests found.' ) ;
return ;
}
for ( const p of display ) {
console . log ( ` ! ${ p . number } [ ${ p . state } ] ${ p . title } ( ${ p . head . ref } -> ${ p . base . ref } ) ` ) ;
}
if ( pulls . length > display . length ) {
console . log ( ` ...and ${ pulls . length - display . length } more (use -l 0 for all). ` ) ;
}
} catch ( err ) {
console . error ( ` Failed to list pull requests: ${ err . message } ` ) ;
process . exit ( 1 ) ;
}
} ) ;
Audit: fix auth/config bugs, add issue/pr create, tests and docs
Fixes found during a full audit of the CLI:
- auth logout: remote token revocation always failed with 401 because
Forgejo only accepts Basic auth on the token endpoints. Logout now
asks for (or accepts) the account password, supports --password,
--password-file and --local-only, and clearly reports when the token
is left active.
- Silent password prompt actually echoed the password on a TTY:
overriding rl.write does not suppress readline echo. Switched to the
callback readline module and mute _writeToOutput instead (the
readline/promises interface does not honor that hook).
- Global --config flag was silently ignored: config paths were resolved
at require time, before the preAction hook set STOKE_CONFIG_FILE.
Paths are now resolved lazily on every access.
- XDG_CONFIG_HOME handling put the config in $XDG_CONFIG_HOME/.config/stoke;
per the XDG spec it now resolves to $XDG_CONFIG_HOME/stoke.
- repo create: --auto-init defaulted to true with no way to disable it;
added --no-auto-init.
- repo import/import-batch: a GitHub token was required even for
non-GitHub services (e.g. --service git), making those imports fail
without gh/GITHUB_TOKEN. Tokens are now only auto-resolved for the
github service; batch imports resolve per entry and memoize.
- Branding leftovers: 'Run: forgejo auth login' hint and
forgejo-cli/1.0.0 User-Agent now say stoke (UA tracks pkg.version).
- Added request timeouts (30s default, 10m for migrations).
- --limit and --team-id are validated as integers instead of silently
misbehaving on garbage (NaN made -l show all results).
New commands (per the repo's every-operation-becomes-a-command design):
- stoke issue create (title/body/body-file/assignees)
- stoke pr create (head/base/title/body/body-file)
Tests and metadata:
- New test suite on the built-in node:test runner (25 tests) covering
config resolution/persistence, the API client with a mocked fetch,
and end-to-end CLI behavior. npm test previously matched no files.
- package.json: engines >=22.12.0 (required by commander@15 — the
README claimed Node 18), repository, keywords, author; version 1.1.0.
- README: corrected Node requirement, documented repo rename (was
missing), issue create, pr create, logout options and revocation
caveat, --no-auto-init, XDG behavior, import token rules, testing.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-22 19:28:23 +00:00
pr
. command ( 'create' )
. description ( 'Create a pull request in a repository' )
. requiredOption ( '-o, --owner <owner>' , 'repository owner' )
. requiredOption ( '-r, --repo <repo>' , 'repository name' )
. requiredOption ( '-t, --title <title>' , 'pull request title' )
. requiredOption ( '--head <branch>' , 'source branch (for cross-repo PRs use user:branch)' )
. option ( '--base <branch>' , 'target branch' , 'main' )
. option ( '-b, --body <body>' , 'pull request body (markdown)' )
. option ( '--body-file <path>' , 'read the pull request body from a file' )
. action ( async ( options ) => {
try {
const config = loadConfig ( ) ;
const client = ForgejoClient . fromConfig ( config ) ;
const payload = {
title : options . title ,
head : options . head ,
base : options . base ,
body : readBodyOption ( options ) || '' ,
} ;
const result = await client . createPullRequest ( options . owner , options . repo , payload ) ;
console . log ( ` Pull request created: ! ${ result . number } ${ result . title } ` ) ;
console . log ( ` URL: ${ result . html _url } ` ) ;
} catch ( err ) {
console . error ( ` Pull request creation failed: ${ err . message } ` ) ;
if ( err . status ) console . error ( ` HTTP status: ${ err . status } ` ) ;
process . exit ( 1 ) ;
}
} ) ;
Add apt distribution: deb packaging, registry publish, docs (#1)
Implements #1 — stoke installable with apt-get install stoke.
Packaging:
- scripts/build-deb.sh: builds dist/stoke_<version>_all.deb from a clean
staging copy (src + fresh npm ci --omit=dev), pure-JS Architecture: all,
Depends: nodejs (>= 22.12), /usr/lib/stoke payload with /usr/bin/stoke
symlink, copyright + changelog, normalized permissions. Lintian-clean.
- scripts/publish-deb.sh: uploads a .deb to the Forgejo Debian registry
(owner/distribution/component parameterized, defaults heavy-duty/
stable/main), authenticating with STOKE_TOKEN or the stoke login token.
- scripts/install-apt.sh: consumer-side one-time setup — adds the
registry key and apt source, then apt-get install stoke. Falls back to
a [trusted=yes] source when apt's sqv verifier rejects the forge's
registry signature (known upstream Forgejo signing bug; the script
prefers the signed source so setups heal once the forge is fixed).
- .forgejo/workflows/release.yml: on v* tags — test, build, publish to
the heavy-duty registry, attach the .deb to the release page. Needs a
runner and a RELEASE_TOKEN secret with org package write.
New command:
- stoke pr merge (-n, --method merge|rebase|rebase-merge|squash,
--title, --message, --delete-branch) — gap found while merging !2.
Docs and housekeeping:
- README: 'Install with apt' as the primary installation method with
manual setup and dpkg fallback, signature caveat, pr merge reference,
Packaging and releasing section with a release checklist.
- dist/ gitignored; version bumped to 1.2.0.
Verified end-to-end on this machine: built the deb (lintian-clean),
published it to the Forgejo Debian registry, installed it with
apt-get install stoke via install-apt.sh, and confirmed the installed
CLI works against the live forge. The test upload was removed from the
personal namespace afterwards; publishing under heavy-duty needs an
org-member token (401 reqPackageAccess with this restricted account).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-22 19:40:51 +00:00
pr
. command ( 'merge' )
. description ( 'Merge a pull request' )
. requiredOption ( '-o, --owner <owner>' , 'repository owner' )
. requiredOption ( '-r, --repo <repo>' , 'repository name' )
. requiredOption ( '-n, --number <number>' , 'pull request number' , parseId )
. option ( '--method <method>' , 'merge method: merge, rebase, rebase-merge, squash' , 'merge' )
. option ( '--title <title>' , 'custom merge commit title' )
. option ( '--message <message>' , 'custom merge commit message' )
. option ( '--delete-branch' , 'delete the source branch after merging' , false )
. action ( async ( options ) => {
try {
const config = loadConfig ( ) ;
const client = ForgejoClient . fromConfig ( config ) ;
const payload = {
Do : options . method ,
delete _branch _after _merge : options . deleteBranch ,
} ;
if ( options . title ) payload . MergeTitleField = options . title ;
if ( options . message ) payload . MergeMessageField = options . message ;
await client . mergePullRequest ( options . owner , options . repo , options . number , payload ) ;
console . log ( ` Merged ! ${ options . number } in ${ options . owner } / ${ options . repo } ( ${ options . method } ). ` ) ;
if ( options . deleteBranch ) console . log ( 'Source branch deleted.' ) ;
} catch ( err ) {
console . error ( ` Pull request merge failed: ${ err . message } ` ) ;
if ( err . status ) console . error ( ` HTTP status: ${ err . status } ` ) ;
process . exit ( 1 ) ;
}
} ) ;
2026-07-22 21:19:36 +00:00
pr
. command ( 'show' )
. description ( 'Show details of a pull request' )
. requiredOption ( '-o, --owner <owner>' , 'repository owner' )
. requiredOption ( '-r, --repo <repo>' , 'repository name' )
. requiredOption ( '-n, --number <number>' , 'pull request number' , parseId )
. action ( async ( options ) => {
try {
const config = loadConfig ( ) ;
const client = ForgejoClient . fromConfig ( config ) ;
const prData = await client . getPullRequest ( options . owner , options . repo , options . number ) ;
console . log ( ` ! ${ prData . number } [ ${ prData . state } ] ${ prData . title } ` ) ;
console . log ( ` URL: ${ prData . html _url } ` ) ;
console . log ( ` Author: ${ prData . user . login } ` ) ;
console . log ( ` Branch: ${ prData . head . ref } -> ${ prData . base . ref } ` ) ;
console . log ( ` Mergeable: ${ prData . mergeable } ` ) ;
console . log ( ` Created: ${ prData . created _at } ` ) ;
if ( prData . body ) {
console . log ( '\n' + prData . body ) ;
}
} catch ( err ) {
console . error ( ` Failed to show pull request: ${ err . message } ` ) ;
if ( err . status ) console . error ( ` HTTP status: ${ err . status } ` ) ;
process . exit ( 1 ) ;
}
} ) ;
pr
. command ( 'comment' )
. description ( 'Add a comment to a pull request' )
. requiredOption ( '-o, --owner <owner>' , 'repository owner' )
. requiredOption ( '-r, --repo <repo>' , 'repository name' )
. requiredOption ( '-n, --number <number>' , 'pull request number' , parseId )
. option ( '-b, --body <body>' , 'comment body (markdown)' )
. option ( '--body-file <path>' , 'read the comment body from a file' )
. action ( async ( options ) => {
try {
const config = loadConfig ( ) ;
const client = ForgejoClient . fromConfig ( config ) ;
2026-07-22 21:57:30 +00:00
const rawBody = readBodyOption ( options ) || '' ;
if ( rawBody . trim ( ) . length === 0 ) {
2026-07-22 21:19:36 +00:00
console . error ( 'Comment body is required. Use -b/--body or --body-file.' ) ;
process . exit ( 1 ) ;
}
2026-07-22 21:57:30 +00:00
const result = await client . createPullRequestComment ( options . owner , options . repo , options . number , rawBody ) ;
2026-07-22 21:19:36 +00:00
console . log ( ` Comment added to ! ${ options . number } . ` ) ;
console . log ( ` URL: ${ result . html _url } ` ) ;
} catch ( err ) {
console . error ( ` Failed to comment on pull request: ${ err . message } ` ) ;
if ( err . status ) console . error ( ` HTTP status: ${ err . status } ` ) ;
process . exit ( 1 ) ;
}
} ) ;
pr
. command ( 'review' )
. description ( 'Submit a review on a pull request' )
. requiredOption ( '-o, --owner <owner>' , 'repository owner' )
. requiredOption ( '-r, --repo <repo>' , 'repository name' )
. requiredOption ( '-n, --number <number>' , 'pull request number' , parseId )
2026-07-22 21:57:30 +00:00
. requiredOption ( '--event <event>' , 'review event: approve, request-changes|request_changes, comment' )
2026-07-22 21:19:36 +00:00
. option ( '-b, --body <body>' , 'review body (markdown)' )
. option ( '--body-file <path>' , 'read the review body from a file' )
. action ( async ( options ) => {
try {
const config = loadConfig ( ) ;
const client = ForgejoClient . fromConfig ( config ) ;
const eventMap = {
2026-07-22 21:32:06 +00:00
approve : 'APPROVED' ,
2026-07-22 21:19:36 +00:00
'request-changes' : 'REQUEST_CHANGES' ,
2026-07-22 21:57:30 +00:00
request _changes : 'REQUEST_CHANGES' ,
2026-07-22 21:19:36 +00:00
comment : 'COMMENT' ,
} ;
const event = eventMap [ options . event . toLowerCase ( ) ] ;
if ( ! event ) {
2026-07-22 21:57:30 +00:00
console . error ( ` Invalid review event: ${ options . event } . Must be approve, request-changes (or request_changes), or comment. ` ) ;
2026-07-22 21:19:36 +00:00
process . exit ( 1 ) ;
}
2026-07-22 21:37:56 +00:00
const rawBody = readBodyOption ( options ) || '' ;
if ( event !== 'APPROVED' && rawBody . trim ( ) . length === 0 ) {
2026-07-22 21:32:06 +00:00
console . error ( ` Review event ${ options . event } requires a non-empty body. Use -b/--body or --body-file. ` ) ;
process . exit ( 1 ) ;
}
2026-07-22 21:37:56 +00:00
await client . createPullRequestReview ( options . owner , options . repo , options . number , event , rawBody ) ;
2026-07-22 21:19:36 +00:00
console . log ( ` Review submitted on ! ${ options . number } : ${ event } . ` ) ;
} catch ( err ) {
console . error ( ` Failed to submit review: ${ err . message } ` ) ;
if ( err . status ) console . error ( ` HTTP status: ${ err . status } ` ) ;
process . exit ( 1 ) ;
}
} ) ;
2026-07-22 15:03:32 +00:00
const branchCmd = program
. command ( 'branch' )
. description ( 'Manage branches' ) ;
branchCmd
. command ( 'list' )
. description ( 'List branches in a repository' )
. requiredOption ( '-o, --owner <owner>' , 'repository owner' )
. requiredOption ( '-r, --repo <repo>' , 'repository name' )
Audit: fix auth/config bugs, add issue/pr create, tests and docs
Fixes found during a full audit of the CLI:
- auth logout: remote token revocation always failed with 401 because
Forgejo only accepts Basic auth on the token endpoints. Logout now
asks for (or accepts) the account password, supports --password,
--password-file and --local-only, and clearly reports when the token
is left active.
- Silent password prompt actually echoed the password on a TTY:
overriding rl.write does not suppress readline echo. Switched to the
callback readline module and mute _writeToOutput instead (the
readline/promises interface does not honor that hook).
- Global --config flag was silently ignored: config paths were resolved
at require time, before the preAction hook set STOKE_CONFIG_FILE.
Paths are now resolved lazily on every access.
- XDG_CONFIG_HOME handling put the config in $XDG_CONFIG_HOME/.config/stoke;
per the XDG spec it now resolves to $XDG_CONFIG_HOME/stoke.
- repo create: --auto-init defaulted to true with no way to disable it;
added --no-auto-init.
- repo import/import-batch: a GitHub token was required even for
non-GitHub services (e.g. --service git), making those imports fail
without gh/GITHUB_TOKEN. Tokens are now only auto-resolved for the
github service; batch imports resolve per entry and memoize.
- Branding leftovers: 'Run: forgejo auth login' hint and
forgejo-cli/1.0.0 User-Agent now say stoke (UA tracks pkg.version).
- Added request timeouts (30s default, 10m for migrations).
- --limit and --team-id are validated as integers instead of silently
misbehaving on garbage (NaN made -l show all results).
New commands (per the repo's every-operation-becomes-a-command design):
- stoke issue create (title/body/body-file/assignees)
- stoke pr create (head/base/title/body/body-file)
Tests and metadata:
- New test suite on the built-in node:test runner (25 tests) covering
config resolution/persistence, the API client with a mocked fetch,
and end-to-end CLI behavior. npm test previously matched no files.
- package.json: engines >=22.12.0 (required by commander@15 — the
README claimed Node 18), repository, keywords, author; version 1.1.0.
- README: corrected Node requirement, documented repo rename (was
missing), issue create, pr create, logout options and revocation
caveat, --no-auto-init, XDG behavior, import token rules, testing.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-22 19:28:23 +00:00
. option ( '-l, --limit <number>' , 'maximum branches to return (0 for all)' , parseLimit , 50 )
2026-07-22 15:03:32 +00:00
. action ( async ( options ) => {
try {
const config = loadConfig ( ) ;
const client = ForgejoClient . fromConfig ( config ) ;
const branches = await client . listBranches ( options . owner , options . repo ) ;
Audit: fix auth/config bugs, add issue/pr create, tests and docs
Fixes found during a full audit of the CLI:
- auth logout: remote token revocation always failed with 401 because
Forgejo only accepts Basic auth on the token endpoints. Logout now
asks for (or accepts) the account password, supports --password,
--password-file and --local-only, and clearly reports when the token
is left active.
- Silent password prompt actually echoed the password on a TTY:
overriding rl.write does not suppress readline echo. Switched to the
callback readline module and mute _writeToOutput instead (the
readline/promises interface does not honor that hook).
- Global --config flag was silently ignored: config paths were resolved
at require time, before the preAction hook set STOKE_CONFIG_FILE.
Paths are now resolved lazily on every access.
- XDG_CONFIG_HOME handling put the config in $XDG_CONFIG_HOME/.config/stoke;
per the XDG spec it now resolves to $XDG_CONFIG_HOME/stoke.
- repo create: --auto-init defaulted to true with no way to disable it;
added --no-auto-init.
- repo import/import-batch: a GitHub token was required even for
non-GitHub services (e.g. --service git), making those imports fail
without gh/GITHUB_TOKEN. Tokens are now only auto-resolved for the
github service; batch imports resolve per entry and memoize.
- Branding leftovers: 'Run: forgejo auth login' hint and
forgejo-cli/1.0.0 User-Agent now say stoke (UA tracks pkg.version).
- Added request timeouts (30s default, 10m for migrations).
- --limit and --team-id are validated as integers instead of silently
misbehaving on garbage (NaN made -l show all results).
New commands (per the repo's every-operation-becomes-a-command design):
- stoke issue create (title/body/body-file/assignees)
- stoke pr create (head/base/title/body/body-file)
Tests and metadata:
- New test suite on the built-in node:test runner (25 tests) covering
config resolution/persistence, the API client with a mocked fetch,
and end-to-end CLI behavior. npm test previously matched no files.
- package.json: engines >=22.12.0 (required by commander@15 — the
README claimed Node 18), repository, keywords, author; version 1.1.0.
- README: corrected Node requirement, documented repo rename (was
missing), issue create, pr create, logout options and revocation
caveat, --no-auto-init, XDG behavior, import token rules, testing.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-22 19:28:23 +00:00
const display = options . limit > 0 ? branches . slice ( 0 , options . limit ) : branches ;
2026-07-22 15:03:32 +00:00
if ( ! display . length ) {
console . log ( 'No branches found.' ) ;
return ;
}
for ( const b of display ) {
console . log ( b . name ) ;
}
if ( branches . length > display . length ) {
console . log ( ` ...and ${ branches . length - display . length } more (use -l 0 for all). ` ) ;
}
} catch ( err ) {
console . error ( ` Failed to list branches: ${ err . message } ` ) ;
process . exit ( 1 ) ;
}
} ) ;
2026-07-22 15:06:35 +00:00
const collaborator = program
. command ( 'collaborator' )
. description ( 'Manage repository collaborators' ) ;
collaborator
. command ( 'add' )
. description ( 'Add a collaborator to a repository' )
. requiredOption ( '-o, --owner <owner>' , 'repository owner' )
. requiredOption ( '-r, --repo <repo>' , 'repository name' )
. requiredOption ( '-u, --user <username>' , 'username of the collaborator' )
. option ( '--permission <permission>' , 'permission level: read, write, admin' , 'write' )
. action ( async ( options ) => {
try {
const config = loadConfig ( ) ;
const client = ForgejoClient . fromConfig ( config ) ;
await client . addCollaborator ( options . owner , options . repo , options . user , options . permission ) ;
console . log ( ` Added ${ options . user } as ${ options . permission } collaborator to ${ options . owner } / ${ options . repo } . ` ) ;
} catch ( err ) {
console . error ( ` Failed to add collaborator: ${ err . message } ` ) ;
if ( err . status ) console . error ( ` HTTP status: ${ err . status } ` ) ;
process . exit ( 1 ) ;
}
} ) ;
2026-07-22 17:47:48 +00:00
const org = program
. command ( 'org' )
. description ( 'Manage organizations' ) ;
org
. command ( 'create' )
. description ( 'Create a new organization' )
. requiredOption ( '--name <name>' , 'organization username (short name used in URLs)' )
. option ( '--full-name <full-name>' , 'display name of the organization' , '' )
. option ( '-d, --description <description>' , 'organization description' , '' )
. option ( '--website <website>' , 'organization website' , '' )
. option ( '--location <location>' , 'organization location' , '' )
. option ( '--visibility <visibility>' , 'visibility: public, limited, private' , 'public' )
. action ( async ( options ) => {
try {
const config = loadConfig ( ) ;
const client = ForgejoClient . fromConfig ( config ) ;
const payload = {
username : options . name ,
full _name : options . fullName ,
description : options . description ,
website : options . website ,
location : options . location ,
visibility : options . visibility ,
} ;
const result = await client . createOrg ( payload ) ;
console . log ( ` Organization created: ${ result . username } ` ) ;
if ( result . full _name ) console . log ( ` Full name: ${ result . full _name } ` ) ;
console . log ( ` URL: ${ config . url } / ${ result . username } ` ) ;
} catch ( err ) {
console . error ( ` Organization creation failed: ${ err . message } ` ) ;
if ( err . status ) console . error ( ` HTTP status: ${ err . status } ` ) ;
process . exit ( 1 ) ;
}
} ) ;
org
. command ( 'repos' )
. description ( 'List repositories owned by an organization' )
. requiredOption ( '-o, --org <org>' , 'organization name' )
Audit: fix auth/config bugs, add issue/pr create, tests and docs
Fixes found during a full audit of the CLI:
- auth logout: remote token revocation always failed with 401 because
Forgejo only accepts Basic auth on the token endpoints. Logout now
asks for (or accepts) the account password, supports --password,
--password-file and --local-only, and clearly reports when the token
is left active.
- Silent password prompt actually echoed the password on a TTY:
overriding rl.write does not suppress readline echo. Switched to the
callback readline module and mute _writeToOutput instead (the
readline/promises interface does not honor that hook).
- Global --config flag was silently ignored: config paths were resolved
at require time, before the preAction hook set STOKE_CONFIG_FILE.
Paths are now resolved lazily on every access.
- XDG_CONFIG_HOME handling put the config in $XDG_CONFIG_HOME/.config/stoke;
per the XDG spec it now resolves to $XDG_CONFIG_HOME/stoke.
- repo create: --auto-init defaulted to true with no way to disable it;
added --no-auto-init.
- repo import/import-batch: a GitHub token was required even for
non-GitHub services (e.g. --service git), making those imports fail
without gh/GITHUB_TOKEN. Tokens are now only auto-resolved for the
github service; batch imports resolve per entry and memoize.
- Branding leftovers: 'Run: forgejo auth login' hint and
forgejo-cli/1.0.0 User-Agent now say stoke (UA tracks pkg.version).
- Added request timeouts (30s default, 10m for migrations).
- --limit and --team-id are validated as integers instead of silently
misbehaving on garbage (NaN made -l show all results).
New commands (per the repo's every-operation-becomes-a-command design):
- stoke issue create (title/body/body-file/assignees)
- stoke pr create (head/base/title/body/body-file)
Tests and metadata:
- New test suite on the built-in node:test runner (25 tests) covering
config resolution/persistence, the API client with a mocked fetch,
and end-to-end CLI behavior. npm test previously matched no files.
- package.json: engines >=22.12.0 (required by commander@15 — the
README claimed Node 18), repository, keywords, author; version 1.1.0.
- README: corrected Node requirement, documented repo rename (was
missing), issue create, pr create, logout options and revocation
caveat, --no-auto-init, XDG behavior, import token rules, testing.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-22 19:28:23 +00:00
. option ( '-l, --limit <number>' , 'maximum repositories to return (0 for all)' , parseLimit , 50 )
2026-07-22 17:47:48 +00:00
. action ( async ( options ) => {
try {
const config = loadConfig ( ) ;
const client = ForgejoClient . fromConfig ( config ) ;
const repos = await client . listOrgRepos ( options . org ) ;
Audit: fix auth/config bugs, add issue/pr create, tests and docs
Fixes found during a full audit of the CLI:
- auth logout: remote token revocation always failed with 401 because
Forgejo only accepts Basic auth on the token endpoints. Logout now
asks for (or accepts) the account password, supports --password,
--password-file and --local-only, and clearly reports when the token
is left active.
- Silent password prompt actually echoed the password on a TTY:
overriding rl.write does not suppress readline echo. Switched to the
callback readline module and mute _writeToOutput instead (the
readline/promises interface does not honor that hook).
- Global --config flag was silently ignored: config paths were resolved
at require time, before the preAction hook set STOKE_CONFIG_FILE.
Paths are now resolved lazily on every access.
- XDG_CONFIG_HOME handling put the config in $XDG_CONFIG_HOME/.config/stoke;
per the XDG spec it now resolves to $XDG_CONFIG_HOME/stoke.
- repo create: --auto-init defaulted to true with no way to disable it;
added --no-auto-init.
- repo import/import-batch: a GitHub token was required even for
non-GitHub services (e.g. --service git), making those imports fail
without gh/GITHUB_TOKEN. Tokens are now only auto-resolved for the
github service; batch imports resolve per entry and memoize.
- Branding leftovers: 'Run: forgejo auth login' hint and
forgejo-cli/1.0.0 User-Agent now say stoke (UA tracks pkg.version).
- Added request timeouts (30s default, 10m for migrations).
- --limit and --team-id are validated as integers instead of silently
misbehaving on garbage (NaN made -l show all results).
New commands (per the repo's every-operation-becomes-a-command design):
- stoke issue create (title/body/body-file/assignees)
- stoke pr create (head/base/title/body/body-file)
Tests and metadata:
- New test suite on the built-in node:test runner (25 tests) covering
config resolution/persistence, the API client with a mocked fetch,
and end-to-end CLI behavior. npm test previously matched no files.
- package.json: engines >=22.12.0 (required by commander@15 — the
README claimed Node 18), repository, keywords, author; version 1.1.0.
- README: corrected Node requirement, documented repo rename (was
missing), issue create, pr create, logout options and revocation
caveat, --no-auto-init, XDG behavior, import token rules, testing.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-22 19:28:23 +00:00
const display = options . limit > 0 ? repos . slice ( 0 , options . limit ) : repos ;
2026-07-22 17:47:48 +00:00
if ( ! display . length ) {
console . log ( 'No repositories found.' ) ;
return ;
}
for ( const r of display ) {
const vis = r . private ? 'private' : 'public' ;
console . log ( ` ${ r . full _name } [ ${ vis } ] ${ r . html _url } ` ) ;
}
if ( repos . length > display . length ) {
console . log ( ` ...and ${ repos . length - display . length } more (use -l 0 for all). ` ) ;
}
} catch ( err ) {
console . error ( ` Failed to list organization repositories: ${ err . message } ` ) ;
process . exit ( 1 ) ;
}
} ) ;
org
. command ( 'avatar' )
. description ( 'Set the avatar (logo) of an organization from an image file' )
. requiredOption ( '-o, --org <org>' , 'organization name' )
. requiredOption ( '-f, --file <path>' , 'path to the image file (png, jpeg, gif, ...)' )
. action ( async ( options ) => {
try {
const config = loadConfig ( ) ;
const client = ForgejoClient . fromConfig ( config ) ;
let image ;
try {
image = fs . readFileSync ( options . file ) ;
} catch ( err ) {
throw new Error ( ` Could not read image file ${ options . file } : ${ err . message } ` ) ;
}
await client . updateOrgAvatar ( options . org , image . toString ( 'base64' ) ) ;
console . log ( ` Avatar updated for organization ${ options . org } . ` ) ;
} catch ( err ) {
console . error ( ` Failed to update organization avatar: ${ err . message } ` ) ;
if ( err . status ) console . error ( ` HTTP status: ${ err . status } ` ) ;
process . exit ( 1 ) ;
}
} ) ;
2026-07-22 17:56:14 +00:00
const team = org
. command ( 'team' )
. description ( 'Manage organization teams' ) ;
team
. command ( 'list' )
. description ( 'List teams in an organization' )
. requiredOption ( '-o, --org <org>' , 'organization name' )
. action ( async ( options ) => {
try {
const config = loadConfig ( ) ;
const client = ForgejoClient . fromConfig ( config ) ;
const teams = await client . listOrgTeams ( options . org ) ;
if ( ! teams . length ) {
console . log ( 'No teams found.' ) ;
return ;
}
for ( const t of teams ) {
console . log ( ` # ${ t . id } ${ t . name } [ ${ t . permission } ] ` ) ;
}
} catch ( err ) {
console . error ( ` Failed to list teams: ${ err . message } ` ) ;
process . exit ( 1 ) ;
}
} ) ;
team
. command ( 'create' )
. description ( 'Create a team in an organization' )
. requiredOption ( '-o, --org <org>' , 'organization name' )
. requiredOption ( '--name <name>' , 'team name' )
. option ( '-d, --description <description>' , 'team description' , '' )
. option ( '--permission <permission>' , 'permission level: read, write, admin' , 'read' )
. option ( '--all-repos' , 'grant access to all current and future organization repositories' , false )
. option ( '--can-create-repo' , 'allow members to create repositories in the organization' , false )
. option ( '--units <units>' , 'comma-separated team units' , 'repo.code,repo.issues,repo.pulls,repo.releases,repo.wiki,repo.projects' )
. action ( async ( options ) => {
try {
const config = loadConfig ( ) ;
const client = ForgejoClient . fromConfig ( config ) ;
const payload = {
name : options . name ,
description : options . description ,
permission : options . permission ,
includes _all _repositories : options . allRepos ,
can _create _org _repo : options . canCreateRepo ,
units : options . units . split ( ',' ) . map ( ( u ) => u . trim ( ) ) . filter ( Boolean ) ,
} ;
const result = await client . createTeam ( options . org , payload ) ;
console . log ( ` Team created: # ${ result . id } ${ result . name } [ ${ result . permission } ] ` ) ;
} catch ( err ) {
console . error ( ` Team creation failed: ${ err . message } ` ) ;
if ( err . status ) console . error ( ` HTTP status: ${ err . status } ` ) ;
process . exit ( 1 ) ;
}
} ) ;
team
. command ( 'member-list' )
. description ( 'List members of a team' )
Audit: fix auth/config bugs, add issue/pr create, tests and docs
Fixes found during a full audit of the CLI:
- auth logout: remote token revocation always failed with 401 because
Forgejo only accepts Basic auth on the token endpoints. Logout now
asks for (or accepts) the account password, supports --password,
--password-file and --local-only, and clearly reports when the token
is left active.
- Silent password prompt actually echoed the password on a TTY:
overriding rl.write does not suppress readline echo. Switched to the
callback readline module and mute _writeToOutput instead (the
readline/promises interface does not honor that hook).
- Global --config flag was silently ignored: config paths were resolved
at require time, before the preAction hook set STOKE_CONFIG_FILE.
Paths are now resolved lazily on every access.
- XDG_CONFIG_HOME handling put the config in $XDG_CONFIG_HOME/.config/stoke;
per the XDG spec it now resolves to $XDG_CONFIG_HOME/stoke.
- repo create: --auto-init defaulted to true with no way to disable it;
added --no-auto-init.
- repo import/import-batch: a GitHub token was required even for
non-GitHub services (e.g. --service git), making those imports fail
without gh/GITHUB_TOKEN. Tokens are now only auto-resolved for the
github service; batch imports resolve per entry and memoize.
- Branding leftovers: 'Run: forgejo auth login' hint and
forgejo-cli/1.0.0 User-Agent now say stoke (UA tracks pkg.version).
- Added request timeouts (30s default, 10m for migrations).
- --limit and --team-id are validated as integers instead of silently
misbehaving on garbage (NaN made -l show all results).
New commands (per the repo's every-operation-becomes-a-command design):
- stoke issue create (title/body/body-file/assignees)
- stoke pr create (head/base/title/body/body-file)
Tests and metadata:
- New test suite on the built-in node:test runner (25 tests) covering
config resolution/persistence, the API client with a mocked fetch,
and end-to-end CLI behavior. npm test previously matched no files.
- package.json: engines >=22.12.0 (required by commander@15 — the
README claimed Node 18), repository, keywords, author; version 1.1.0.
- README: corrected Node requirement, documented repo rename (was
missing), issue create, pr create, logout options and revocation
caveat, --no-auto-init, XDG behavior, import token rules, testing.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-22 19:28:23 +00:00
. requiredOption ( '--team-id <id>' , 'team id (see `stoke org team list`)' , parseId )
2026-07-22 17:56:14 +00:00
. action ( async ( options ) => {
try {
const config = loadConfig ( ) ;
const client = ForgejoClient . fromConfig ( config ) ;
Audit: fix auth/config bugs, add issue/pr create, tests and docs
Fixes found during a full audit of the CLI:
- auth logout: remote token revocation always failed with 401 because
Forgejo only accepts Basic auth on the token endpoints. Logout now
asks for (or accepts) the account password, supports --password,
--password-file and --local-only, and clearly reports when the token
is left active.
- Silent password prompt actually echoed the password on a TTY:
overriding rl.write does not suppress readline echo. Switched to the
callback readline module and mute _writeToOutput instead (the
readline/promises interface does not honor that hook).
- Global --config flag was silently ignored: config paths were resolved
at require time, before the preAction hook set STOKE_CONFIG_FILE.
Paths are now resolved lazily on every access.
- XDG_CONFIG_HOME handling put the config in $XDG_CONFIG_HOME/.config/stoke;
per the XDG spec it now resolves to $XDG_CONFIG_HOME/stoke.
- repo create: --auto-init defaulted to true with no way to disable it;
added --no-auto-init.
- repo import/import-batch: a GitHub token was required even for
non-GitHub services (e.g. --service git), making those imports fail
without gh/GITHUB_TOKEN. Tokens are now only auto-resolved for the
github service; batch imports resolve per entry and memoize.
- Branding leftovers: 'Run: forgejo auth login' hint and
forgejo-cli/1.0.0 User-Agent now say stoke (UA tracks pkg.version).
- Added request timeouts (30s default, 10m for migrations).
- --limit and --team-id are validated as integers instead of silently
misbehaving on garbage (NaN made -l show all results).
New commands (per the repo's every-operation-becomes-a-command design):
- stoke issue create (title/body/body-file/assignees)
- stoke pr create (head/base/title/body/body-file)
Tests and metadata:
- New test suite on the built-in node:test runner (25 tests) covering
config resolution/persistence, the API client with a mocked fetch,
and end-to-end CLI behavior. npm test previously matched no files.
- package.json: engines >=22.12.0 (required by commander@15 — the
README claimed Node 18), repository, keywords, author; version 1.1.0.
- README: corrected Node requirement, documented repo rename (was
missing), issue create, pr create, logout options and revocation
caveat, --no-auto-init, XDG behavior, import token rules, testing.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-22 19:28:23 +00:00
const members = await client . listTeamMembers ( options . teamId ) ;
2026-07-22 17:56:14 +00:00
if ( ! members . length ) {
console . log ( 'No members found.' ) ;
return ;
}
for ( const m of members ) {
const name = m . full _name ? ` ( ${ m . full _name } ) ` : '' ;
console . log ( ` ${ m . login } ${ name } ` ) ;
}
} catch ( err ) {
console . error ( ` Failed to list team members: ${ err . message } ` ) ;
process . exit ( 1 ) ;
}
} ) ;
team
. command ( 'member-add' )
. description ( 'Add a user to a team' )
Audit: fix auth/config bugs, add issue/pr create, tests and docs
Fixes found during a full audit of the CLI:
- auth logout: remote token revocation always failed with 401 because
Forgejo only accepts Basic auth on the token endpoints. Logout now
asks for (or accepts) the account password, supports --password,
--password-file and --local-only, and clearly reports when the token
is left active.
- Silent password prompt actually echoed the password on a TTY:
overriding rl.write does not suppress readline echo. Switched to the
callback readline module and mute _writeToOutput instead (the
readline/promises interface does not honor that hook).
- Global --config flag was silently ignored: config paths were resolved
at require time, before the preAction hook set STOKE_CONFIG_FILE.
Paths are now resolved lazily on every access.
- XDG_CONFIG_HOME handling put the config in $XDG_CONFIG_HOME/.config/stoke;
per the XDG spec it now resolves to $XDG_CONFIG_HOME/stoke.
- repo create: --auto-init defaulted to true with no way to disable it;
added --no-auto-init.
- repo import/import-batch: a GitHub token was required even for
non-GitHub services (e.g. --service git), making those imports fail
without gh/GITHUB_TOKEN. Tokens are now only auto-resolved for the
github service; batch imports resolve per entry and memoize.
- Branding leftovers: 'Run: forgejo auth login' hint and
forgejo-cli/1.0.0 User-Agent now say stoke (UA tracks pkg.version).
- Added request timeouts (30s default, 10m for migrations).
- --limit and --team-id are validated as integers instead of silently
misbehaving on garbage (NaN made -l show all results).
New commands (per the repo's every-operation-becomes-a-command design):
- stoke issue create (title/body/body-file/assignees)
- stoke pr create (head/base/title/body/body-file)
Tests and metadata:
- New test suite on the built-in node:test runner (25 tests) covering
config resolution/persistence, the API client with a mocked fetch,
and end-to-end CLI behavior. npm test previously matched no files.
- package.json: engines >=22.12.0 (required by commander@15 — the
README claimed Node 18), repository, keywords, author; version 1.1.0.
- README: corrected Node requirement, documented repo rename (was
missing), issue create, pr create, logout options and revocation
caveat, --no-auto-init, XDG behavior, import token rules, testing.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-22 19:28:23 +00:00
. requiredOption ( '--team-id <id>' , 'team id (see `stoke org team list`)' , parseId )
2026-07-22 17:56:14 +00:00
. requiredOption ( '-u, --user <username>' , 'username to add' )
. action ( async ( options ) => {
try {
const config = loadConfig ( ) ;
const client = ForgejoClient . fromConfig ( config ) ;
Audit: fix auth/config bugs, add issue/pr create, tests and docs
Fixes found during a full audit of the CLI:
- auth logout: remote token revocation always failed with 401 because
Forgejo only accepts Basic auth on the token endpoints. Logout now
asks for (or accepts) the account password, supports --password,
--password-file and --local-only, and clearly reports when the token
is left active.
- Silent password prompt actually echoed the password on a TTY:
overriding rl.write does not suppress readline echo. Switched to the
callback readline module and mute _writeToOutput instead (the
readline/promises interface does not honor that hook).
- Global --config flag was silently ignored: config paths were resolved
at require time, before the preAction hook set STOKE_CONFIG_FILE.
Paths are now resolved lazily on every access.
- XDG_CONFIG_HOME handling put the config in $XDG_CONFIG_HOME/.config/stoke;
per the XDG spec it now resolves to $XDG_CONFIG_HOME/stoke.
- repo create: --auto-init defaulted to true with no way to disable it;
added --no-auto-init.
- repo import/import-batch: a GitHub token was required even for
non-GitHub services (e.g. --service git), making those imports fail
without gh/GITHUB_TOKEN. Tokens are now only auto-resolved for the
github service; batch imports resolve per entry and memoize.
- Branding leftovers: 'Run: forgejo auth login' hint and
forgejo-cli/1.0.0 User-Agent now say stoke (UA tracks pkg.version).
- Added request timeouts (30s default, 10m for migrations).
- --limit and --team-id are validated as integers instead of silently
misbehaving on garbage (NaN made -l show all results).
New commands (per the repo's every-operation-becomes-a-command design):
- stoke issue create (title/body/body-file/assignees)
- stoke pr create (head/base/title/body/body-file)
Tests and metadata:
- New test suite on the built-in node:test runner (25 tests) covering
config resolution/persistence, the API client with a mocked fetch,
and end-to-end CLI behavior. npm test previously matched no files.
- package.json: engines >=22.12.0 (required by commander@15 — the
README claimed Node 18), repository, keywords, author; version 1.1.0.
- README: corrected Node requirement, documented repo rename (was
missing), issue create, pr create, logout options and revocation
caveat, --no-auto-init, XDG behavior, import token rules, testing.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-22 19:28:23 +00:00
await client . addTeamMember ( options . teamId , options . user ) ;
2026-07-22 17:56:14 +00:00
console . log ( ` Added ${ options . user } to team # ${ options . teamId } . ` ) ;
} catch ( err ) {
console . error ( ` Failed to add team member: ${ err . message } ` ) ;
if ( err . status ) console . error ( ` HTTP status: ${ err . status } ` ) ;
process . exit ( 1 ) ;
}
} ) ;
team
. command ( 'member-remove' )
. description ( 'Remove a user from a team' )
Audit: fix auth/config bugs, add issue/pr create, tests and docs
Fixes found during a full audit of the CLI:
- auth logout: remote token revocation always failed with 401 because
Forgejo only accepts Basic auth on the token endpoints. Logout now
asks for (or accepts) the account password, supports --password,
--password-file and --local-only, and clearly reports when the token
is left active.
- Silent password prompt actually echoed the password on a TTY:
overriding rl.write does not suppress readline echo. Switched to the
callback readline module and mute _writeToOutput instead (the
readline/promises interface does not honor that hook).
- Global --config flag was silently ignored: config paths were resolved
at require time, before the preAction hook set STOKE_CONFIG_FILE.
Paths are now resolved lazily on every access.
- XDG_CONFIG_HOME handling put the config in $XDG_CONFIG_HOME/.config/stoke;
per the XDG spec it now resolves to $XDG_CONFIG_HOME/stoke.
- repo create: --auto-init defaulted to true with no way to disable it;
added --no-auto-init.
- repo import/import-batch: a GitHub token was required even for
non-GitHub services (e.g. --service git), making those imports fail
without gh/GITHUB_TOKEN. Tokens are now only auto-resolved for the
github service; batch imports resolve per entry and memoize.
- Branding leftovers: 'Run: forgejo auth login' hint and
forgejo-cli/1.0.0 User-Agent now say stoke (UA tracks pkg.version).
- Added request timeouts (30s default, 10m for migrations).
- --limit and --team-id are validated as integers instead of silently
misbehaving on garbage (NaN made -l show all results).
New commands (per the repo's every-operation-becomes-a-command design):
- stoke issue create (title/body/body-file/assignees)
- stoke pr create (head/base/title/body/body-file)
Tests and metadata:
- New test suite on the built-in node:test runner (25 tests) covering
config resolution/persistence, the API client with a mocked fetch,
and end-to-end CLI behavior. npm test previously matched no files.
- package.json: engines >=22.12.0 (required by commander@15 — the
README claimed Node 18), repository, keywords, author; version 1.1.0.
- README: corrected Node requirement, documented repo rename (was
missing), issue create, pr create, logout options and revocation
caveat, --no-auto-init, XDG behavior, import token rules, testing.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-22 19:28:23 +00:00
. requiredOption ( '--team-id <id>' , 'team id (see `stoke org team list`)' , parseId )
2026-07-22 17:56:14 +00:00
. requiredOption ( '-u, --user <username>' , 'username to remove' )
. action ( async ( options ) => {
try {
const config = loadConfig ( ) ;
const client = ForgejoClient . fromConfig ( config ) ;
Audit: fix auth/config bugs, add issue/pr create, tests and docs
Fixes found during a full audit of the CLI:
- auth logout: remote token revocation always failed with 401 because
Forgejo only accepts Basic auth on the token endpoints. Logout now
asks for (or accepts) the account password, supports --password,
--password-file and --local-only, and clearly reports when the token
is left active.
- Silent password prompt actually echoed the password on a TTY:
overriding rl.write does not suppress readline echo. Switched to the
callback readline module and mute _writeToOutput instead (the
readline/promises interface does not honor that hook).
- Global --config flag was silently ignored: config paths were resolved
at require time, before the preAction hook set STOKE_CONFIG_FILE.
Paths are now resolved lazily on every access.
- XDG_CONFIG_HOME handling put the config in $XDG_CONFIG_HOME/.config/stoke;
per the XDG spec it now resolves to $XDG_CONFIG_HOME/stoke.
- repo create: --auto-init defaulted to true with no way to disable it;
added --no-auto-init.
- repo import/import-batch: a GitHub token was required even for
non-GitHub services (e.g. --service git), making those imports fail
without gh/GITHUB_TOKEN. Tokens are now only auto-resolved for the
github service; batch imports resolve per entry and memoize.
- Branding leftovers: 'Run: forgejo auth login' hint and
forgejo-cli/1.0.0 User-Agent now say stoke (UA tracks pkg.version).
- Added request timeouts (30s default, 10m for migrations).
- --limit and --team-id are validated as integers instead of silently
misbehaving on garbage (NaN made -l show all results).
New commands (per the repo's every-operation-becomes-a-command design):
- stoke issue create (title/body/body-file/assignees)
- stoke pr create (head/base/title/body/body-file)
Tests and metadata:
- New test suite on the built-in node:test runner (25 tests) covering
config resolution/persistence, the API client with a mocked fetch,
and end-to-end CLI behavior. npm test previously matched no files.
- package.json: engines >=22.12.0 (required by commander@15 — the
README claimed Node 18), repository, keywords, author; version 1.1.0.
- README: corrected Node requirement, documented repo rename (was
missing), issue create, pr create, logout options and revocation
caveat, --no-auto-init, XDG behavior, import token rules, testing.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-22 19:28:23 +00:00
await client . removeTeamMember ( options . teamId , options . user ) ;
2026-07-22 17:56:14 +00:00
console . log ( ` Removed ${ options . user } from team # ${ options . teamId } . ` ) ;
} catch ( err ) {
console . error ( ` Failed to remove team member: ${ err . message } ` ) ;
if ( err . status ) console . error ( ` HTTP status: ${ err . status } ` ) ;
process . exit ( 1 ) ;
}
} ) ;
const user = program
. command ( 'user' )
. description ( 'Manage users' ) ;
user
. command ( 'list' )
. description ( 'Search/list users on the Forgejo instance' )
. option ( '-q, --query <query>' , 'search query (empty lists all visible users)' , '' )
Audit: fix auth/config bugs, add issue/pr create, tests and docs
Fixes found during a full audit of the CLI:
- auth logout: remote token revocation always failed with 401 because
Forgejo only accepts Basic auth on the token endpoints. Logout now
asks for (or accepts) the account password, supports --password,
--password-file and --local-only, and clearly reports when the token
is left active.
- Silent password prompt actually echoed the password on a TTY:
overriding rl.write does not suppress readline echo. Switched to the
callback readline module and mute _writeToOutput instead (the
readline/promises interface does not honor that hook).
- Global --config flag was silently ignored: config paths were resolved
at require time, before the preAction hook set STOKE_CONFIG_FILE.
Paths are now resolved lazily on every access.
- XDG_CONFIG_HOME handling put the config in $XDG_CONFIG_HOME/.config/stoke;
per the XDG spec it now resolves to $XDG_CONFIG_HOME/stoke.
- repo create: --auto-init defaulted to true with no way to disable it;
added --no-auto-init.
- repo import/import-batch: a GitHub token was required even for
non-GitHub services (e.g. --service git), making those imports fail
without gh/GITHUB_TOKEN. Tokens are now only auto-resolved for the
github service; batch imports resolve per entry and memoize.
- Branding leftovers: 'Run: forgejo auth login' hint and
forgejo-cli/1.0.0 User-Agent now say stoke (UA tracks pkg.version).
- Added request timeouts (30s default, 10m for migrations).
- --limit and --team-id are validated as integers instead of silently
misbehaving on garbage (NaN made -l show all results).
New commands (per the repo's every-operation-becomes-a-command design):
- stoke issue create (title/body/body-file/assignees)
- stoke pr create (head/base/title/body/body-file)
Tests and metadata:
- New test suite on the built-in node:test runner (25 tests) covering
config resolution/persistence, the API client with a mocked fetch,
and end-to-end CLI behavior. npm test previously matched no files.
- package.json: engines >=22.12.0 (required by commander@15 — the
README claimed Node 18), repository, keywords, author; version 1.1.0.
- README: corrected Node requirement, documented repo rename (was
missing), issue create, pr create, logout options and revocation
caveat, --no-auto-init, XDG behavior, import token rules, testing.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-22 19:28:23 +00:00
. option ( '-l, --limit <number>' , 'maximum users to return (0 for all)' , parseLimit , 50 )
2026-07-22 17:56:14 +00:00
. action ( async ( options ) => {
try {
const config = loadConfig ( ) ;
const client = ForgejoClient . fromConfig ( config ) ;
const users = await client . searchUsers ( options . query ) ;
Audit: fix auth/config bugs, add issue/pr create, tests and docs
Fixes found during a full audit of the CLI:
- auth logout: remote token revocation always failed with 401 because
Forgejo only accepts Basic auth on the token endpoints. Logout now
asks for (or accepts) the account password, supports --password,
--password-file and --local-only, and clearly reports when the token
is left active.
- Silent password prompt actually echoed the password on a TTY:
overriding rl.write does not suppress readline echo. Switched to the
callback readline module and mute _writeToOutput instead (the
readline/promises interface does not honor that hook).
- Global --config flag was silently ignored: config paths were resolved
at require time, before the preAction hook set STOKE_CONFIG_FILE.
Paths are now resolved lazily on every access.
- XDG_CONFIG_HOME handling put the config in $XDG_CONFIG_HOME/.config/stoke;
per the XDG spec it now resolves to $XDG_CONFIG_HOME/stoke.
- repo create: --auto-init defaulted to true with no way to disable it;
added --no-auto-init.
- repo import/import-batch: a GitHub token was required even for
non-GitHub services (e.g. --service git), making those imports fail
without gh/GITHUB_TOKEN. Tokens are now only auto-resolved for the
github service; batch imports resolve per entry and memoize.
- Branding leftovers: 'Run: forgejo auth login' hint and
forgejo-cli/1.0.0 User-Agent now say stoke (UA tracks pkg.version).
- Added request timeouts (30s default, 10m for migrations).
- --limit and --team-id are validated as integers instead of silently
misbehaving on garbage (NaN made -l show all results).
New commands (per the repo's every-operation-becomes-a-command design):
- stoke issue create (title/body/body-file/assignees)
- stoke pr create (head/base/title/body/body-file)
Tests and metadata:
- New test suite on the built-in node:test runner (25 tests) covering
config resolution/persistence, the API client with a mocked fetch,
and end-to-end CLI behavior. npm test previously matched no files.
- package.json: engines >=22.12.0 (required by commander@15 — the
README claimed Node 18), repository, keywords, author; version 1.1.0.
- README: corrected Node requirement, documented repo rename (was
missing), issue create, pr create, logout options and revocation
caveat, --no-auto-init, XDG behavior, import token rules, testing.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-22 19:28:23 +00:00
const display = options . limit > 0 ? users . slice ( 0 , options . limit ) : users ;
2026-07-22 17:56:14 +00:00
if ( ! display . length ) {
console . log ( 'No users found.' ) ;
return ;
}
for ( const u of display ) {
const name = u . full _name ? ` ( ${ u . full _name } ) ` : '' ;
console . log ( ` ${ u . login } ${ name } ` ) ;
}
if ( users . length > display . length ) {
console . log ( ` ...and ${ users . length - display . length } more (use -l 0 for all). ` ) ;
}
} catch ( err ) {
console . error ( ` Failed to list users: ${ err . message } ` ) ;
process . exit ( 1 ) ;
}
} ) ;
user
. command ( 'show' )
. description ( 'Show a single user profile' )
. requiredOption ( '-u, --user <username>' , 'username to look up' )
. action ( async ( options ) => {
try {
const config = loadConfig ( ) ;
const client = ForgejoClient . fromConfig ( config ) ;
const u = await client . getUser ( options . user ) ;
console . log ( ` Login: ${ u . login } ` ) ;
console . log ( ` Full name: ${ u . full _name || '-' } ` ) ;
console . log ( ` Email: ${ u . email || '-' } ` ) ;
console . log ( ` URL: ${ u . html _url } ` ) ;
} catch ( err ) {
console . error ( ` Failed to show user: ${ err . message } ` ) ;
if ( err . status ) console . error ( ` HTTP status: ${ err . status } ` ) ;
process . exit ( 1 ) ;
}
} ) ;
2026-07-22 15:03:32 +00:00
program . parseAsync ( process . argv ) . catch ( ( err ) => {
console . error ( err ) ;
process . exit ( 1 ) ;
} ) ;