From fb9cc5635a5d0f15f9b9e13cab64ae0102bc5d64 Mon Sep 17 00:00:00 2001 From: damocles Date: Tue, 18 Aug 2026 12:56:54 +0200 Subject: [PATCH] hive-forge reaction: add --list-allowed to surface the instance's configured shortcodes --- docs/tools/forge.md | 6 +++++- hive-forge/src/verbs/issue_cmd.rs | 7 ++++++- hive-forge/src/verbs/pr_cmd.rs | 7 ++++++- hive-forge/src/verbs/reaction.rs | 21 +++++++++++++++++++-- 4 files changed, 36 insertions(+), 5 deletions(-) diff --git a/docs/tools/forge.md b/docs/tools/forge.md index dbebf681..e6fcdd0c 100644 --- a/docs/tools/forge.md +++ b/docs/tools/forge.md @@ -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 + --list-allowed` prints the instance's actual configured shortcode + list (`GET /settings/ui`'s `allowed_reactions`) instead of guessing — + instance-global, ``/`--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 ` / `pr-create --label ` are diff --git a/hive-forge/src/verbs/issue_cmd.rs b/hive-forge/src/verbs/issue_cmd.rs index 245e4c68..168ab2e3 100644 --- a/hive-forge/src/verbs/issue_cmd.rs +++ b/hive-forge/src/verbs/issue_cmd.rs @@ -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) => { diff --git a/hive-forge/src/verbs/pr_cmd.rs b/hive-forge/src/verbs/pr_cmd.rs index ef1eee4f..19574b38 100644 --- a/hive-forge/src/verbs/pr_cmd.rs +++ b/hive-forge/src/verbs/pr_cmd.rs @@ -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) => { diff --git a/hive-forge/src/verbs/reaction.rs b/hive-forge/src/verbs/reaction.rs index 2fa98005..bde19d12 100644 --- a/hive-forge/src/verbs/reaction.rs +++ b/hive-forge/src/verbs/reaction.rs @@ -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, + /// 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, } @@ -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 => {}