From f293454df97c46eada9e2bb4c6bd8555d0a4c79f Mon Sep 17 00:00:00 2001 From: atlas Date: Wed, 2 Sep 2026 17:24:56 +0200 Subject: [PATCH] hive-forge: the renamed flat verbs now say what replaced them MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 ` and `pr `, 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 --- hive-forge/src/main.rs | 51 ++++++++++++++++++++++++++++++++---------- 1 file changed, 39 insertions(+), 12 deletions(-) diff --git a/hive-forge/src/main.rs b/hive-forge/src/main.rs index 1b5cdbfc..d423a665 100644 --- a/hive-forge/src/main.rs +++ b/hive-forge/src/main.rs @@ -29,7 +29,7 @@ mod client; mod notify; mod verbs; -use anyhow::{Context, Result}; +use anyhow::{Context, Result, bail}; use clap::{Parser, Subcommand}; use std::process::ExitCode; @@ -60,10 +60,18 @@ struct Cli { #[derive(Subcommand)] enum Verb { - // The kind verbs below are hidden back-compat aliases of the new - // `pr ` / `issue ` forms (`pr-close` -> `pr close`, bare - // `close` -> `pr close` / `issue close`, etc.). They still parse but are - // dropped from `--help`; a later change removes them once usage migrates. + // The hidden verbs below are back-compat aliases of the `pr ` / + // `issue ` forms, and they are at two different stages: + // + // - **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 ` and `pr `, 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. #[command(hide = true)] 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 ` and +/// `pr `, 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<()> { match verb { Verb::View(a) => verbs::view::run(client, a), Verb::Issue(a) => verbs::issue_cmd::run(client, a), - Verb::IssueCreate(a) => verbs::issue_create::run(client, a), - Verb::IssueEdit(a) => verbs::issue_edit::run(client, a), + Verb::IssueCreate(_) => renamed("issue-create", "issue create"), + Verb::IssueEdit(_) => renamed("issue-edit", "issue edit"), Verb::Pr(a) => verbs::pr_cmd::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::Comments(a) => verbs::comments::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::Close(a) => verbs::close::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::RepoCreate(a) => verbs::repo_create::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::List(a) => verbs::list::run(client, a), Verb::Milestone(a) => verbs::milestone::run(client, a), - Verb::PrMerge(a) => verbs::pr_merge::run(client, a), - Verb::PrReviews(a) => verbs::pr_reviews::run(client, a), + Verb::PrMerge(_) => renamed("pr-merge", "pr merge"), + Verb::PrReviews(_) => renamed("pr-reviews", "pr reviews"), Verb::PrAssignReviewer(a) => verbs::pr_assign_reviewer::run(client, a), Verb::Branches(a) => verbs::branches::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::Timeline(a) => verbs::timeline::run(client, a), Verb::AttachIssue(a) => verbs::attach::run_issue(client, a),