44import os
55import re
66import shlex
7- import sys
87import tempfile
98from dataclasses import dataclass
109from pathlib import Path
1110from typing import TYPE_CHECKING , Any
1211
13- from gh_llm .commands .options import raise_unknown_option_value
12+ from gh_llm .commands .options import raise_unknown_option_value , resolve_file_or_inline_text
1413from gh_llm .github_api import GitHubClient
1514from gh_llm .invocation import display_command_with
1615from gh_llm .models import PullRequestDiffPage
@@ -221,7 +220,13 @@ def register_pr_parser(subparsers: Any) -> None:
221220
222221 comment_edit_parser = pr_subparsers .add_parser ("comment-edit" , help = "edit one issue/review comment by node id" )
223222 comment_edit_parser .add_argument ("comment_id" , help = "comment id, e.g. IC_xxx or PRRC_xxx" )
224- comment_edit_parser .add_argument ("--body" , required = True , help = "new comment body" )
223+ comment_edit_body_group = comment_edit_parser .add_mutually_exclusive_group (required = True )
224+ comment_edit_body_group .add_argument ("--body" , help = "new comment body" )
225+ comment_edit_body_group .add_argument (
226+ "-F" ,
227+ "--body-file" ,
228+ help = "read new comment body from file (use `-` to read from standard input)" ,
229+ )
225230 comment_edit_parser .add_argument ("--pr" , help = "PR number/url/branch" )
226231 comment_edit_parser .add_argument ("--repo" , help = "repository in OWNER/REPO format" )
227232 comment_edit_parser .set_defaults (handler = cmd_pr_comment_edit )
@@ -313,11 +318,15 @@ def register_pr_parser(subparsers: Any) -> None:
313318 "--body-file" ,
314319 help = "read review comment body from file (use `-` to read from standard input)" ,
315320 )
316- review_suggest_parser .add_argument (
321+ review_suggest_suggestion_group = review_suggest_parser .add_mutually_exclusive_group (required = True )
322+ review_suggest_suggestion_group .add_argument (
317323 "--suggestion" ,
318- required = True ,
319324 help = "replacement content inserted inside ```suggestion block" ,
320325 )
326+ review_suggest_suggestion_group .add_argument (
327+ "--suggestion-file" ,
328+ help = "read replacement content from file (use `-` to read from standard input)" ,
329+ )
321330 review_suggest_parser .add_argument ("--head" , help = "expected PR head sha for stale-snapshot protection" )
322331 review_suggest_parser .add_argument ("--pr" , help = "PR number/url/branch" )
323332 review_suggest_parser .add_argument ("--repo" , help = "repository in OWNER/REPO format" )
@@ -344,20 +353,19 @@ def register_pr_parser(subparsers: Any) -> None:
344353 review_submit_parser .set_defaults (handler = cmd_pr_review_submit )
345354
346355
347- def _read_body_file (path : str ) -> str :
348- if path == "-" :
349- return sys .stdin .read ()
350- return Path (path ).read_text (encoding = "utf-8" )
356+ def _resolve_body_argument (args : Any , * , default : str = "" ) -> str :
357+ return resolve_file_or_inline_text (args , text_attr = "body" , file_attr = "body_file" , default = default )
358+
351359
360+ def _resolve_suggestion_argument (args : Any ) -> str :
361+ return resolve_file_or_inline_text (args , text_attr = "suggestion" , file_attr = "suggestion_file" )
352362
353- def _resolve_body_argument (args : Any , * , default : str = "" ) -> str :
354- body_file = getattr (args , "body_file" , None )
355- if body_file :
356- return _read_body_file (str (body_file ))
357- body = getattr (args , "body" , None )
358- if body is None :
359- return default
360- return str (body )
363+
364+ def _validate_review_suggest_stdin_sources (args : Any ) -> None :
365+ if getattr (args , "body_file" , None ) == "-" and getattr (args , "suggestion_file" , None ) == "-" :
366+ raise RuntimeError (
367+ "`--body-file -` cannot be combined with `--suggestion-file -`; standard input can only be consumed once"
368+ )
361369
362370
363371def _resolve_review_submit_body (args : Any ) -> str :
@@ -746,7 +754,7 @@ def cmd_pr_comment_edit(args: Any) -> int:
746754 raise RuntimeError ("`--pr` is required when `--repo` is provided" )
747755 if args .pr is not None :
748756 client .resolve_pull_request (selector = args .pr , repo = args .repo )
749- updated_comment_id = client .edit_comment (comment_id = str (args .comment_id ), body = str (args . body ))
757+ updated_comment_id = client .edit_comment (comment_id = str (args .comment_id ), body = _resolve_body_argument (args ))
750758 print (f"comment: { updated_comment_id } " )
751759 print ("status: edited" )
752760 return 0
@@ -1082,6 +1090,7 @@ def cmd_pr_review_comment(args: Any) -> int:
10821090
10831091
10841092def cmd_pr_review_suggest (args : Any ) -> int :
1093+ _validate_review_suggest_stdin_sources (args )
10851094 client = GitHubClient ()
10861095 meta = _resolve_pr_meta (client = client , args = args )
10871096 _validate_pr_head_snapshot (meta = meta , requested_head = _resolve_requested_head (args ))
@@ -1096,7 +1105,7 @@ def cmd_pr_review_suggest(args: Any) -> int:
10961105 start_line = start_line ,
10971106 start_side = start_side ,
10981107 )
1099- suggestion = str (args . suggestion ).rstrip ("\n " )
1108+ suggestion = _resolve_suggestion_argument (args ).rstrip ("\n " )
11001109 body = _resolve_body_argument (args , default = "Suggested change" )
11011110 full_body = f"{ body .rstrip ()} \n \n ```suggestion\n { suggestion } \n ```"
11021111 thread_id , comment_id = client .add_pull_request_review_thread_comment (
0 commit comments