pygitcode 0.1.3__tar.gz → 0.1.4__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {pygitcode-0.1.3/src/pygitcode.egg-info → pygitcode-0.1.4}/PKG-INFO +1 -1
- {pygitcode-0.1.3 → pygitcode-0.1.4}/pyproject.toml +1 -0
- {pygitcode-0.1.3 → pygitcode-0.1.4}/src/gitcode_cli/__init__.py +1 -1
- {pygitcode-0.1.3 → pygitcode-0.1.4}/src/gitcode_cli/adapters/capabilities.py +4 -0
- {pygitcode-0.1.3 → pygitcode-0.1.4}/src/gitcode_cli/adapters/pulls.py +0 -2
- {pygitcode-0.1.3 → pygitcode-0.1.4}/src/gitcode_cli/cli.py +13 -9
- pygitcode-0.1.4/src/gitcode_cli/commands/compat.py +89 -0
- {pygitcode-0.1.3 → pygitcode-0.1.4}/src/gitcode_cli/commands/pr.py +110 -22
- pygitcode-0.1.4/src/gitcode_cli/compat/__init__.py +10 -0
- pygitcode-0.1.4/src/gitcode_cli/compat/_types.py +48 -0
- pygitcode-0.1.4/src/gitcode_cli/compat/entries.py +459 -0
- pygitcode-0.1.4/src/gitcode_cli/compat/registry.py +146 -0
- {pygitcode-0.1.3 → pygitcode-0.1.4}/src/gitcode_cli/services/pulls.py +20 -1
- {pygitcode-0.1.3 → pygitcode-0.1.4/src/pygitcode.egg-info}/PKG-INFO +1 -1
- {pygitcode-0.1.3 → pygitcode-0.1.4}/src/pygitcode.egg-info/SOURCES.txt +5 -0
- {pygitcode-0.1.3 → pygitcode-0.1.4}/LICENSE +0 -0
- {pygitcode-0.1.3 → pygitcode-0.1.4}/README.md +0 -0
- {pygitcode-0.1.3 → pygitcode-0.1.4}/setup.cfg +0 -0
- {pygitcode-0.1.3 → pygitcode-0.1.4}/src/gitcode_cli/adapters/__init__.py +0 -0
- {pygitcode-0.1.3 → pygitcode-0.1.4}/src/gitcode_cli/adapters/base.py +0 -0
- {pygitcode-0.1.3 → pygitcode-0.1.4}/src/gitcode_cli/adapters/issues.py +0 -0
- {pygitcode-0.1.3 → pygitcode-0.1.4}/src/gitcode_cli/cli_compat.py +0 -0
- {pygitcode-0.1.3 → pygitcode-0.1.4}/src/gitcode_cli/client.py +0 -0
- {pygitcode-0.1.3 → pygitcode-0.1.4}/src/gitcode_cli/commands/__init__.py +0 -0
- {pygitcode-0.1.3 → pygitcode-0.1.4}/src/gitcode_cli/commands/auth.py +0 -0
- {pygitcode-0.1.3 → pygitcode-0.1.4}/src/gitcode_cli/commands/issue.py +0 -0
- {pygitcode-0.1.3 → pygitcode-0.1.4}/src/gitcode_cli/config.py +0 -0
- {pygitcode-0.1.3 → pygitcode-0.1.4}/src/gitcode_cli/context.py +0 -0
- {pygitcode-0.1.3 → pygitcode-0.1.4}/src/gitcode_cli/errors.py +0 -0
- {pygitcode-0.1.3 → pygitcode-0.1.4}/src/gitcode_cli/formatters.py +0 -0
- {pygitcode-0.1.3 → pygitcode-0.1.4}/src/gitcode_cli/helptext.py +0 -0
- {pygitcode-0.1.3 → pygitcode-0.1.4}/src/gitcode_cli/repo.py +0 -0
- {pygitcode-0.1.3 → pygitcode-0.1.4}/src/gitcode_cli/services/__init__.py +0 -0
- {pygitcode-0.1.3 → pygitcode-0.1.4}/src/gitcode_cli/services/issues.py +0 -0
- {pygitcode-0.1.3 → pygitcode-0.1.4}/src/gitcode_cli/services/users.py +0 -0
- {pygitcode-0.1.3 → pygitcode-0.1.4}/src/gitcode_cli/utils.py +0 -0
- {pygitcode-0.1.3 → pygitcode-0.1.4}/src/pygitcode.egg-info/dependency_links.txt +0 -0
- {pygitcode-0.1.3 → pygitcode-0.1.4}/src/pygitcode.egg-info/entry_points.txt +0 -0
- {pygitcode-0.1.3 → pygitcode-0.1.4}/src/pygitcode.egg-info/requires.txt +0 -0
- {pygitcode-0.1.3 → pygitcode-0.1.4}/src/pygitcode.egg-info/top_level.txt +0 -0
|
@@ -95,6 +95,7 @@ select = [
|
|
|
95
95
|
ignore = [
|
|
96
96
|
"PLR2004", # Magic value used in comparison
|
|
97
97
|
"PLR0913", # Too many arguments in function definition (common for Click commands and API methods)
|
|
98
|
+
"PLR0917", # Too many positional arguments (common for Click commands and API methods)
|
|
98
99
|
]
|
|
99
100
|
|
|
100
101
|
[tool.ruff.lint.per-file-ignores]
|
|
@@ -17,6 +17,10 @@ CAPABILITY_MESSAGES = {
|
|
|
17
17
|
),
|
|
18
18
|
"PR_MERGE_AUTHOR_EMAIL": "GitCode merge API does not support --author-email.",
|
|
19
19
|
"PR_MERGE_AUTO": "GitCode merge API does not support --auto.",
|
|
20
|
+
"PR_EDIT_BASE": (
|
|
21
|
+
"GitCode's pull request update API does not support changing the base branch; "
|
|
22
|
+
"-B/--base cannot be used with 'gc pr edit'."
|
|
23
|
+
),
|
|
20
24
|
"PR_REVIEW_REQUEST_CHANGES": (
|
|
21
25
|
"GitCode review API does not support request-changes reviews; the pull request comment was posted instead."
|
|
22
26
|
),
|
|
@@ -161,7 +161,6 @@ class PullRequestAdapter:
|
|
|
161
161
|
*,
|
|
162
162
|
title: str | None,
|
|
163
163
|
body: str | None,
|
|
164
|
-
base: str | None,
|
|
165
164
|
add_assignee: str | None,
|
|
166
165
|
add_label: str | None,
|
|
167
166
|
add_reviewer: str | None,
|
|
@@ -176,7 +175,6 @@ class PullRequestAdapter:
|
|
|
176
175
|
for k, v in {
|
|
177
176
|
"title": title,
|
|
178
177
|
"body": body,
|
|
179
|
-
"base": base,
|
|
180
178
|
"assignee": add_assignee,
|
|
181
179
|
"labels": add_label,
|
|
182
180
|
"reviewer": add_reviewer,
|
|
@@ -9,6 +9,7 @@ from click.shell_completion import get_completion_class
|
|
|
9
9
|
|
|
10
10
|
from . import __version__
|
|
11
11
|
from .commands.auth import auth_group
|
|
12
|
+
from .commands.compat import compat_group
|
|
12
13
|
from .commands.issue import issue_group
|
|
13
14
|
from .commands.pr import pr_group
|
|
14
15
|
from .config import get_token
|
|
@@ -67,12 +68,13 @@ set_gc_help(
|
|
|
67
68
|
gc_usage="gc <command> <subcommand> [flags]",
|
|
68
69
|
gc_command_sections=[
|
|
69
70
|
("CORE COMMANDS", ["auth", "issue", "pr"]),
|
|
70
|
-
("ADDITIONAL COMMANDS", ["completion", "version"]),
|
|
71
|
+
("ADDITIONAL COMMANDS", ["compat", "completion", "version"]),
|
|
71
72
|
],
|
|
72
73
|
gc_examples=[
|
|
73
74
|
"gc issue create",
|
|
74
75
|
"gc pr list -R owner/repo",
|
|
75
76
|
"gc auth login",
|
|
77
|
+
"gc compat report --stdout",
|
|
76
78
|
],
|
|
77
79
|
gc_learn_more=[
|
|
78
80
|
"Use `gc <command> <subcommand> --help` for more information about a command.",
|
|
@@ -80,17 +82,18 @@ set_gc_help(
|
|
|
80
82
|
)
|
|
81
83
|
|
|
82
84
|
|
|
83
|
-
auth_group.short_help = "Authenticate gc with GitCode"
|
|
84
|
-
issue_group.short_help = "Manage issues"
|
|
85
|
-
pr_group.short_help = "Manage pull requests"
|
|
85
|
+
auth_group.short_help = "Authenticate gc with GitCode" # type: ignore[attr-defined]
|
|
86
|
+
issue_group.short_help = "Manage issues" # type: ignore[attr-defined]
|
|
87
|
+
pr_group.short_help = "Manage pull requests" # type: ignore[attr-defined]
|
|
88
|
+
compat_group.short_help = "Inspect gc ↔ gh compatibility" # type: ignore[attr-defined]
|
|
86
89
|
|
|
87
90
|
|
|
88
|
-
@main.command("version", short_help="Show gc version", help="Show gc version.")
|
|
91
|
+
@main.command("version", short_help="Show gc version", help="Show gc version.") # type: ignore[attr-defined]
|
|
89
92
|
def version_command() -> None:
|
|
90
93
|
safe_echo(f"gitcode version {_get_version()}")
|
|
91
94
|
|
|
92
95
|
|
|
93
|
-
@main.command("completion", short_help="Generate shell completion scripts", help="Generate shell completion scripts.")
|
|
96
|
+
@main.command("completion", short_help="Generate shell completion scripts", help="Generate shell completion scripts.") # type: ignore[attr-defined]
|
|
94
97
|
@click.argument("shell", type=click.Choice(["bash", "zsh", "fish"]))
|
|
95
98
|
def completion_command(shell: str) -> None:
|
|
96
99
|
comp_class = get_completion_class(shell)
|
|
@@ -100,9 +103,10 @@ def completion_command(shell: str) -> None:
|
|
|
100
103
|
safe_echo(comp.source())
|
|
101
104
|
|
|
102
105
|
|
|
103
|
-
main.add_command(auth_group)
|
|
104
|
-
main.add_command(issue_group)
|
|
105
|
-
main.add_command(pr_group)
|
|
106
|
+
main.add_command(auth_group) # type: ignore[attr-defined]
|
|
107
|
+
main.add_command(issue_group) # type: ignore[attr-defined]
|
|
108
|
+
main.add_command(pr_group) # type: ignore[attr-defined]
|
|
109
|
+
main.add_command(compat_group) # type: ignore[attr-defined]
|
|
106
110
|
|
|
107
111
|
|
|
108
112
|
if __name__ == "__main__": # pragma: no cover
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
"""``gc compat`` subcommand — render the gh-compatibility matrix."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import json
|
|
6
|
+
import sys
|
|
7
|
+
|
|
8
|
+
import click
|
|
9
|
+
|
|
10
|
+
from ..compat import CompatStatus, iter_entries, render_markdown_report, statistics
|
|
11
|
+
from ..helptext import set_gc_help
|
|
12
|
+
from ..utils import safe_echo
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
@click.group(
|
|
16
|
+
"compat",
|
|
17
|
+
short_help="Inspect gc ↔ gh compatibility status",
|
|
18
|
+
help="Inspect the authoritative gh-compatibility registry used to track issue #68.",
|
|
19
|
+
)
|
|
20
|
+
def compat_group() -> None:
|
|
21
|
+
pass
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
@compat_group.command( # type: ignore[attr-defined]
|
|
25
|
+
"report",
|
|
26
|
+
short_help="Render the compatibility matrix",
|
|
27
|
+
help=(
|
|
28
|
+
"Render the compatibility matrix as Markdown. The output mirrors the "
|
|
29
|
+
"structure of issue #68 and is regenerated from the registry on every "
|
|
30
|
+
"run; do not edit it by hand."
|
|
31
|
+
),
|
|
32
|
+
)
|
|
33
|
+
@click.option("--stdout", "to_stdout", is_flag=True, help="Write Markdown to stdout instead of disk.")
|
|
34
|
+
def compat_report(to_stdout: bool) -> None:
|
|
35
|
+
markdown = render_markdown_report()
|
|
36
|
+
if to_stdout:
|
|
37
|
+
safe_echo(markdown)
|
|
38
|
+
return
|
|
39
|
+
safe_echo(markdown)
|
|
40
|
+
safe_echo(
|
|
41
|
+
"\n(hint: re-run with --stdout to capture the report, e.g. `gc compat report --stdout > COMPATIBILITY.md`)",
|
|
42
|
+
err=True,
|
|
43
|
+
)
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
@compat_group.command( # type: ignore[attr-defined]
|
|
47
|
+
"stats",
|
|
48
|
+
short_help="Print registry statistics as JSON",
|
|
49
|
+
help=("Print aggregated status counts per top-level group as JSON on stdout."),
|
|
50
|
+
)
|
|
51
|
+
def compat_stats() -> None:
|
|
52
|
+
json.dump(statistics(), sys.stdout, indent=2, sort_keys=True)
|
|
53
|
+
sys.stdout.write("\n")
|
|
54
|
+
sys.stdout.flush()
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
@compat_group.command( # type: ignore[attr-defined]
|
|
58
|
+
"list",
|
|
59
|
+
short_help="List entries (one per line)",
|
|
60
|
+
help=(
|
|
61
|
+
"List all registry entries. By default only pending and unsupported "
|
|
62
|
+
"entries are shown; pass --all to include everything."
|
|
63
|
+
),
|
|
64
|
+
)
|
|
65
|
+
@click.option("--all", "show_all", is_flag=True, help="Show every entry, not just open work.")
|
|
66
|
+
@click.option("--status", "status_filter", multiple=True, help="Filter by status (repeatable).")
|
|
67
|
+
def compat_list(show_all: bool, status_filter: tuple[str, ...]) -> None:
|
|
68
|
+
statuses = tuple(CompatStatus(s) for s in status_filter) if status_filter else None
|
|
69
|
+
if statuses is None and not show_all:
|
|
70
|
+
statuses = (CompatStatus.PENDING, CompatStatus.UNSUPPORTED, CompatStatus.NOT_IN_CLI)
|
|
71
|
+
for entry in iter_entries(statuses=statuses):
|
|
72
|
+
safe_echo(f"{entry.command:<22} {entry.flag or '-':<28} {entry.status.value:<13} {entry.note}")
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
set_gc_help(
|
|
76
|
+
compat_group,
|
|
77
|
+
gc_usage="gc compat <command>",
|
|
78
|
+
gc_command_sections=[
|
|
79
|
+
("COMMANDS", ["report", "stats", "list"]),
|
|
80
|
+
],
|
|
81
|
+
gc_examples=[
|
|
82
|
+
"gc compat report --stdout",
|
|
83
|
+
"gc compat stats",
|
|
84
|
+
"gc compat list --status pending",
|
|
85
|
+
],
|
|
86
|
+
gc_arguments_help=[
|
|
87
|
+
"The registry replaces the hand-maintained table in issue #68.",
|
|
88
|
+
],
|
|
89
|
+
)
|
|
@@ -46,6 +46,66 @@ def _normalize_pr_fields(item: dict) -> dict:
|
|
|
46
46
|
return result
|
|
47
47
|
|
|
48
48
|
|
|
49
|
+
def _normalize_closing_issue(item: dict) -> dict:
|
|
50
|
+
number = item.get("number")
|
|
51
|
+
if isinstance(number, str) and number.isdigit():
|
|
52
|
+
number = int(number)
|
|
53
|
+
|
|
54
|
+
state = item.get("state")
|
|
55
|
+
if isinstance(state, str):
|
|
56
|
+
state = state.upper()
|
|
57
|
+
if state == "OPENED":
|
|
58
|
+
state = "OPEN"
|
|
59
|
+
|
|
60
|
+
return {
|
|
61
|
+
"number": number,
|
|
62
|
+
"title": item.get("title"),
|
|
63
|
+
"state": state,
|
|
64
|
+
"url": item.get("html_url") or item.get("url"),
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
def _normalize_pr_actor(value: object) -> object:
|
|
69
|
+
if not isinstance(value, dict):
|
|
70
|
+
return value
|
|
71
|
+
login = value.get("login") or value.get("username") or value.get("name")
|
|
72
|
+
return {
|
|
73
|
+
"id": value.get("id"),
|
|
74
|
+
"is_bot": bool(value.get("is_bot", False)),
|
|
75
|
+
"login": login,
|
|
76
|
+
"name": value.get("name") or login,
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
def _pr_ref_name(value: object) -> object:
|
|
81
|
+
return value.get("ref") if isinstance(value, dict) else value
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
def _normalize_pr_view_fields(item: dict) -> dict:
|
|
85
|
+
result = _normalize_pr_fields(item)
|
|
86
|
+
assignees = item.get("assignees")
|
|
87
|
+
result.update(
|
|
88
|
+
{
|
|
89
|
+
"author": _normalize_pr_actor(item.get("author") or item.get("user") or item.get("creator")),
|
|
90
|
+
"assignees": (
|
|
91
|
+
[_normalize_pr_actor(assignee) for assignee in assignees] if isinstance(assignees, list) else assignees
|
|
92
|
+
),
|
|
93
|
+
"baseRefName": item.get("baseRefName") or _pr_ref_name(item.get("base")),
|
|
94
|
+
"createdAt": item.get("createdAt") or item.get("created_at"),
|
|
95
|
+
"headRefName": item.get("headRefName") or _pr_ref_name(item.get("head")),
|
|
96
|
+
"updatedAt": item.get("updatedAt") or item.get("updated_at"),
|
|
97
|
+
"url": item.get("html_url") or item.get("url"),
|
|
98
|
+
}
|
|
99
|
+
)
|
|
100
|
+
if isinstance(result.get("state"), str):
|
|
101
|
+
result["state"] = result["state"].upper()
|
|
102
|
+
return result
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
def _json_field_requested(json_fields: str | None, field: str) -> bool:
|
|
106
|
+
return json_fields is not None and field in {value.strip() for value in json_fields.split(",")}
|
|
107
|
+
|
|
108
|
+
|
|
49
109
|
def _split_merged_range(value: str) -> tuple[str | None, str | None]:
|
|
50
110
|
if ".." in value:
|
|
51
111
|
after, before = value.split("..", 1)
|
|
@@ -362,7 +422,7 @@ def pr_reopen(ctx: click.Context, repo_name: str | None, identifier: str | None,
|
|
|
362
422
|
@click.option("-t", "--title")
|
|
363
423
|
@click.option("-b", "--body")
|
|
364
424
|
@click.option("-F", "--body-file")
|
|
365
|
-
@click.option("-B", "--base")
|
|
425
|
+
@click.option("-B", "--base", help="Unsupported: GitCode does not allow changing a pull request's base branch.")
|
|
366
426
|
@click.option("-a", "--add-assignee")
|
|
367
427
|
@click.option("-l", "--add-label")
|
|
368
428
|
@click.option("-r", "--add-reviewer")
|
|
@@ -389,6 +449,8 @@ def pr_edit(
|
|
|
389
449
|
milestone: str | None,
|
|
390
450
|
remove_milestone: bool,
|
|
391
451
|
) -> None:
|
|
452
|
+
if base is not None:
|
|
453
|
+
raise unsupported("PR_EDIT_BASE")
|
|
392
454
|
app = ctx.obj["app"]
|
|
393
455
|
owner, repo = resolve_repo(repo_name or app.repo)
|
|
394
456
|
service = PullRequestService(app.client())
|
|
@@ -401,7 +463,6 @@ def pr_edit(
|
|
|
401
463
|
[
|
|
402
464
|
title is not None,
|
|
403
465
|
body is not None,
|
|
404
|
-
base is not None,
|
|
405
466
|
add_assignee is not None,
|
|
406
467
|
add_label is not None,
|
|
407
468
|
add_reviewer is not None,
|
|
@@ -420,7 +481,6 @@ def pr_edit(
|
|
|
420
481
|
number,
|
|
421
482
|
title=title,
|
|
422
483
|
body=body,
|
|
423
|
-
base=base,
|
|
424
484
|
add_assignee=add_assignee,
|
|
425
485
|
add_label=add_label,
|
|
426
486
|
add_reviewer=add_reviewer,
|
|
@@ -535,28 +595,36 @@ def pr_view(
|
|
|
535
595
|
if web:
|
|
536
596
|
open_in_browser(item["html_url"])
|
|
537
597
|
return
|
|
538
|
-
if
|
|
539
|
-
|
|
540
|
-
|
|
598
|
+
output_item = _normalize_pr_view_fields(item) if item and (json_fields or jq_query or template) else item
|
|
599
|
+
if _json_field_requested(json_fields, "closingIssuesReferences"):
|
|
600
|
+
output_item = dict(output_item or {})
|
|
601
|
+
related_issues = service.list_issues(owner, repo, number) or []
|
|
602
|
+
output_item["closingIssuesReferences"] = [_normalize_closing_issue(issue) for issue in related_issues]
|
|
603
|
+
if comments or _json_field_requested(json_fields, "comments"):
|
|
604
|
+
comment_items = service.list_comments(owner, repo, number) or []
|
|
605
|
+
data = dict(output_item) if output_item else {}
|
|
541
606
|
data["comments"] = comment_items
|
|
542
607
|
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
safe_echo(
|
|
547
|
-
|
|
548
|
-
safe_echo(
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
608
|
+
if comments:
|
|
609
|
+
|
|
610
|
+
def default_formatter(data: dict) -> None:
|
|
611
|
+
safe_echo(format_pr_detail(data))
|
|
612
|
+
if comment_items:
|
|
613
|
+
safe_echo("\nComments:")
|
|
614
|
+
for comment in comment_items:
|
|
615
|
+
safe_echo(f"- {comment.get('body') or ''}")
|
|
616
|
+
|
|
617
|
+
output_result(
|
|
618
|
+
data,
|
|
619
|
+
json_fields,
|
|
620
|
+
jq_query,
|
|
621
|
+
template,
|
|
622
|
+
default_formatter=default_formatter,
|
|
623
|
+
)
|
|
624
|
+
return
|
|
625
|
+
output_item = data
|
|
558
626
|
output_result(
|
|
559
|
-
|
|
627
|
+
output_item,
|
|
560
628
|
json_fields,
|
|
561
629
|
jq_query,
|
|
562
630
|
template,
|
|
@@ -896,6 +964,26 @@ set_gc_help(
|
|
|
896
964
|
)
|
|
897
965
|
pr_view.short_help = "View a pull request"
|
|
898
966
|
pr_view.help = "View a pull request."
|
|
967
|
+
set_gc_help(
|
|
968
|
+
pr_view,
|
|
969
|
+
gc_json_fields=[
|
|
970
|
+
"author",
|
|
971
|
+
"assignees",
|
|
972
|
+
"baseRefName",
|
|
973
|
+
"body",
|
|
974
|
+
"closingIssuesReferences",
|
|
975
|
+
"comments",
|
|
976
|
+
"createdAt",
|
|
977
|
+
"headRefName",
|
|
978
|
+
"labels",
|
|
979
|
+
"mergedAt",
|
|
980
|
+
"number",
|
|
981
|
+
"state",
|
|
982
|
+
"title",
|
|
983
|
+
"updatedAt",
|
|
984
|
+
"url",
|
|
985
|
+
],
|
|
986
|
+
)
|
|
899
987
|
pr_create.short_help = "Create a pull request"
|
|
900
988
|
pr_create.help = "Create a pull request."
|
|
901
989
|
set_gc_help(pr_create, gc_aliases=["new"])
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
"""Shared types and constants for the compat registry."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from dataclasses import dataclass, field
|
|
6
|
+
from enum import Enum
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class CompatStatus(str, Enum):
|
|
10
|
+
"""Classification of a ``gc`` command/flag against the ``gh`` baseline."""
|
|
11
|
+
|
|
12
|
+
IMPLEMENTED = "implemented"
|
|
13
|
+
PENDING = "pending"
|
|
14
|
+
UNSUPPORTED = "unsupported"
|
|
15
|
+
NOT_IN_CLI = "not_in_cli"
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
@dataclass(frozen=True)
|
|
19
|
+
class CompatEntry:
|
|
20
|
+
"""One row of the compatibility matrix.
|
|
21
|
+
|
|
22
|
+
``command`` is a space-separated path without the leading ``gc`` (e.g.
|
|
23
|
+
``"issue list"``). ``flag`` is the long-form flag (``"--app"``) or ``None``
|
|
24
|
+
when the entry describes a whole subcommand.
|
|
25
|
+
|
|
26
|
+
``pending_key`` must match the literal string passed to
|
|
27
|
+
``_pending_gh_compat()`` for ``PENDING`` entries. ``unsupported_key`` must
|
|
28
|
+
match the capability key passed to ``unsupported()`` for ``UNSUPPORTED``
|
|
29
|
+
entries (and the key must exist in ``CAPABILITY_MESSAGES``).
|
|
30
|
+
"""
|
|
31
|
+
|
|
32
|
+
command: str
|
|
33
|
+
flag: str | None
|
|
34
|
+
status: CompatStatus
|
|
35
|
+
note: str = ""
|
|
36
|
+
pending_key: str | None = None
|
|
37
|
+
unsupported_key: str | None = None
|
|
38
|
+
gc_extension: bool = False
|
|
39
|
+
aliases: tuple[str, ...] = field(default_factory=tuple)
|
|
40
|
+
|
|
41
|
+
def label(self) -> str:
|
|
42
|
+
"""Human-readable label used in the rendered report."""
|
|
43
|
+
if self.flag is None:
|
|
44
|
+
return self.command
|
|
45
|
+
aliases = "/".join((self.aliases or ()) + ((self.flag,) if self.flag else ()))
|
|
46
|
+
if not aliases:
|
|
47
|
+
return f"{self.command} (no flag)"
|
|
48
|
+
return f"{self.command} {aliases}"
|
|
@@ -0,0 +1,459 @@
|
|
|
1
|
+
"""Registry entries — declarative gh-compatibility status.
|
|
2
|
+
|
|
3
|
+
Each tuple is ``(command, flag, status, note, **kwargs)`` where ``flag is None``
|
|
4
|
+
denotes a subcommand-level row. ``pending_key`` and ``unsupported_key`` link the
|
|
5
|
+
entry to runtime guards (enforced by ``test_registry_consistency``).
|
|
6
|
+
|
|
7
|
+
The data was transcribed from issue #68 (last revised 2026-05-06) and
|
|
8
|
+
re-validated against ``origin/main`` (2026-08-08, commit de31598). Subsequent
|
|
9
|
+
PRs that change command code MUST update this file in lockstep.
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
from __future__ import annotations
|
|
13
|
+
|
|
14
|
+
from ._types import CompatEntry, CompatStatus
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def _e(
|
|
18
|
+
command: str,
|
|
19
|
+
flag: str | None,
|
|
20
|
+
status: CompatStatus,
|
|
21
|
+
note: str = "",
|
|
22
|
+
*,
|
|
23
|
+
pending_key: str | None = None,
|
|
24
|
+
unsupported_key: str | None = None,
|
|
25
|
+
gc_extension: bool = False,
|
|
26
|
+
aliases: tuple[str, ...] = (),
|
|
27
|
+
) -> CompatEntry:
|
|
28
|
+
return CompatEntry(
|
|
29
|
+
command=command,
|
|
30
|
+
flag=flag,
|
|
31
|
+
status=status,
|
|
32
|
+
note=note,
|
|
33
|
+
pending_key=pending_key,
|
|
34
|
+
unsupported_key=unsupported_key,
|
|
35
|
+
gc_extension=gc_extension,
|
|
36
|
+
aliases=aliases,
|
|
37
|
+
)
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
ENTRIES: list[CompatEntry] = [
|
|
41
|
+
# --- auth subcommands ---
|
|
42
|
+
_e("auth login", None, CompatStatus.IMPLEMENTED, "Supports --with-token and interactive input"),
|
|
43
|
+
_e("auth logout", None, CompatStatus.IMPLEMENTED),
|
|
44
|
+
_e("auth status", None, CompatStatus.IMPLEMENTED, "Simplified: token mask only"),
|
|
45
|
+
_e("auth token", None, CompatStatus.IMPLEMENTED),
|
|
46
|
+
# --- auth flags ---
|
|
47
|
+
_e(
|
|
48
|
+
"auth login",
|
|
49
|
+
"--with-token",
|
|
50
|
+
CompatStatus.IMPLEMENTED,
|
|
51
|
+
),
|
|
52
|
+
_e("auth login", "-c/--clipboard", CompatStatus.NOT_IN_CLI),
|
|
53
|
+
_e("auth login", "-p/--git-protocol", CompatStatus.NOT_IN_CLI),
|
|
54
|
+
_e("auth login", "-h/--hostname", CompatStatus.NOT_IN_CLI),
|
|
55
|
+
_e("auth login", "--insecure-storage", CompatStatus.NOT_IN_CLI),
|
|
56
|
+
_e("auth login", "-s/--scopes", CompatStatus.NOT_IN_CLI),
|
|
57
|
+
_e("auth login", "--skip-ssh-key", CompatStatus.NOT_IN_CLI),
|
|
58
|
+
_e("auth login", "-w/--web", CompatStatus.NOT_IN_CLI),
|
|
59
|
+
_e("auth logout", "-h/--hostname", CompatStatus.NOT_IN_CLI),
|
|
60
|
+
_e("auth logout", "-u/--user", CompatStatus.NOT_IN_CLI),
|
|
61
|
+
_e("auth status", "-a/--active", CompatStatus.NOT_IN_CLI),
|
|
62
|
+
_e("auth status", "-h/--hostname", CompatStatus.NOT_IN_CLI),
|
|
63
|
+
_e(
|
|
64
|
+
"auth status",
|
|
65
|
+
"--json",
|
|
66
|
+
CompatStatus.PENDING,
|
|
67
|
+
"Click guard added; structured output not yet implemented",
|
|
68
|
+
pending_key="auth status --json",
|
|
69
|
+
),
|
|
70
|
+
_e(
|
|
71
|
+
"auth status",
|
|
72
|
+
"--jq",
|
|
73
|
+
CompatStatus.PENDING,
|
|
74
|
+
"Click guard added; structured output not yet implemented",
|
|
75
|
+
pending_key="auth status --jq",
|
|
76
|
+
),
|
|
77
|
+
_e(
|
|
78
|
+
"auth status",
|
|
79
|
+
"--template",
|
|
80
|
+
CompatStatus.PENDING,
|
|
81
|
+
"Click guard added; structured output not yet implemented",
|
|
82
|
+
pending_key="auth status --template",
|
|
83
|
+
),
|
|
84
|
+
_e("auth status", "--show-token", CompatStatus.NOT_IN_CLI),
|
|
85
|
+
_e("auth token", "-h/--hostname", CompatStatus.NOT_IN_CLI),
|
|
86
|
+
_e("auth token", "-u/--user", CompatStatus.NOT_IN_CLI),
|
|
87
|
+
# --- issue subcommands ---
|
|
88
|
+
_e("issue list", None, CompatStatus.IMPLEMENTED, "Alias: ls"),
|
|
89
|
+
_e("issue view", None, CompatStatus.IMPLEMENTED),
|
|
90
|
+
_e("issue create", None, CompatStatus.IMPLEMENTED, "Alias: new"),
|
|
91
|
+
_e("issue close", None, CompatStatus.IMPLEMENTED),
|
|
92
|
+
_e("issue comment", None, CompatStatus.IMPLEMENTED),
|
|
93
|
+
_e("issue reopen", None, CompatStatus.IMPLEMENTED),
|
|
94
|
+
_e("issue edit", None, CompatStatus.IMPLEMENTED),
|
|
95
|
+
_e(
|
|
96
|
+
"issue status",
|
|
97
|
+
None,
|
|
98
|
+
CompatStatus.IMPLEMENTED,
|
|
99
|
+
"Approximate: lacks gh user-specific filtering (ISSUE_STATUS_GH_SEMANTICS)",
|
|
100
|
+
),
|
|
101
|
+
_e(
|
|
102
|
+
"issue develop",
|
|
103
|
+
None,
|
|
104
|
+
CompatStatus.IMPLEMENTED,
|
|
105
|
+
"Degraded: opens GitCode issue page, does not create/checkout local branch",
|
|
106
|
+
),
|
|
107
|
+
_e(
|
|
108
|
+
"issue delete",
|
|
109
|
+
None,
|
|
110
|
+
CompatStatus.IMPLEMENTED,
|
|
111
|
+
"Uses unofficial GitCode endpoint; unstable (PR #83)",
|
|
112
|
+
),
|
|
113
|
+
_e("issue lock", None, CompatStatus.NOT_IN_CLI),
|
|
114
|
+
_e("issue pin", None, CompatStatus.NOT_IN_CLI),
|
|
115
|
+
_e("issue transfer", None, CompatStatus.NOT_IN_CLI),
|
|
116
|
+
_e("issue unlock", None, CompatStatus.NOT_IN_CLI),
|
|
117
|
+
_e("issue unpin", None, CompatStatus.NOT_IN_CLI),
|
|
118
|
+
# --- issue flags ---
|
|
119
|
+
_e("issue list", "-R/--repo", CompatStatus.IMPLEMENTED),
|
|
120
|
+
_e("issue list", "-s/--state", CompatStatus.IMPLEMENTED),
|
|
121
|
+
_e("issue list", "-l/--label", CompatStatus.IMPLEMENTED),
|
|
122
|
+
_e("issue list", "-A/--author", CompatStatus.IMPLEMENTED),
|
|
123
|
+
_e("issue list", "-a/--assignee", CompatStatus.IMPLEMENTED),
|
|
124
|
+
_e("issue list", "--milestone", CompatStatus.IMPLEMENTED),
|
|
125
|
+
_e("issue list", "--mention", CompatStatus.IMPLEMENTED),
|
|
126
|
+
_e(
|
|
127
|
+
"issue list",
|
|
128
|
+
"--app",
|
|
129
|
+
CompatStatus.UNSUPPORTED,
|
|
130
|
+
"GitCode issue API does not support application filtering",
|
|
131
|
+
unsupported_key="ISSUE_LIST_APP",
|
|
132
|
+
),
|
|
133
|
+
_e("issue list", "-S/--search", CompatStatus.IMPLEMENTED),
|
|
134
|
+
_e("issue list", "-L/--limit", CompatStatus.IMPLEMENTED),
|
|
135
|
+
_e("issue list", "-w/--web", CompatStatus.IMPLEMENTED),
|
|
136
|
+
_e("issue list", "--json", CompatStatus.IMPLEMENTED),
|
|
137
|
+
_e("issue list", "-q/--jq", CompatStatus.IMPLEMENTED),
|
|
138
|
+
_e("issue list", "-t/--template", CompatStatus.IMPLEMENTED),
|
|
139
|
+
_e(
|
|
140
|
+
"issue list",
|
|
141
|
+
"-m",
|
|
142
|
+
CompatStatus.NOT_IN_CLI,
|
|
143
|
+
"gh short alias clashes with gc; gc exposes --milestone only",
|
|
144
|
+
),
|
|
145
|
+
_e("issue view", "-R/--repo", CompatStatus.IMPLEMENTED),
|
|
146
|
+
_e("issue view", "-w/--web", CompatStatus.IMPLEMENTED),
|
|
147
|
+
_e("issue view", "-c/--comments", CompatStatus.IMPLEMENTED),
|
|
148
|
+
_e("issue view", "--json", CompatStatus.IMPLEMENTED),
|
|
149
|
+
_e("issue view", "-q/--jq", CompatStatus.IMPLEMENTED),
|
|
150
|
+
_e("issue view", "-t/--template", CompatStatus.IMPLEMENTED),
|
|
151
|
+
_e("issue create", "-R/--repo", CompatStatus.IMPLEMENTED),
|
|
152
|
+
_e("issue create", "-t/--title", CompatStatus.IMPLEMENTED),
|
|
153
|
+
_e("issue create", "-b/--body", CompatStatus.IMPLEMENTED),
|
|
154
|
+
_e("issue create", "-a/--assignee", CompatStatus.IMPLEMENTED),
|
|
155
|
+
_e("issue create", "-e/--editor", CompatStatus.IMPLEMENTED),
|
|
156
|
+
_e("issue create", "-l/--label", CompatStatus.IMPLEMENTED),
|
|
157
|
+
_e("issue create", "-m/--milestone", CompatStatus.IMPLEMENTED),
|
|
158
|
+
_e("issue create", "-F/--body-file", CompatStatus.IMPLEMENTED),
|
|
159
|
+
_e("issue create", "-w/--web", CompatStatus.IMPLEMENTED),
|
|
160
|
+
_e("issue create", "--json", CompatStatus.IMPLEMENTED),
|
|
161
|
+
_e("issue create", "-q/--jq", CompatStatus.IMPLEMENTED),
|
|
162
|
+
_e(
|
|
163
|
+
"issue create",
|
|
164
|
+
"-T/--template",
|
|
165
|
+
CompatStatus.IMPLEMENTED,
|
|
166
|
+
"Treated as body template file (PR #91)",
|
|
167
|
+
),
|
|
168
|
+
_e("issue create", "-p/--project", CompatStatus.NOT_IN_CLI, "Project association not implemented"),
|
|
169
|
+
_e("issue create", "--recover", CompatStatus.NOT_IN_CLI, "Draft recovery not implemented"),
|
|
170
|
+
_e("issue close", "-R/--repo", CompatStatus.IMPLEMENTED),
|
|
171
|
+
_e("issue close", "-c/--comment", CompatStatus.IMPLEMENTED),
|
|
172
|
+
_e(
|
|
173
|
+
"issue close",
|
|
174
|
+
"-r/--reason",
|
|
175
|
+
CompatStatus.IMPLEMENTED,
|
|
176
|
+
"Supports completed/not_planned; gh additionally supports duplicate",
|
|
177
|
+
),
|
|
178
|
+
_e(
|
|
179
|
+
"issue close",
|
|
180
|
+
"--duplicate-of",
|
|
181
|
+
CompatStatus.IMPLEMENTED,
|
|
182
|
+
"GitCode-limited approximation: posts duplicate notice then closes (PR #89)",
|
|
183
|
+
),
|
|
184
|
+
_e("issue comment", "-R/--repo", CompatStatus.IMPLEMENTED),
|
|
185
|
+
_e("issue comment", "-b/--body", CompatStatus.IMPLEMENTED),
|
|
186
|
+
_e("issue comment", "-F/--body-file", CompatStatus.IMPLEMENTED),
|
|
187
|
+
_e("issue comment", "-e/--editor", CompatStatus.IMPLEMENTED),
|
|
188
|
+
_e("issue comment", "-w/--web", CompatStatus.IMPLEMENTED),
|
|
189
|
+
_e("issue comment", "--create-if-none", CompatStatus.IMPLEMENTED),
|
|
190
|
+
_e("issue comment", "--delete-last", CompatStatus.IMPLEMENTED),
|
|
191
|
+
_e("issue comment", "--edit-last", CompatStatus.IMPLEMENTED),
|
|
192
|
+
_e("issue comment", "--yes", CompatStatus.IMPLEMENTED),
|
|
193
|
+
_e("issue edit", "-R/--repo", CompatStatus.IMPLEMENTED),
|
|
194
|
+
_e("issue edit", "-t/--title", CompatStatus.IMPLEMENTED),
|
|
195
|
+
_e("issue edit", "-b/--body", CompatStatus.IMPLEMENTED),
|
|
196
|
+
_e("issue edit", "-F/--body-file", CompatStatus.IMPLEMENTED),
|
|
197
|
+
_e("issue edit", "-a/--add-assignee", CompatStatus.IMPLEMENTED),
|
|
198
|
+
_e("issue edit", "-l/--add-label", CompatStatus.IMPLEMENTED),
|
|
199
|
+
_e("issue edit", "-m/--milestone", CompatStatus.IMPLEMENTED),
|
|
200
|
+
_e("issue edit", "--remove-milestone", CompatStatus.IMPLEMENTED),
|
|
201
|
+
_e("issue edit", "--remove-assignee", CompatStatus.IMPLEMENTED, "Implemented (PR #120)"),
|
|
202
|
+
_e("issue edit", "--remove-label", CompatStatus.IMPLEMENTED, "Implemented (PR #120)"),
|
|
203
|
+
_e("issue edit", "--add-project", CompatStatus.NOT_IN_CLI),
|
|
204
|
+
_e("issue edit", "--remove-project", CompatStatus.NOT_IN_CLI),
|
|
205
|
+
_e("issue reopen", "-R/--repo", CompatStatus.IMPLEMENTED),
|
|
206
|
+
_e("issue reopen", "-c/--comment", CompatStatus.IMPLEMENTED, "Implemented (PR #122)"),
|
|
207
|
+
_e("issue status", "-R/--repo", CompatStatus.IMPLEMENTED),
|
|
208
|
+
_e("issue status", "--json", CompatStatus.IMPLEMENTED, "Implemented (PR #113)"),
|
|
209
|
+
_e("issue status", "-q/--jq", CompatStatus.IMPLEMENTED, "Implemented (PR #113)"),
|
|
210
|
+
_e("issue status", "-t/--template", CompatStatus.IMPLEMENTED, "Implemented (PR #113)"),
|
|
211
|
+
_e("issue develop", "-R/--repo", CompatStatus.IMPLEMENTED),
|
|
212
|
+
_e(
|
|
213
|
+
"issue develop",
|
|
214
|
+
"-b/--base",
|
|
215
|
+
CompatStatus.UNSUPPORTED,
|
|
216
|
+
"GitCode API does not provide issue develop branch API",
|
|
217
|
+
unsupported_key="ISSUE_DEVELOP_BASE",
|
|
218
|
+
),
|
|
219
|
+
_e(
|
|
220
|
+
"issue develop",
|
|
221
|
+
"-n/--name",
|
|
222
|
+
CompatStatus.UNSUPPORTED,
|
|
223
|
+
"GitCode API does not provide issue develop branch API",
|
|
224
|
+
unsupported_key="ISSUE_DEVELOP_NAME",
|
|
225
|
+
),
|
|
226
|
+
_e("issue develop", "-c/--checkout", CompatStatus.NOT_IN_CLI),
|
|
227
|
+
_e("issue develop", "-l/--list", CompatStatus.NOT_IN_CLI),
|
|
228
|
+
_e("issue develop", "--branch-repo", CompatStatus.NOT_IN_CLI),
|
|
229
|
+
_e("issue delete", "-R/--repo", CompatStatus.IMPLEMENTED),
|
|
230
|
+
_e("issue delete", "--yes", CompatStatus.IMPLEMENTED),
|
|
231
|
+
# --- pr subcommands ---
|
|
232
|
+
_e("pr list", None, CompatStatus.IMPLEMENTED, "Alias: ls"),
|
|
233
|
+
_e("pr view", None, CompatStatus.IMPLEMENTED),
|
|
234
|
+
_e("pr create", None, CompatStatus.IMPLEMENTED, "Alias: new"),
|
|
235
|
+
_e("pr close", None, CompatStatus.IMPLEMENTED),
|
|
236
|
+
_e("pr merge", None, CompatStatus.IMPLEMENTED),
|
|
237
|
+
_e("pr comment", None, CompatStatus.IMPLEMENTED),
|
|
238
|
+
_e(
|
|
239
|
+
"pr review",
|
|
240
|
+
None,
|
|
241
|
+
CompatStatus.IMPLEMENTED,
|
|
242
|
+
"Degraded: --request-changes falls back to plain comment (PR_REVIEW_REQUEST_CHANGES)",
|
|
243
|
+
),
|
|
244
|
+
_e("pr reopen", None, CompatStatus.IMPLEMENTED),
|
|
245
|
+
_e("pr edit", None, CompatStatus.IMPLEMENTED),
|
|
246
|
+
_e("pr diff", None, CompatStatus.IMPLEMENTED),
|
|
247
|
+
_e("pr checkout", None, CompatStatus.IMPLEMENTED),
|
|
248
|
+
_e("pr ready", None, CompatStatus.IMPLEMENTED),
|
|
249
|
+
_e(
|
|
250
|
+
"pr status",
|
|
251
|
+
None,
|
|
252
|
+
CompatStatus.IMPLEMENTED,
|
|
253
|
+
"Approximate: lacks gh user-specific filtering (PR_STATUS_GH_SEMANTICS)",
|
|
254
|
+
),
|
|
255
|
+
_e("pr checks", None, CompatStatus.NOT_IN_CLI),
|
|
256
|
+
_e("pr lock", None, CompatStatus.NOT_IN_CLI),
|
|
257
|
+
_e("pr revert", None, CompatStatus.NOT_IN_CLI),
|
|
258
|
+
_e("pr unlock", None, CompatStatus.NOT_IN_CLI),
|
|
259
|
+
_e("pr update-branch", None, CompatStatus.NOT_IN_CLI),
|
|
260
|
+
# --- pr flags ---
|
|
261
|
+
_e("pr list", "-R/--repo", CompatStatus.IMPLEMENTED),
|
|
262
|
+
_e("pr list", "-s/--state", CompatStatus.IMPLEMENTED),
|
|
263
|
+
_e("pr list", "-A/--author", CompatStatus.IMPLEMENTED),
|
|
264
|
+
_e("pr list", "-B/--base", CompatStatus.IMPLEMENTED),
|
|
265
|
+
_e("pr list", "-a/--assignee", CompatStatus.IMPLEMENTED),
|
|
266
|
+
_e("pr list", "-d/--draft", CompatStatus.IMPLEMENTED),
|
|
267
|
+
_e("pr list", "-H/--head", CompatStatus.IMPLEMENTED),
|
|
268
|
+
_e("pr list", "-l/--label", CompatStatus.IMPLEMENTED),
|
|
269
|
+
_e("pr list", "-S/--search", CompatStatus.IMPLEMENTED),
|
|
270
|
+
_e("pr list", "-L/--limit", CompatStatus.IMPLEMENTED),
|
|
271
|
+
_e("pr list", "-w/--web", CompatStatus.IMPLEMENTED),
|
|
272
|
+
_e("pr list", "--json", CompatStatus.IMPLEMENTED),
|
|
273
|
+
_e("pr list", "-q/--jq", CompatStatus.IMPLEMENTED),
|
|
274
|
+
_e("pr list", "-t/--template", CompatStatus.IMPLEMENTED),
|
|
275
|
+
_e("pr list", "--app", CompatStatus.NOT_IN_CLI, "App filtering not implemented"),
|
|
276
|
+
_e("pr view", "-R/--repo", CompatStatus.IMPLEMENTED),
|
|
277
|
+
_e("pr view", "-w/--web", CompatStatus.IMPLEMENTED),
|
|
278
|
+
_e("pr view", "-c/--comments", CompatStatus.IMPLEMENTED),
|
|
279
|
+
_e("pr view", "--json", CompatStatus.IMPLEMENTED),
|
|
280
|
+
_e("pr view", "-q/--jq", CompatStatus.IMPLEMENTED),
|
|
281
|
+
_e("pr view", "-t/--template", CompatStatus.IMPLEMENTED),
|
|
282
|
+
_e("pr create", "-R/--repo", CompatStatus.IMPLEMENTED),
|
|
283
|
+
_e("pr create", "-t/--title", CompatStatus.IMPLEMENTED),
|
|
284
|
+
_e("pr create", "-b/--body", CompatStatus.IMPLEMENTED),
|
|
285
|
+
_e("pr create", "-F/--body-file", CompatStatus.IMPLEMENTED),
|
|
286
|
+
_e("pr create", "-e/--editor", CompatStatus.IMPLEMENTED),
|
|
287
|
+
_e("pr create", "-f/--fill", CompatStatus.IMPLEMENTED),
|
|
288
|
+
_e("pr create", "--fill-first", CompatStatus.IMPLEMENTED),
|
|
289
|
+
_e("pr create", "--fill-verbose", CompatStatus.IMPLEMENTED),
|
|
290
|
+
_e("pr create", "--dry-run", CompatStatus.IMPLEMENTED),
|
|
291
|
+
_e("pr create", "-B/--base", CompatStatus.IMPLEMENTED),
|
|
292
|
+
_e("pr create", "-H/--head", CompatStatus.IMPLEMENTED),
|
|
293
|
+
_e("pr create", "-d/--draft", CompatStatus.IMPLEMENTED),
|
|
294
|
+
_e("pr create", "-m/--milestone", CompatStatus.IMPLEMENTED),
|
|
295
|
+
_e("pr create", "-l/--label", CompatStatus.IMPLEMENTED),
|
|
296
|
+
_e("pr create", "-r/--reviewer", CompatStatus.IMPLEMENTED),
|
|
297
|
+
_e("pr create", "-a/--assignee", CompatStatus.IMPLEMENTED),
|
|
298
|
+
_e("pr create", "-w/--web", CompatStatus.IMPLEMENTED),
|
|
299
|
+
_e("pr create", "--json", CompatStatus.IMPLEMENTED),
|
|
300
|
+
_e("pr create", "-q/--jq", CompatStatus.IMPLEMENTED),
|
|
301
|
+
_e(
|
|
302
|
+
"pr create",
|
|
303
|
+
"-T/--template",
|
|
304
|
+
CompatStatus.IMPLEMENTED,
|
|
305
|
+
"Treated as body template file (PR #91)",
|
|
306
|
+
),
|
|
307
|
+
_e("pr create", "-p/--project", CompatStatus.NOT_IN_CLI, "Project association not implemented"),
|
|
308
|
+
_e("pr create", "--recover", CompatStatus.NOT_IN_CLI, "Draft recovery not implemented"),
|
|
309
|
+
_e("pr create", "--no-maintainer-edit", CompatStatus.NOT_IN_CLI, "Maintainer-edit controls not implemented"),
|
|
310
|
+
_e("pr close", "-R/--repo", CompatStatus.IMPLEMENTED),
|
|
311
|
+
_e("pr close", "-c/--comment", CompatStatus.IMPLEMENTED),
|
|
312
|
+
_e("pr close", "-d/--delete-branch", CompatStatus.IMPLEMENTED),
|
|
313
|
+
_e("pr merge", "-R/--repo", CompatStatus.IMPLEMENTED),
|
|
314
|
+
_e("pr merge", "-m/--merge", CompatStatus.IMPLEMENTED),
|
|
315
|
+
_e("pr merge", "-s/--squash", CompatStatus.IMPLEMENTED),
|
|
316
|
+
_e("pr merge", "-r/--rebase", CompatStatus.IMPLEMENTED),
|
|
317
|
+
_e("pr merge", "-d/--delete-branch", CompatStatus.IMPLEMENTED),
|
|
318
|
+
_e("pr merge", "-b/--body", CompatStatus.IMPLEMENTED),
|
|
319
|
+
_e("pr merge", "-F/--body-file", CompatStatus.IMPLEMENTED),
|
|
320
|
+
_e("pr merge", "-t/--subject", CompatStatus.IMPLEMENTED),
|
|
321
|
+
_e(
|
|
322
|
+
"pr merge",
|
|
323
|
+
"-A/--author-email",
|
|
324
|
+
CompatStatus.UNSUPPORTED,
|
|
325
|
+
"GitCode merge API does not support author-email override (PR #88)",
|
|
326
|
+
unsupported_key="PR_MERGE_AUTHOR_EMAIL",
|
|
327
|
+
),
|
|
328
|
+
_e(
|
|
329
|
+
"pr merge",
|
|
330
|
+
"--auto",
|
|
331
|
+
CompatStatus.UNSUPPORTED,
|
|
332
|
+
"GitCode merge API does not support auto-merge/merge-queue (PR #88)",
|
|
333
|
+
unsupported_key="PR_MERGE_AUTO",
|
|
334
|
+
),
|
|
335
|
+
_e("pr merge", "--admin", CompatStatus.IMPLEMENTED),
|
|
336
|
+
_e("pr merge", "--disable-auto", CompatStatus.NOT_IN_CLI),
|
|
337
|
+
_e("pr merge", "--match-head-commit", CompatStatus.NOT_IN_CLI),
|
|
338
|
+
_e("pr comment", "-R/--repo", CompatStatus.IMPLEMENTED),
|
|
339
|
+
_e("pr comment", "-b/--body", CompatStatus.IMPLEMENTED),
|
|
340
|
+
_e("pr comment", "-F/--body-file", CompatStatus.IMPLEMENTED),
|
|
341
|
+
_e("pr comment", "-e/--editor", CompatStatus.IMPLEMENTED),
|
|
342
|
+
_e("pr comment", "-w/--web", CompatStatus.IMPLEMENTED),
|
|
343
|
+
_e("pr comment", "--path", CompatStatus.IMPLEMENTED, "gc extension; not in gh", gc_extension=True),
|
|
344
|
+
_e("pr comment", "--position", CompatStatus.IMPLEMENTED, "gc extension; not in gh", gc_extension=True),
|
|
345
|
+
_e("pr comment", "--line", CompatStatus.IMPLEMENTED, "gc extension; not in gh", gc_extension=True),
|
|
346
|
+
_e("pr comment", "--side", CompatStatus.IMPLEMENTED, "gc extension; not in gh", gc_extension=True),
|
|
347
|
+
_e("pr comment", "--commit-id", CompatStatus.IMPLEMENTED, "gc extension; not in gh", gc_extension=True),
|
|
348
|
+
_e(
|
|
349
|
+
"pr comment",
|
|
350
|
+
"--create-if-none",
|
|
351
|
+
CompatStatus.PENDING,
|
|
352
|
+
"Click guard added; history management not yet implemented",
|
|
353
|
+
pending_key="pr comment --create-if-none",
|
|
354
|
+
),
|
|
355
|
+
_e(
|
|
356
|
+
"pr comment",
|
|
357
|
+
"--delete-last",
|
|
358
|
+
CompatStatus.PENDING,
|
|
359
|
+
"Click guard added; history management not yet implemented",
|
|
360
|
+
pending_key="pr comment --delete-last",
|
|
361
|
+
),
|
|
362
|
+
_e(
|
|
363
|
+
"pr comment",
|
|
364
|
+
"--edit-last",
|
|
365
|
+
CompatStatus.PENDING,
|
|
366
|
+
"Click guard added; history management not yet implemented",
|
|
367
|
+
pending_key="pr comment --edit-last",
|
|
368
|
+
),
|
|
369
|
+
_e(
|
|
370
|
+
"pr comment",
|
|
371
|
+
"--yes",
|
|
372
|
+
CompatStatus.PENDING,
|
|
373
|
+
"Click guard added; confirmation skip not yet implemented",
|
|
374
|
+
pending_key="pr comment --yes",
|
|
375
|
+
),
|
|
376
|
+
_e("pr review", "-R/--repo", CompatStatus.IMPLEMENTED),
|
|
377
|
+
_e("pr review", "-a/--approve", CompatStatus.IMPLEMENTED),
|
|
378
|
+
_e("pr review", "-b/--body", CompatStatus.IMPLEMENTED),
|
|
379
|
+
_e("pr review", "-c/--comment", CompatStatus.IMPLEMENTED),
|
|
380
|
+
_e(
|
|
381
|
+
"pr review",
|
|
382
|
+
"-r/--request-changes",
|
|
383
|
+
CompatStatus.IMPLEMENTED,
|
|
384
|
+
"Degraded: posts plain comment (PR_REVIEW_REQUEST_CHANGES)",
|
|
385
|
+
),
|
|
386
|
+
_e("pr review", "--force", CompatStatus.IMPLEMENTED, "gc extension", gc_extension=True),
|
|
387
|
+
_e("pr review", "-F/--body-file", CompatStatus.IMPLEMENTED),
|
|
388
|
+
_e("pr reopen", "-R/--repo", CompatStatus.IMPLEMENTED),
|
|
389
|
+
_e(
|
|
390
|
+
"pr reopen",
|
|
391
|
+
"-c/--comment",
|
|
392
|
+
CompatStatus.PENDING,
|
|
393
|
+
"Click guard added; gh supports reopening with comment",
|
|
394
|
+
pending_key="pr reopen --comment",
|
|
395
|
+
),
|
|
396
|
+
_e("pr edit", "-R/--repo", CompatStatus.IMPLEMENTED),
|
|
397
|
+
_e("pr edit", "-t/--title", CompatStatus.IMPLEMENTED),
|
|
398
|
+
_e("pr edit", "-b/--body", CompatStatus.IMPLEMENTED),
|
|
399
|
+
_e("pr edit", "-F/--body-file", CompatStatus.IMPLEMENTED),
|
|
400
|
+
_e(
|
|
401
|
+
"pr edit",
|
|
402
|
+
"-B/--base",
|
|
403
|
+
CompatStatus.UNSUPPORTED,
|
|
404
|
+
"GitCode PR update API does not support base branch changes (PR #137)",
|
|
405
|
+
unsupported_key="PR_EDIT_BASE",
|
|
406
|
+
),
|
|
407
|
+
_e("pr edit", "-a/--add-assignee", CompatStatus.IMPLEMENTED),
|
|
408
|
+
_e("pr edit", "-l/--add-label", CompatStatus.IMPLEMENTED),
|
|
409
|
+
_e("pr edit", "-r/--add-reviewer", CompatStatus.IMPLEMENTED),
|
|
410
|
+
_e("pr edit", "--remove-assignee", CompatStatus.IMPLEMENTED),
|
|
411
|
+
_e("pr edit", "--remove-label", CompatStatus.IMPLEMENTED),
|
|
412
|
+
_e("pr edit", "--remove-reviewer", CompatStatus.IMPLEMENTED),
|
|
413
|
+
_e("pr edit", "-m/--milestone", CompatStatus.IMPLEMENTED),
|
|
414
|
+
_e("pr edit", "--remove-milestone", CompatStatus.IMPLEMENTED),
|
|
415
|
+
_e("pr edit", "--add-project", CompatStatus.NOT_IN_CLI),
|
|
416
|
+
_e("pr edit", "--remove-project", CompatStatus.NOT_IN_CLI),
|
|
417
|
+
_e("pr diff", "-R/--repo", CompatStatus.IMPLEMENTED),
|
|
418
|
+
_e("pr diff", "--color", CompatStatus.NOT_IN_CLI),
|
|
419
|
+
_e("pr diff", "-e/--exclude", CompatStatus.NOT_IN_CLI),
|
|
420
|
+
_e(
|
|
421
|
+
"pr diff",
|
|
422
|
+
"--name-only",
|
|
423
|
+
CompatStatus.PENDING,
|
|
424
|
+
"Click guard added; behaviour not yet implemented",
|
|
425
|
+
pending_key="pr diff --name-only",
|
|
426
|
+
),
|
|
427
|
+
_e(
|
|
428
|
+
"pr diff",
|
|
429
|
+
"--patch",
|
|
430
|
+
CompatStatus.PENDING,
|
|
431
|
+
"Click guard added; behaviour not yet implemented",
|
|
432
|
+
pending_key="pr diff --patch",
|
|
433
|
+
),
|
|
434
|
+
_e("pr diff", "-w/--web", CompatStatus.NOT_IN_CLI),
|
|
435
|
+
_e("pr checkout", "-R/--repo", CompatStatus.IMPLEMENTED),
|
|
436
|
+
_e("pr checkout", "-b/--branch", CompatStatus.IMPLEMENTED),
|
|
437
|
+
_e(
|
|
438
|
+
"pr checkout",
|
|
439
|
+
"--detach",
|
|
440
|
+
CompatStatus.PENDING,
|
|
441
|
+
"Click guard added; behaviour not yet implemented",
|
|
442
|
+
pending_key="pr checkout --detach",
|
|
443
|
+
),
|
|
444
|
+
_e(
|
|
445
|
+
"pr checkout",
|
|
446
|
+
"-f/--force",
|
|
447
|
+
CompatStatus.PENDING,
|
|
448
|
+
"Click guard added; behaviour not yet implemented",
|
|
449
|
+
pending_key="pr checkout --force",
|
|
450
|
+
),
|
|
451
|
+
_e("pr checkout", "--recurse-submodules", CompatStatus.NOT_IN_CLI),
|
|
452
|
+
_e("pr ready", "-R/--repo", CompatStatus.IMPLEMENTED),
|
|
453
|
+
_e("pr ready", "--undo", CompatStatus.IMPLEMENTED),
|
|
454
|
+
_e("pr status", "-R/--repo", CompatStatus.IMPLEMENTED),
|
|
455
|
+
_e("pr status", "-c/--conflict-status", CompatStatus.NOT_IN_CLI),
|
|
456
|
+
_e("pr status", "--json", CompatStatus.NOT_IN_CLI),
|
|
457
|
+
_e("pr status", "-q/--jq", CompatStatus.NOT_IN_CLI),
|
|
458
|
+
_e("pr status", "-t/--template", CompatStatus.NOT_IN_CLI),
|
|
459
|
+
]
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
"""Authoritative registry of ``gc`` gh-compatibility status.
|
|
2
|
+
|
|
3
|
+
This module is the single source of truth for the ``gc`` ↔ ``gh`` compatibility
|
|
4
|
+
matrix that previously lived in GitCode issue #68. Tracking the matrix in the
|
|
5
|
+
issue body drifted every time a flag was implemented, marked unsupported, or
|
|
6
|
+
reclassified; CI now enforces that the registry, the Click command tree, and the
|
|
7
|
+
runtime guards stay in lockstep.
|
|
8
|
+
|
|
9
|
+
Status taxonomy
|
|
10
|
+
---------------
|
|
11
|
+
* ``implemented`` — Click option exists, behaviour matches ``gh`` semantics
|
|
12
|
+
(or a clearly documented approximation).
|
|
13
|
+
* ``pending`` — Click option exists; invoking it raises a
|
|
14
|
+
``_pending_gh_compat("<pending_key>")`` ClickException. The optional
|
|
15
|
+
``pending_key`` field is the literal string passed to that helper.
|
|
16
|
+
* ``unsupported`` — Click option exists; invoking it raises
|
|
17
|
+
``unsupported("<unsupported_key>")``. The key must be present in
|
|
18
|
+
``adapters.capabilities.CAPABILITY_MESSAGES``.
|
|
19
|
+
* ``not_in_cli`` — ``gh`` exposes the flag but ``gc`` does not. Invoking it
|
|
20
|
+
surfaces a Click "no such option" error.
|
|
21
|
+
|
|
22
|
+
Entries with ``flag is None`` describe a subcommand (e.g. ``issue lock``); the
|
|
23
|
+
status applies to the subcommand as a whole.
|
|
24
|
+
|
|
25
|
+
Adding or reclassifying an entry
|
|
26
|
+
--------------------------------
|
|
27
|
+
Edit this file in the same PR that changes the corresponding command code. The
|
|
28
|
+
``tests/unit/compat/test_registry_consistency.py`` suite enforces that every
|
|
29
|
+
``pending`` entry has a matching runtime guard, every ``unsupported`` entry has a
|
|
30
|
+
matching capability message, every ``implemented``/``pending``/``unsupported``
|
|
31
|
+
flag is actually registered with Click, and every ``not_in_cli`` flag is
|
|
32
|
+
absent.
|
|
33
|
+
"""
|
|
34
|
+
|
|
35
|
+
from __future__ import annotations
|
|
36
|
+
|
|
37
|
+
from collections.abc import Iterable, Iterator
|
|
38
|
+
from typing import Final
|
|
39
|
+
|
|
40
|
+
from ._types import CompatEntry, CompatStatus
|
|
41
|
+
from .entries import ENTRIES
|
|
42
|
+
|
|
43
|
+
# A tuple of ``CompatEntry`` keyed by ``(command, flag)``. ``flag is None``
|
|
44
|
+
# represents a subcommand-level entry. The tuple is the registry; do not mutate.
|
|
45
|
+
_ENTRIES: Final[tuple[CompatEntry, ...]] = tuple(ENTRIES)
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def iter_entries(
|
|
49
|
+
*,
|
|
50
|
+
groups: Iterable[str] | None = None,
|
|
51
|
+
statuses: Iterable[CompatStatus] | None = None,
|
|
52
|
+
) -> Iterator[CompatEntry]:
|
|
53
|
+
"""Iterate entries, optionally filtered by top-level group and/or status."""
|
|
54
|
+
group_set = set(groups) if groups is not None else None
|
|
55
|
+
status_set = set(statuses) if statuses is not None else None
|
|
56
|
+
for entry in _ENTRIES:
|
|
57
|
+
if group_set is not None:
|
|
58
|
+
top = entry.command.split(" ", 1)[0]
|
|
59
|
+
if top not in group_set:
|
|
60
|
+
continue
|
|
61
|
+
if status_set is not None and entry.status not in status_set:
|
|
62
|
+
continue
|
|
63
|
+
yield entry
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def statistics() -> dict[str, dict[str, int]]:
|
|
67
|
+
"""Aggregate counts per top-level command group, row kind, and status.
|
|
68
|
+
|
|
69
|
+
Returns ``{group: {status_label: count, ...}, ...}``. ``group`` is one of
|
|
70
|
+
``auth``/``issue``/``pr``. Subcommand rows (``flag is None``) are counted
|
|
71
|
+
separately from flag rows so the totals match the structure of issue #68.
|
|
72
|
+
"""
|
|
73
|
+
|
|
74
|
+
counts: dict[str, dict[str, int]] = {}
|
|
75
|
+
for entry in _ENTRIES:
|
|
76
|
+
top = entry.command.split(" ", 1)[0]
|
|
77
|
+
bucket = counts.setdefault(top, {})
|
|
78
|
+
key = f"{entry.status.value}_{'subcommand' if entry.flag is None else 'flag'}"
|
|
79
|
+
bucket[key] = bucket.get(key, 0) + 1
|
|
80
|
+
return counts
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
def render_markdown_report() -> str:
|
|
84
|
+
"""Render the registry as Markdown mirroring the issue #68 layout."""
|
|
85
|
+
lines: list[str] = [
|
|
86
|
+
"# gc ↔ gh compatibility matrix",
|
|
87
|
+
"",
|
|
88
|
+
"Auto-generated from `gitcode_cli.compat.registry`. Do not edit by hand; "
|
|
89
|
+
"edit `src/gitcode_cli/compat/entries.py` and run `gc compat report`.",
|
|
90
|
+
"",
|
|
91
|
+
"## Status legend",
|
|
92
|
+
"",
|
|
93
|
+
"| Status | Meaning |",
|
|
94
|
+
"|---|---|",
|
|
95
|
+
"| **implemented** | CLI placeholder + behaviour implemented |",
|
|
96
|
+
"| **pending** | CLI placeholder + flag parses but raises `_pending_gh_compat()` |",
|
|
97
|
+
"| **unsupported** | CLI placeholder + adapter marks it unsupported (GitCode API limit) |",
|
|
98
|
+
"| **not_in_cli** | `gh` baseline exposes it but `gc` does not |",
|
|
99
|
+
"",
|
|
100
|
+
]
|
|
101
|
+
for group in ("auth", "issue", "pr"):
|
|
102
|
+
sub_entries = [e for e in iter_entries(groups=[group]) if e.flag is None]
|
|
103
|
+
flag_entries = [e for e in iter_entries(groups=[group]) if e.flag is not None]
|
|
104
|
+
if not sub_entries and not flag_entries:
|
|
105
|
+
continue
|
|
106
|
+
lines.append(f"## {group}")
|
|
107
|
+
lines.append("")
|
|
108
|
+
lines.append("### Subcommands")
|
|
109
|
+
lines.append("")
|
|
110
|
+
lines.append("| Subcommand | Status | Notes |")
|
|
111
|
+
lines.append("|---|---|---|")
|
|
112
|
+
for entry in sub_entries:
|
|
113
|
+
note = entry.note.replace("|", "\\|")
|
|
114
|
+
lines.append(f"| `{entry.command}` | {entry.status.value} | {note} |")
|
|
115
|
+
lines.append("")
|
|
116
|
+
lines.append("### Flags")
|
|
117
|
+
lines.append("")
|
|
118
|
+
lines.append("| Command | Flag | Status | Notes |")
|
|
119
|
+
lines.append("|---|---|---|---|")
|
|
120
|
+
for entry in flag_entries:
|
|
121
|
+
note = entry.note.replace("|", "\\|")
|
|
122
|
+
lines.append(f"| `{entry.command}` | `{entry.flag}` | {entry.status.value} | {note} |")
|
|
123
|
+
lines.append("")
|
|
124
|
+
|
|
125
|
+
lines.append("## Statistics")
|
|
126
|
+
lines.append("")
|
|
127
|
+
lines.append("| Group | implemented | pending | unsupported | not_in_cli | Total |")
|
|
128
|
+
lines.append("|---|---|---|---|---|---|")
|
|
129
|
+
grand = {"implemented": 0, "pending": 0, "unsupported": 0, "not_in_cli": 0}
|
|
130
|
+
for group in ("auth", "issue", "pr"):
|
|
131
|
+
bucket = statistics().get(group, {})
|
|
132
|
+
impl = bucket.get("implemented", 0)
|
|
133
|
+
pend = bucket.get("pending", 0)
|
|
134
|
+
unsup = bucket.get("unsupported", 0)
|
|
135
|
+
ninc = bucket.get("not_in_cli", 0)
|
|
136
|
+
total = impl + pend + unsup + ninc
|
|
137
|
+
lines.append(f"| {group} | {impl} | {pend} | {unsup} | {ninc} | {total} |")
|
|
138
|
+
for k, v in (("implemented", impl), ("pending", pend), ("unsupported", unsup), ("not_in_cli", ninc)):
|
|
139
|
+
grand[k] += v
|
|
140
|
+
gtotal = sum(grand.values())
|
|
141
|
+
lines.append(
|
|
142
|
+
f"| **Total** | **{grand['implemented']}** | **{grand['pending']}** | "
|
|
143
|
+
f"**{grand['unsupported']}** | **{grand['not_in_cli']}** | **{gtotal}** |"
|
|
144
|
+
)
|
|
145
|
+
lines.append("")
|
|
146
|
+
return "\n".join(lines)
|
|
@@ -15,6 +15,9 @@ class PullRequestService:
|
|
|
15
15
|
def get(self, owner: str, repo: str, number: int) -> Any | None:
|
|
16
16
|
return self.client.get(f"/repos/{owner}/{repo}/pulls/{number}")
|
|
17
17
|
|
|
18
|
+
def list_issues(self, owner: str, repo: str, number: int) -> Any | None:
|
|
19
|
+
return self.client.get(f"/repos/{owner}/{repo}/pulls/{number}/issues")
|
|
20
|
+
|
|
18
21
|
def create(self, owner: str, repo: str, **data: Any) -> Any | None:
|
|
19
22
|
return self.client.post(f"/repos/{owner}/{repo}/pulls", json={k: v for k, v in data.items() if v is not None})
|
|
20
23
|
|
|
@@ -42,7 +45,23 @@ class PullRequestService:
|
|
|
42
45
|
return self.client.post(f"/repos/{owner}/{repo}/pulls/{number}/review", json=filtered_payload)
|
|
43
46
|
|
|
44
47
|
def list_comments(self, owner: str, repo: str, number: int) -> Any | None:
|
|
45
|
-
|
|
48
|
+
per_page = 100
|
|
49
|
+
page = 1
|
|
50
|
+
comments: list[Any] = []
|
|
51
|
+
|
|
52
|
+
while True:
|
|
53
|
+
result = self.client.get(
|
|
54
|
+
f"/repos/{owner}/{repo}/pulls/{number}/comments",
|
|
55
|
+
params={"page": page, "per_page": per_page},
|
|
56
|
+
)
|
|
57
|
+
if not isinstance(result, list):
|
|
58
|
+
return comments if comments else result
|
|
59
|
+
|
|
60
|
+
comments.extend(result)
|
|
61
|
+
if len(result) < per_page:
|
|
62
|
+
return comments
|
|
63
|
+
|
|
64
|
+
page += 1
|
|
46
65
|
|
|
47
66
|
def diff(self, owner: str, repo: str, number: int) -> str:
|
|
48
67
|
response = self.client.request(
|
|
@@ -19,8 +19,13 @@ src/gitcode_cli/adapters/issues.py
|
|
|
19
19
|
src/gitcode_cli/adapters/pulls.py
|
|
20
20
|
src/gitcode_cli/commands/__init__.py
|
|
21
21
|
src/gitcode_cli/commands/auth.py
|
|
22
|
+
src/gitcode_cli/commands/compat.py
|
|
22
23
|
src/gitcode_cli/commands/issue.py
|
|
23
24
|
src/gitcode_cli/commands/pr.py
|
|
25
|
+
src/gitcode_cli/compat/__init__.py
|
|
26
|
+
src/gitcode_cli/compat/_types.py
|
|
27
|
+
src/gitcode_cli/compat/entries.py
|
|
28
|
+
src/gitcode_cli/compat/registry.py
|
|
24
29
|
src/gitcode_cli/services/__init__.py
|
|
25
30
|
src/gitcode_cli/services/issues.py
|
|
26
31
|
src/gitcode_cli/services/pulls.py
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|