diff --git a/docs/4-implementation/agent-tools/forge.md b/docs/4-implementation/agent-tools/forge.md index d03829f09..0b4615d64 100644 --- a/docs/4-implementation/agent-tools/forge.md +++ b/docs/4-implementation/agent-tools/forge.md @@ -83,7 +83,7 @@ hive-forge pr timeline 42 # review rows list their line comment hive-forge pr reviews 42 --approve # submit APPROVED review hive-forge pr reviews 42 --request-changes -m "msg" # submit REQUEST_CHANGES review hive-forge pr reviews 42 --comment -m "msg" # submit COMMENT review -hive-forge pr reviews 42 --request-changes -m "msg" --at src/a.rs:12 "off by one" --at src/b.rs:3 "why?" # one review carrying line comments; LINE is the new-file line `pr diff --numbered` prints +hive-forge pr reviews 42 --request-changes -m "msg" --at src/a.rs:12 "off by one" --at src/b.rs:3 "why?" # one review carrying line comments; LINE is the new-file line `pr diff --numbered` prints; the text of `--at` and `-m` may start with a hyphen (a markdown bullet) hive-forge pr assign-reviewer 42 argus # set argus as a requested reviewer on the PR hive-forge pr assign-reviewer 42 argus --remove # withdraw the request; fails if argus is still listed afterwards (a reviewer who already reviewed stays listed) hive-forge pr assign-committer 42 damocles # add damocles to the PR's assignee list diff --git a/hive-forge/src/verbs/pr_reviews.rs b/hive-forge/src/verbs/pr_reviews.rs index 833ff3ee2..5c8daca41 100644 --- a/hive-forge/src/verbs/pr_reviews.rs +++ b/hive-forge/src/verbs/pr_reviews.rs @@ -30,7 +30,7 @@ pub struct Args { /// Optional body / message for the review (used with --approve, /// --request-changes, or --comment). - #[arg(long, short = 'm')] + #[arg(long, short = 'm', allow_hyphen_values = true)] body: Option, /// An inline comment on a line of the new file: `--at path:LINE "text"`. @@ -40,6 +40,7 @@ pub struct Args { #[arg( long, num_args = 2, + allow_hyphen_values = true, value_names = ["PATH:LINE", "TEXT"], action = clap::ArgAction::Append, conflicts_with = "only", @@ -398,6 +399,38 @@ mod tests { .collect() } + /// `Args` parsed the way the binary does, from `pr reviews`'s own argv. + fn parse(argv: &[&str]) -> Args { + #[derive(clap::Parser)] + struct Cli { + #[command(flatten)] + args: Args, + } + use clap::Parser as _; + Cli::try_parse_from(std::iter::once("reviews").chain(argv.iter().copied())) + .unwrap_or_else(|e| panic!("{argv:?}: {e}")) + .args + } + + #[test] + fn a_comment_or_body_may_start_with_a_hyphen() { + // A markdown bullet is a natural way to start a finding. + let got = parse(&[ + "7", + "--comment", + "-m", + "- summary", + "--at", + "a.rs:1", + "- first", + "--at", + "b.rs:2", + "second", + ]); + assert_eq!(got.body.as_deref(), Some("- summary")); + assert_eq!(got.at, ["a.rs:1", "- first", "b.rs:2", "second"]); + } + #[test] fn at_pairs_become_new_file_line_comments() { let got = parse_inline_comments(&at(&[