hive-forge: the renamed flat verbs now say what replaced them

Seven hidden aliases are pure renames — `pr status` does exactly what
`pr-status` did. They now refuse and name the replacement instead of
running, which is the deprecation step before removing them outright.

Deliberately not all of the hidden verbs. The other ten (`view`,
`comment`, `comments`, `close`, `reopen`, `assign`, `labels`, `timeline`,
`dependency`, `reaction`) exist as BOTH `issue <verb>` and `pr <verb>`,
so a caller holding only a number cannot be told which to run. Refusing
those removes a capability rather than renaming one, and there is no
message that would help; they keep working pending a decision on what
replaces them.

Measured: each of the seven exits 1 naming its replacement; `view` still
exits 0; `pr status` still returns its merge-readiness verdict (0 on a
ready PR, 1 on one with CI pending).

Refs #3974
This commit is contained in:
atlas 2026-09-02 17:24:56 +02:00 committed by mara
commit f293454df9

View file

@ -29,7 +29,7 @@ mod client;
mod notify; mod notify;
mod verbs; mod verbs;
use anyhow::{Context, Result}; use anyhow::{Context, Result, bail};
use clap::{Parser, Subcommand}; use clap::{Parser, Subcommand};
use std::process::ExitCode; use std::process::ExitCode;
@ -60,10 +60,18 @@ struct Cli {
#[derive(Subcommand)] #[derive(Subcommand)]
enum Verb { enum Verb {
// The kind verbs below are hidden back-compat aliases of the new // The hidden verbs below are back-compat aliases of the `pr <verb>` /
// `pr <verb>` / `issue <verb>` forms (`pr-close` -> `pr close`, bare // `issue <verb>` forms, and they are at two different stages:
// `close` -> `pr close` / `issue close`, etc.). They still parse but are //
// dropped from `--help`; a later change removes them once usage migrates. // - **Pure renames** (`pr-status`, `pr-create`, `pr-merge`,
// `pr-reviews`, `issue-create`, `issue-edit`, `diff`) now *refuse*
// and name their replacement — see `renamed`. They are removed
// outright in a later release.
// - **Kind-agnostic** (`view`, `comment`, `comments`, `close`,
// `assign`, `labels`, `timeline`, …) still work. Each exists as both
// `issue <verb>` and `pr <verb>`, so there is no single replacement
// to send a caller to when all they hold is a number — refusing
// these would remove a capability rather than rename one.
/// Dump title + body + all comments for an issue or PR. /// Dump title + body + all comments for an issue or PR.
#[command(hide = true)] #[command(hide = true)]
View(verbs::view::Args), View(verbs::view::Args),
@ -242,15 +250,34 @@ fn run() -> Result<()> {
} }
} }
/// Refuse a flat alias that has an exact scoped replacement, naming it.
///
/// Deprecation step for the aliases that are **pure renames**: `pr status`
/// does precisely what `pr-status` did, so the caller can be sent somewhere
/// specific. They still parse, only to say this — the variants come out
/// entirely in a later release.
///
/// The kind-agnostic aliases (`view`, `comments`, `close`, …) are
/// deliberately still working: those exist as *both* `issue <verb>` and
/// `pr <verb>`, so a caller holding only a number cannot be told which one
/// to run, and an error naming neither would be worse than the alias.
fn renamed(old: &str, new: &str) -> Result<()> {
bail!(
"`{old}` is now `{new}` — run `hive-forge {new}` instead. \
The old name still parses only to print this, and is removed in a \
later release."
)
}
fn dispatch(client: &client::Client, verb: Verb) -> Result<()> { fn dispatch(client: &client::Client, verb: Verb) -> Result<()> {
match verb { match verb {
Verb::View(a) => verbs::view::run(client, a), Verb::View(a) => verbs::view::run(client, a),
Verb::Issue(a) => verbs::issue_cmd::run(client, a), Verb::Issue(a) => verbs::issue_cmd::run(client, a),
Verb::IssueCreate(a) => verbs::issue_create::run(client, a), Verb::IssueCreate(_) => renamed("issue-create", "issue create"),
Verb::IssueEdit(a) => verbs::issue_edit::run(client, a), Verb::IssueEdit(_) => renamed("issue-edit", "issue edit"),
Verb::Pr(a) => verbs::pr_cmd::run(client, a), Verb::Pr(a) => verbs::pr_cmd::run(client, a),
Verb::PrCommits(a) => verbs::pr_commits::run(client, a), Verb::PrCommits(a) => verbs::pr_commits::run(client, a),
Verb::PrCreate(a) => verbs::pr_create::run(client, a), Verb::PrCreate(_) => renamed("pr-create", "pr create"),
Verb::Comment(a) => verbs::comment::run(client, a), Verb::Comment(a) => verbs::comment::run(client, a),
Verb::Comments(a) => verbs::comments::run(client, a), Verb::Comments(a) => verbs::comments::run(client, a),
Verb::CommentShow(a) => verbs::comment_show::run(client, a), Verb::CommentShow(a) => verbs::comment_show::run(client, a),
@ -258,7 +285,7 @@ fn dispatch(client: &client::Client, verb: Verb) -> Result<()> {
Verb::Assign(a) => verbs::assign::run(client, a), Verb::Assign(a) => verbs::assign::run(client, a),
Verb::Close(a) => verbs::close::run(client, a), Verb::Close(a) => verbs::close::run(client, a),
Verb::Labels(a) => verbs::labels::run(client, a), Verb::Labels(a) => verbs::labels::run(client, a),
Verb::PrStatus(a) => verbs::pr_status::run(client, a), Verb::PrStatus(_) => renamed("pr-status", "pr status"),
Verb::Clone(a) => verbs::clone::run(client, a), Verb::Clone(a) => verbs::clone::run(client, a),
Verb::RepoCreate(a) => verbs::repo_create::run(client, a), Verb::RepoCreate(a) => verbs::repo_create::run(client, a),
Verb::RepoAddCollaborator(a) => verbs::repo_add_collaborator::run(client, a), Verb::RepoAddCollaborator(a) => verbs::repo_add_collaborator::run(client, a),
@ -267,12 +294,12 @@ fn dispatch(client: &client::Client, verb: Verb) -> Result<()> {
Verb::Lint(a) => verbs::lint::run(client, a), Verb::Lint(a) => verbs::lint::run(client, a),
Verb::List(a) => verbs::list::run(client, a), Verb::List(a) => verbs::list::run(client, a),
Verb::Milestone(a) => verbs::milestone::run(client, a), Verb::Milestone(a) => verbs::milestone::run(client, a),
Verb::PrMerge(a) => verbs::pr_merge::run(client, a), Verb::PrMerge(_) => renamed("pr-merge", "pr merge"),
Verb::PrReviews(a) => verbs::pr_reviews::run(client, a), Verb::PrReviews(_) => renamed("pr-reviews", "pr reviews"),
Verb::PrAssignReviewer(a) => verbs::pr_assign_reviewer::run(client, a), Verb::PrAssignReviewer(a) => verbs::pr_assign_reviewer::run(client, a),
Verb::Branches(a) => verbs::branches::run(client, a), Verb::Branches(a) => verbs::branches::run(client, a),
Verb::TreeSha(a) => verbs::tree_sha::run(client, a), Verb::TreeSha(a) => verbs::tree_sha::run(client, a),
Verb::Diff(a) => verbs::diff::run(client, a), Verb::Diff(_) => renamed("diff", "pr diff"),
Verb::Subscription(a) => verbs::subscription::run(client, a), Verb::Subscription(a) => verbs::subscription::run(client, a),
Verb::Timeline(a) => verbs::timeline::run(client, a), Verb::Timeline(a) => verbs::timeline::run(client, a),
Verb::AttachIssue(a) => verbs::attach::run_issue(client, a), Verb::AttachIssue(a) => verbs::attach::run_issue(client, a),