Skip to content

Commit 39a48fc

Browse files
committed
✨ feat: Add --show option to PR and issue commands for selective visibility of sections
1 parent 2a16919 commit 39a48fc

5 files changed

Lines changed: 282 additions & 85 deletions

File tree

README.md

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,9 @@ The extension entrypoint forwards to local repository path via `uv run --project
6363
gh-llm pr view 77900 --repo PaddlePaddle/Paddle
6464
gh llm pr view 77900 --repo PaddlePaddle/Paddle
6565

66+
# Show selected regions only
67+
gh-llm pr view 77900 --repo PaddlePaddle/Paddle --show timeline,checks
68+
6669
# Expand one hidden timeline page
6770
gh-llm pr timeline-expand 2 --pr 77900 --repo PaddlePaddle/Paddle
6871

@@ -88,6 +91,7 @@ gh-llm issue view 77924 --repo PaddlePaddle/Paddle
8891
gh-llm issue timeline-expand 2 --issue 77924 --repo PaddlePaddle/Paddle
8992
gh-llm issue event 6 --issue 77924 --repo PaddlePaddle/Paddle
9093
gh-llm issue view 77924 --repo PaddlePaddle/Paddle --expand hidden,details
94+
gh-llm issue view 77924 --repo PaddlePaddle/Paddle --show meta,description
9195
```
9296

9397
`--expand` values:
@@ -96,6 +100,13 @@ gh-llm issue view 77924 --repo PaddlePaddle/Paddle --expand hidden,details
96100
- Issue: `hidden`, `details`, `all`
97101
- Supports comma-separated values and repeated flags.
98102

103+
`--show` values:
104+
105+
- PR: `meta`, `description`, `timeline`, `checks`, `actions`, `all`
106+
- Issue: `meta`, `description`, `timeline`, `actions`, `all`
107+
- Supports comma-separated values and repeated flags.
108+
- `summary` is supported as an alias for `meta,description`.
109+
99110
### Comment / Thread Actions
100111

101112
```bash
@@ -161,8 +172,7 @@ This supports the normal flow where one review contains multiple inline comments
161172
## Render Conventions
162173

163174
- PR/Issue metadata is rendered as frontmatter.
164-
- PR description uses `<pr_description>...</pr_description>`.
165-
- Issue description uses `<issue_description>...</issue_description>`.
175+
- Description uses `<description>...</description>`.
166176
- Comment body uses `<comment>...</comment>` to avoid markdown fence ambiguity.
167177
- Hidden timeline sections are separated by `---` and include expand commands.
168178

src/gh_llm/commands/issue.py

Lines changed: 75 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,11 @@
66
from gh_llm.github_api import GitHubClient
77
from gh_llm.pager import DEFAULT_PAGE_SIZE, TimelinePager
88
from gh_llm.render import (
9+
render_description,
910
render_event_detail,
1011
render_event_detail_blocks,
1112
render_expand_hints,
13+
render_frontmatter,
1214
render_header,
1315
render_hidden_gap,
1416
render_issue_actions,
@@ -25,6 +27,14 @@ class _ExpandOptions:
2527
details: bool = False
2628

2729

30+
@dataclass(frozen=True)
31+
class _ShowOptions:
32+
meta: bool = True
33+
description: bool = True
34+
timeline: bool = True
35+
actions: bool = True
36+
37+
2838
def register_issue_parser(subparsers: Any) -> None:
2939
issue_parser = subparsers.add_parser("issue", help="Issue-related commands")
3040
issue_subparsers = issue_parser.add_subparsers(dest="issue_command")
@@ -36,6 +46,12 @@ def register_issue_parser(subparsers: Any) -> None:
3646
view_parser.add_argument("issue", nargs="?", help="Issue number/url")
3747
view_parser.add_argument("--repo", help="repository in OWNER/REPO format")
3848
view_parser.add_argument("--page-size", type=int, default=DEFAULT_PAGE_SIZE, help="timeline entries per page")
49+
view_parser.add_argument(
50+
"--show",
51+
action="append",
52+
default=[],
53+
help="show regions: meta, description, timeline, actions, all (comma-separated or repeatable)",
54+
)
3955
view_parser.add_argument(
4056
"--expand",
4157
action="append",
@@ -85,6 +101,7 @@ def register_issue_parser(subparsers: Any) -> None:
85101
def cmd_issue_view(args: Any) -> int:
86102
page_size = int(args.page_size)
87103
expand = _parse_expand_options(raw_values=list(getattr(args, "expand", [])))
104+
show = _parse_show_options(raw_values=list(getattr(args, "show", [])))
88105
client = GitHubClient()
89106
pager = TimelinePager(client)
90107

@@ -97,12 +114,26 @@ def cmd_issue_view(args: Any) -> int:
97114
)
98115
shown_pages: set[int] = {1}
99116

100-
for line in render_header(context):
101-
print(line)
102-
for line in render_page(1, context, first_page):
103-
print(line)
117+
wrote_output = False
118+
119+
def print_block(lines: list[str]) -> None:
120+
nonlocal wrote_output
121+
if not lines:
122+
return
123+
if wrote_output:
124+
print()
125+
for line in lines:
126+
print(line)
127+
wrote_output = True
128+
129+
if show.meta:
130+
print_block(render_frontmatter(context))
131+
if show.description:
132+
print_block(render_description(context))
133+
if show.timeline:
134+
print_block(render_page(1, context, first_page))
104135

105-
if last_page is not None:
136+
if show.timeline and last_page is not None:
106137
trailing_pages: list[tuple[int, TimelinePage]] = []
107138
include_previous = context.total_pages > 2 and context.total_count % context.page_size != 0
108139
if include_previous:
@@ -124,9 +155,7 @@ def cmd_issue_view(args: Any) -> int:
124155
hidden_start = 2
125156
hidden_end = first_trailing_page - 1
126157
hidden_pages = list(range(hidden_start, hidden_end + 1)) if hidden_start <= hidden_end else []
127-
print()
128-
for line in render_hidden_gap(context, hidden_pages):
129-
print(line)
158+
print_block(render_hidden_gap(context, hidden_pages))
130159
if hidden_pages:
131160
print()
132161
for index, (page_number, page_data) in enumerate(trailing_pages):
@@ -135,11 +164,10 @@ def cmd_issue_view(args: Any) -> int:
135164
for line in render_page(page_number, context, page_data):
136165
print(line)
137166

138-
print()
139-
for line in render_expand_hints(context, shown_pages):
140-
print(line)
141-
for line in render_issue_actions(context):
142-
print(line)
167+
if show.timeline:
168+
print_block(render_expand_hints(context, shown_pages))
169+
if show.actions:
170+
print_block(render_issue_actions(context))
143171

144172
return 0
145173

@@ -282,3 +310,37 @@ def _parse_expand_options(*, raw_values: list[str]) -> _ExpandOptions:
282310
details = True
283311

284312
return _ExpandOptions(hidden=hidden, details=details)
313+
314+
315+
def _parse_show_options(*, raw_values: list[str]) -> _ShowOptions:
316+
if not raw_values:
317+
return _ShowOptions()
318+
319+
selected: set[str] = set()
320+
aliases: dict[str, set[str]] = {
321+
"meta": {"meta"},
322+
"description": {"description"},
323+
"desc": {"description"},
324+
"timeline": {"timeline"},
325+
"actions": {"actions"},
326+
"summary": {"meta", "description"},
327+
"all": {"meta", "description", "timeline", "actions"},
328+
"*": {"meta", "description", "timeline", "actions"},
329+
}
330+
331+
for raw in raw_values:
332+
for part in raw.split(","):
333+
token = part.strip().lower()
334+
if not token:
335+
continue
336+
mapped = aliases.get(token)
337+
if mapped is None:
338+
raise RuntimeError(f"unknown show option: {token}")
339+
selected.update(mapped)
340+
341+
return _ShowOptions(
342+
meta=("meta" in selected),
343+
description=("description" in selected),
344+
timeline=("timeline" in selected),
345+
actions=("actions" in selected),
346+
)

src/gh_llm/commands/pr.py

Lines changed: 94 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,11 @@
99
from gh_llm.pager import DEFAULT_PAGE_SIZE, TimelinePager
1010
from gh_llm.render import (
1111
render_checks_section,
12+
render_description,
1213
render_event_detail,
1314
render_event_detail_blocks,
1415
render_expand_hints,
16+
render_frontmatter,
1517
render_header,
1618
render_hidden_gap,
1719
render_page,
@@ -31,6 +33,15 @@ class _ExpandOptions:
3133
details: bool = False
3234

3335

36+
@dataclass(frozen=True)
37+
class _ShowOptions:
38+
meta: bool = True
39+
description: bool = True
40+
timeline: bool = True
41+
checks: bool = True
42+
actions: bool = True
43+
44+
3445
def register_pr_parser(subparsers: Any) -> None:
3546
pr_parser = subparsers.add_parser("pr", help="PR-related commands")
3647
pr_subparsers = pr_parser.add_subparsers(dest="pr_command")
@@ -42,6 +53,12 @@ def register_pr_parser(subparsers: Any) -> None:
4253
view_parser.add_argument("pr", nargs="?", help="PR number/url/branch")
4354
view_parser.add_argument("--repo", help="repository in OWNER/REPO format")
4455
view_parser.add_argument("--page-size", type=int, default=DEFAULT_PAGE_SIZE, help="timeline entries per page")
56+
view_parser.add_argument(
57+
"--show",
58+
action="append",
59+
default=[],
60+
help="show regions: meta, description, timeline, checks, actions, all (comma-separated or repeatable)",
61+
)
4562
view_parser.add_argument(
4663
"--expand",
4764
action="append",
@@ -213,6 +230,7 @@ def cmd_pr_view(args: Any) -> int:
213230
page_size = int(args.page_size)
214231
diff_hunk_lines = _resolve_diff_hunk_lines(args=args, default=DEFAULT_DIFF_HUNK_LINES)
215232
expand = _parse_expand_options(raw_values=list(getattr(args, "expand", [])))
233+
show = _parse_show_options(raw_values=list(getattr(args, "show", [])))
216234
client = GitHubClient()
217235
pager = TimelinePager(client)
218236

@@ -227,12 +245,26 @@ def cmd_pr_view(args: Any) -> int:
227245
)
228246
shown_pages: set[int] = {1}
229247

230-
for line in render_header(context):
231-
print(line)
232-
for line in render_page(1, context, first_page):
233-
print(line)
248+
wrote_output = False
234249

235-
if last_page is not None:
250+
def print_block(lines: list[str]) -> None:
251+
nonlocal wrote_output
252+
if not lines:
253+
return
254+
if wrote_output:
255+
print()
256+
for line in lines:
257+
print(line)
258+
wrote_output = True
259+
260+
if show.meta:
261+
print_block(render_frontmatter(context))
262+
if show.description:
263+
print_block(render_description(context))
264+
if show.timeline:
265+
print_block(render_page(1, context, first_page))
266+
267+
if show.timeline and last_page is not None:
236268
trailing_pages: list[tuple[int, TimelinePage]] = []
237269
include_previous = context.total_pages > 2 and context.total_count % context.page_size != 0
238270
if include_previous:
@@ -256,9 +288,7 @@ def cmd_pr_view(args: Any) -> int:
256288
hidden_start = 2
257289
hidden_end = first_trailing_page - 1
258290
hidden_pages = list(range(hidden_start, hidden_end + 1)) if hidden_start <= hidden_end else []
259-
print()
260-
for line in render_hidden_gap(context, hidden_pages):
261-
print(line)
291+
print_block(render_hidden_gap(context, hidden_pages))
262292
if hidden_pages:
263293
print()
264294
for index, (page_number, page_data) in enumerate(trailing_pages):
@@ -267,19 +297,26 @@ def cmd_pr_view(args: Any) -> int:
267297
for line in render_page(page_number, context, page_data):
268298
print(line)
269299

270-
print()
271-
for line in render_expand_hints(context, shown_pages):
272-
print(line)
273-
checks = client.fetch_checks(meta.ref) if meta.state == "OPEN" else []
274-
for line in render_checks_section(
275-
context=context,
276-
checks=checks,
277-
show_all=False,
278-
is_open=(meta.state == "OPEN"),
279-
):
280-
print(line)
281-
for line in render_pr_actions(context):
282-
print(line)
300+
if show.timeline:
301+
print_block(render_expand_hints(context, shown_pages))
302+
if show.checks:
303+
checks = client.fetch_checks(meta.ref) if meta.state == "OPEN" else []
304+
print_block(
305+
render_checks_section(
306+
context=context,
307+
checks=checks,
308+
show_all=False,
309+
is_open=(meta.state == "OPEN"),
310+
)
311+
)
312+
if show.actions:
313+
print_block(
314+
render_pr_actions(
315+
context,
316+
include_diff=True,
317+
include_manage=show.actions,
318+
)
319+
)
283320

284321
return 0
285322

@@ -664,6 +701,42 @@ def _parse_expand_options(*, raw_values: list[str]) -> _ExpandOptions:
664701
return _ExpandOptions(resolved=resolved, hidden=hidden, details=details)
665702

666703

704+
def _parse_show_options(*, raw_values: list[str]) -> _ShowOptions:
705+
if not raw_values:
706+
return _ShowOptions()
707+
708+
selected: set[str] = set()
709+
aliases: dict[str, set[str]] = {
710+
"meta": {"meta"},
711+
"description": {"description"},
712+
"desc": {"description"},
713+
"timeline": {"timeline"},
714+
"checks": {"checks"},
715+
"actions": {"actions"},
716+
"summary": {"meta", "description"},
717+
"all": {"meta", "description", "timeline", "checks", "actions"},
718+
"*": {"meta", "description", "timeline", "checks", "actions"},
719+
}
720+
721+
for raw in raw_values:
722+
for part in raw.split(","):
723+
token = part.strip().lower()
724+
if not token:
725+
continue
726+
mapped = aliases.get(token)
727+
if mapped is None:
728+
raise RuntimeError(f"unknown show option: {token}")
729+
selected.update(mapped)
730+
731+
return _ShowOptions(
732+
meta=("meta" in selected),
733+
description=("description" in selected),
734+
timeline=("timeline" in selected),
735+
checks=("checks" in selected),
736+
actions=("actions" in selected),
737+
)
738+
739+
667740
def _resolve_pr_meta(*, client: GitHubClient, args: Any) -> PullRequestMeta:
668741
selector = getattr(args, "pr", None)
669742
repo = getattr(args, "repo", None)

0 commit comments

Comments
 (0)