hive-forge reaction: add --list-allowed to surface the instance's configured shortcodes

This commit is contained in:
damocles 2026-08-18 12:56:54 +02:00 committed by mara
commit fb9cc5635a
4 changed files with 36 additions and 5 deletions

View file

@ -57,6 +57,7 @@ hive-forge issue reaction 42 # list #42's reactions
hive-forge issue reaction 42 add heart # react to #42 with :heart:
hive-forge issue reaction 42 --comment 18042 add +1 # react to a specific comment instead
hive-forge issue reaction 42 remove heart # remove your own :heart: reaction
hive-forge issue reaction 1 --list-allowed # this instance's actual configured shortcodes
hive-forge issue-create --title "..." --body "..."
hive-forge issue-create --title "..." --body "..." --label area/ops --label type/bug # repeatable
hive-forge issue-edit 42 --title "new title"
@ -299,7 +300,10 @@ to discover valid label names before triaging or to audit the label set.
resulting reaction list as JSON, same shape as `dependency`. `view`,
`issue`, `pr`, and `comment-show` always show a reaction summary;
`comments --show-reactions` adds one per comment shown (opt-in — one
extra request per comment, no server-side inline count).
extra request per comment, no server-side inline count). `reaction
<n> --list-allowed` prints the instance's actual configured shortcode
list (`GET /settings/ui`'s `allowed_reactions`) instead of guessing —
instance-global, `<n>`/`--comment` are ignored on this path.
- Do NOT use raw `curl` for forge access -- the CLI handles auth,
error checking, and output formatting.
- `issue-create --label <name>` / `pr-create --label <name>` are

View file

@ -89,7 +89,12 @@ pub fn run(client: &Client, args: Args) -> Result<()> {
verbs::dependency::run(client, a)
}
Cmd::Reaction(a) => {
assert_kind(client, a.number, Kind::Issue)?;
// `--list-allowed` is instance-global, not issue-scoped — skip
// the kind-assert rather than force a real issue number on a
// call that never uses it.
if !a.list_allowed {
assert_kind(client, a.number, Kind::Issue)?;
}
verbs::reaction::run(client, a)
}
Cmd::Timeline(a) => {

View file

@ -113,7 +113,12 @@ pub fn run(client: &Client, args: Args) -> Result<()> {
verbs::dependency::run(client, a)
}
Cmd::Reaction(a) => {
assert_kind(client, a.number, Kind::Pr)?;
// `--list-allowed` is instance-global, not PR-scoped — skip the
// kind-assert rather than force a real PR number on a call that
// never uses it.
if !a.list_allowed {
assert_kind(client, a.number, Kind::Pr)?;
}
verbs::reaction::run(client, a)
}
Cmd::Timeline(a) => {

View file

@ -13,7 +13,12 @@
//! `content` is Forgejo's reaction shortcode vocabulary (`+1`, `-1`,
//! `laugh`, `confused`, `heart`, `hooray`, `rocket`, `eyes`, …), not a
//! raw emoji character — same set the forge web UI's reaction picker
//! offers.
//! offers. `--list-allowed` prints the *actual* instance-configured
//! list (`GET /settings/ui`'s `allowed_reactions`) instead of leaving a
//! caller to guess it or learn it from a 403 — this is instance-global,
//! not issue/PR-scoped, so `number`/`--comment` are ignored when it's
//! set (the number is still required positionally, same as every other
//! generic verb, but unused on this path).
use anyhow::Result;
use clap::{Args as ClapArgs, Subcommand};
@ -24,13 +29,19 @@ use crate::verbs::{comment_reactions, issue_reactions, print_json};
#[derive(ClapArgs)]
pub struct Args {
/// Issue or PR number.
/// Issue or PR number. Ignored (but still required) when
/// `--list-allowed` is set.
pub(crate) number: u64,
/// Target a specific comment's reactions instead of the issue/PR
/// itself — the comment's own id (from `comments`/`comment-show`),
/// not its position in the thread.
#[arg(long)]
comment: Option<u64>,
/// Print the instance's actual configured reaction shortcodes
/// (`GET /settings/ui`) instead of listing/adding/removing —
/// instance-global, ignores `number`/`--comment`.
#[arg(long)]
pub(crate) list_allowed: bool,
#[command(subcommand)]
action: Option<Action>,
}
@ -46,6 +57,12 @@ enum Action {
}
pub fn run(client: &Client, args: Args) -> Result<()> {
if args.list_allowed {
let settings = client.api().get_general_ui_settings().send()?;
return print_json(&serde_json::json!(
settings.allowed_reactions.unwrap_or_default()
));
}
let (owner, name) = client.owner_repo()?;
match args.action.unwrap_or(Action::List) {
Action::List => {}