ghnova 0.3.0__py3-none-any.whl

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.
Files changed (60) hide show
  1. ghnova/__init__.py +8 -0
  2. ghnova/__main__.py +8 -0
  3. ghnova/cli/__init__.py +1 -0
  4. ghnova/cli/config/__init__.py +1 -0
  5. ghnova/cli/config/add.py +48 -0
  6. ghnova/cli/config/delete.py +50 -0
  7. ghnova/cli/config/list.py +40 -0
  8. ghnova/cli/config/main.py +27 -0
  9. ghnova/cli/config/update.py +59 -0
  10. ghnova/cli/issue/__init__.py +7 -0
  11. ghnova/cli/issue/create.py +155 -0
  12. ghnova/cli/issue/get.py +119 -0
  13. ghnova/cli/issue/list.py +267 -0
  14. ghnova/cli/issue/lock.py +110 -0
  15. ghnova/cli/issue/main.py +31 -0
  16. ghnova/cli/issue/unlock.py +101 -0
  17. ghnova/cli/issue/update.py +164 -0
  18. ghnova/cli/main.py +117 -0
  19. ghnova/cli/repository/__init__.py +1 -0
  20. ghnova/cli/repository/list.py +201 -0
  21. ghnova/cli/repository/main.py +21 -0
  22. ghnova/cli/user/__init__.py +1 -0
  23. ghnova/cli/user/ctx_info.py +105 -0
  24. ghnova/cli/user/get.py +98 -0
  25. ghnova/cli/user/list.py +78 -0
  26. ghnova/cli/user/main.py +27 -0
  27. ghnova/cli/user/update.py +164 -0
  28. ghnova/cli/utils/__init__.py +7 -0
  29. ghnova/cli/utils/auth.py +67 -0
  30. ghnova/client/__init__.py +8 -0
  31. ghnova/client/async_github.py +121 -0
  32. ghnova/client/base.py +78 -0
  33. ghnova/client/github.py +107 -0
  34. ghnova/config/__init__.py +8 -0
  35. ghnova/config/manager.py +209 -0
  36. ghnova/config/model.py +58 -0
  37. ghnova/issue/__init__.py +8 -0
  38. ghnova/issue/async_issue.py +554 -0
  39. ghnova/issue/base.py +469 -0
  40. ghnova/issue/issue.py +584 -0
  41. ghnova/repository/__init__.py +8 -0
  42. ghnova/repository/async_repository.py +134 -0
  43. ghnova/repository/base.py +124 -0
  44. ghnova/repository/repository.py +134 -0
  45. ghnova/resource/__init__.py +8 -0
  46. ghnova/resource/async_resource.py +88 -0
  47. ghnova/resource/resource.py +88 -0
  48. ghnova/user/__init__.py +8 -0
  49. ghnova/user/async_user.py +285 -0
  50. ghnova/user/base.py +214 -0
  51. ghnova/user/user.py +285 -0
  52. ghnova/utils/__init__.py +16 -0
  53. ghnova/utils/log.py +70 -0
  54. ghnova/utils/response.py +67 -0
  55. ghnova/version.py +11 -0
  56. ghnova-0.3.0.dist-info/METADATA +194 -0
  57. ghnova-0.3.0.dist-info/RECORD +60 -0
  58. ghnova-0.3.0.dist-info/WHEEL +4 -0
  59. ghnova-0.3.0.dist-info/entry_points.txt +2 -0
  60. ghnova-0.3.0.dist-info/licenses/LICENSE +21 -0
ghnova/cli/main.py ADDED
@@ -0,0 +1,117 @@
1
+ # ruff: noqa PL0415
2
+ """Main entry point for the ghnova CLI application."""
3
+
4
+ from __future__ import annotations
5
+
6
+ import enum
7
+ from typing import Annotated
8
+
9
+ import typer
10
+
11
+
12
+ class LoggingLevel(str, enum.Enum):
13
+ """Logging levels for the CLI."""
14
+
15
+ NOTSET = "NOTSET"
16
+ DEBUG = "DEBUG"
17
+ INFO = "INFO"
18
+ WARNING = "WARNING"
19
+ ERROR = "ERROR"
20
+ CRITICAL = "CRITICAL"
21
+
22
+
23
+ # Create the main Typer app
24
+ app = typer.Typer(
25
+ name="ghnova",
26
+ help="Main CLI for ghnova.",
27
+ rich_markup_mode="rich",
28
+ )
29
+
30
+
31
+ def setup_logging(level: LoggingLevel = LoggingLevel.INFO) -> None:
32
+ """Set up logging with Rich handler.
33
+
34
+ Args:
35
+ level: Logging level.
36
+ """
37
+ import logging
38
+
39
+ from rich.console import Console
40
+ from rich.logging import RichHandler
41
+
42
+ logger = logging.getLogger("ghnova")
43
+
44
+ logger.setLevel(level.value)
45
+
46
+ console = Console(stderr=True)
47
+
48
+ # Remove any existing handlers to ensure RichHandler is used
49
+ for h in logger.handlers[:]: # Use slice copy to avoid modification during iteration
50
+ logger.removeHandler(h)
51
+ # Add the RichHandler
52
+
53
+ handler = RichHandler(
54
+ console=console,
55
+ rich_tracebacks=True,
56
+ show_time=True,
57
+ show_level=True, # Keep level (e.g., DEBUG, INFO) for clarity
58
+ markup=True, # Enable Rich markup in messages for styling
59
+ level=level.value, # Ensure handler respects the level
60
+ omit_repeated_times=False,
61
+ log_time_format="%H:%M",
62
+ )
63
+ handler.setLevel(level.value)
64
+ logger.addHandler(handler)
65
+
66
+ # Prevent propagation to root logger to avoid duplicate output
67
+ logger.propagate = False
68
+
69
+
70
+ @app.callback()
71
+ def main(
72
+ ctx: typer.Context,
73
+ config_path: Annotated[
74
+ str | None,
75
+ typer.Option(
76
+ "--config-path",
77
+ help="Path to the configuration file. If not provided, it uses the path specified by `GHNOVA_CONFIG_PATH`. If the environment variable is not defined, it uses the default location.",
78
+ ),
79
+ ] = None,
80
+ verbose: Annotated[
81
+ LoggingLevel,
82
+ typer.Option("--verbose", "-v", help="Set verbosity level."),
83
+ ] = LoggingLevel.INFO,
84
+ ) -> None:
85
+ """Main entry point for the CLI application.
86
+
87
+ Args:
88
+ ctx: Typer context.
89
+ config_path: Path to the configuration file.
90
+ verbose: Verbosity level for logging.
91
+
92
+ """
93
+
94
+ import os
95
+
96
+ config_path = config_path or os.getenv("GHNOVA_CONFIG_PATH")
97
+
98
+ ctx.obj = {"config_path": config_path}
99
+
100
+ setup_logging(verbose)
101
+
102
+
103
+ def register_commands() -> None:
104
+ """Register CLI commands."""
105
+
106
+ from ghnova.cli.config.main import config_app # noqa: PLC0415
107
+ from ghnova.cli.issue.main import issue_app # noqa: PLC0415
108
+ from ghnova.cli.repository.main import repository_app # noqa: PLC0415
109
+ from ghnova.cli.user.main import user_app # noqa: PLC0415
110
+
111
+ app.add_typer(config_app)
112
+ app.add_typer(issue_app)
113
+ app.add_typer(repository_app)
114
+ app.add_typer(user_app)
115
+
116
+
117
+ register_commands()
@@ -0,0 +1 @@
1
+ """Command line interface for repository-related operations."""
@@ -0,0 +1,201 @@
1
+ """List command for repository CLI."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from datetime import datetime
6
+ from typing import Annotated, Literal
7
+
8
+ import typer
9
+
10
+
11
+ def list_command( # noqa: PLR0913
12
+ ctx: typer.Context,
13
+ owner: Annotated[
14
+ str | None,
15
+ typer.Option(
16
+ "--owner",
17
+ help="The owner of the repositories.",
18
+ ),
19
+ ] = None,
20
+ organization: Annotated[
21
+ str | None,
22
+ typer.Option(
23
+ "--organization",
24
+ help="The organization name.",
25
+ ),
26
+ ] = None,
27
+ visibility: Annotated[
28
+ Literal["all", "public", "private"] | None,
29
+ typer.Option(
30
+ "--visibility",
31
+ help="Filter by visibility: all, public, or private.",
32
+ ),
33
+ ] = None,
34
+ affiliation: Annotated[
35
+ list[str] | None,
36
+ typer.Option(
37
+ "--affiliation",
38
+ help="Filter by affiliation: owner, collaborator, organization_member.",
39
+ ),
40
+ ] = None,
41
+ repository_type: Annotated[
42
+ Literal["all", "owner", "public", "private", "member"] | None,
43
+ typer.Option(
44
+ "--type",
45
+ help="Filter by repository type: all, owner, public, private, or member.",
46
+ ),
47
+ ] = None,
48
+ sort: Annotated[
49
+ Literal["created", "updated", "pushed", "full_name"] | None,
50
+ typer.Option(
51
+ "--sort",
52
+ help="Sort by: created, updated, pushed, or full_name.",
53
+ ),
54
+ ] = None,
55
+ direction: Annotated[
56
+ Literal["asc", "desc"] | None,
57
+ typer.Option(
58
+ "--direction",
59
+ help="Sort direction: asc or desc.",
60
+ ),
61
+ ] = None,
62
+ per_page: Annotated[
63
+ int,
64
+ typer.Option(
65
+ "--per-page",
66
+ help="Number of results per page.",
67
+ ),
68
+ ] = 30,
69
+ page: Annotated[
70
+ int,
71
+ typer.Option(
72
+ "--page",
73
+ help="Page number for pagination.",
74
+ ),
75
+ ] = 1,
76
+ since: Annotated[
77
+ datetime | None,
78
+ typer.Option(
79
+ "--since",
80
+ help="Only show repositories updated after this time (ISO 8601 format).",
81
+ ),
82
+ ] = None,
83
+ before: Annotated[
84
+ datetime | None,
85
+ typer.Option(
86
+ "--before",
87
+ help="Only show repositories updated before this time (ISO 8601 format).",
88
+ ),
89
+ ] = None,
90
+ etag: Annotated[
91
+ str | None,
92
+ typer.Option(
93
+ "--etag",
94
+ help="ETag header value for conditional requests.",
95
+ ),
96
+ ] = None,
97
+ last_modified: Annotated[
98
+ str | None,
99
+ typer.Option(
100
+ "--last-modified",
101
+ help="Last-Modified header value for conditional requests.",
102
+ ),
103
+ ] = None,
104
+ account_name: Annotated[
105
+ str | None,
106
+ typer.Option(
107
+ "--account-name",
108
+ help="Name of the account to use for authentication.",
109
+ ),
110
+ ] = None,
111
+ token: Annotated[
112
+ str | None,
113
+ typer.Option(
114
+ "--token",
115
+ help="Token for authentication. If not provided, the token from the specified account will be used.",
116
+ ),
117
+ ] = None,
118
+ base_url: Annotated[
119
+ str | None,
120
+ typer.Option(
121
+ "--base-url",
122
+ help="Base URL of the GitHub platform. If not provided, the base URL from the specified account will be used.",
123
+ ),
124
+ ] = None,
125
+ ) -> None:
126
+ """List repositories.
127
+
128
+ Args:
129
+ ctx: Typer context.
130
+ owner: The owner of the repositories.
131
+ organization: The organization name.
132
+ visibility: Filter by visibility: all, public, or private.
133
+ affiliation: Filter by affiliation: owner, collaborator, or organization_member.
134
+ repository_type: Filter by repository type: all, owner, public, private, or member.
135
+ sort: Sort by: created, updated, pushed, or full_name.
136
+ direction: Sort direction: asc or desc.
137
+ per_page: Number of results per page.
138
+ page: Page number for pagination.
139
+ since: Only show repositories updated after this time.
140
+ before: Only show repositories updated before this time.
141
+ etag: ETag header value for conditional requests.
142
+ last_modified: Last-Modified header value for conditional requests.
143
+ account_name: Name of the account to use for authentication.
144
+ token: Token for authentication.
145
+ base_url: Base URL of the GitHub platform.
146
+
147
+ """
148
+ import json # noqa: PLC0415
149
+ import logging # noqa: PLC0415
150
+ from typing import cast # noqa: PLC0415
151
+
152
+ from ghnova.cli.utils.auth import get_auth_params # noqa: PLC0415
153
+ from ghnova.client.github import GitHub # noqa: PLC0415
154
+
155
+ logger = logging.getLogger("ghnova")
156
+
157
+ token, base_url = get_auth_params(
158
+ config_path=ctx.obj["config_path"],
159
+ account_name=account_name,
160
+ token=token,
161
+ base_url=base_url,
162
+ )
163
+ affiliation_list = None
164
+ if affiliation:
165
+ if not all(a in {"owner", "collaborator", "organization_member"} for a in affiliation):
166
+ logger.error(
167
+ "Invalid affiliation value. Must be a comma-separated list of: owner, collaborator, organization_member."
168
+ )
169
+ raise typer.Exit(code=1)
170
+ affiliation_list = cast(list[Literal["owner", "collaborator", "organization_member"]], affiliation)
171
+
172
+ try:
173
+ with GitHub(token=token, base_url=base_url) as client:
174
+ repository_client = client.repository
175
+ data, status_code, etag_value, last_modified_value = repository_client.list_repositories(
176
+ owner=owner,
177
+ organization=organization,
178
+ visibility=visibility,
179
+ affiliation=affiliation_list,
180
+ repository_type=repository_type,
181
+ sort=sort,
182
+ direction=direction,
183
+ per_page=per_page,
184
+ page=page,
185
+ since=since,
186
+ before=before,
187
+ etag=etag,
188
+ last_modified=last_modified,
189
+ )
190
+ result = {
191
+ "data": data,
192
+ "metadata": {
193
+ "status_code": status_code,
194
+ "etag": etag_value,
195
+ "last_modified": last_modified_value,
196
+ },
197
+ }
198
+ print(json.dumps(result, indent=2, default=str))
199
+ except Exception as e:
200
+ logger.exception("Error listing repositories: %s", e)
201
+ raise typer.Exit(code=1) from e
@@ -0,0 +1,21 @@
1
+ """Repository CLI commands for ghnova."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import typer
6
+
7
+ repository_app = typer.Typer(
8
+ name="repository",
9
+ help="Manage git repositories.",
10
+ rich_markup_mode="rich",
11
+ )
12
+
13
+
14
+ def register_commands() -> None:
15
+ """Register repository subcommands."""
16
+ from ghnova.cli.repository.list import list_command # noqa: PLC0415
17
+
18
+ repository_app.command(name="list", help="List repositories.")(list_command)
19
+
20
+
21
+ register_commands()
@@ -0,0 +1 @@
1
+ """Command line interface for user-related operations."""
@@ -0,0 +1,105 @@
1
+ """Get contextual information command for user CLI."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from typing import Annotated
6
+
7
+ import typer
8
+
9
+
10
+ def contextual_information_command( # noqa: PLR0913
11
+ ctx: typer.Context,
12
+ account_name: Annotated[
13
+ str | None,
14
+ typer.Option(
15
+ "--account-name",
16
+ help="Name of the account to use for authentication.",
17
+ ),
18
+ ] = None,
19
+ token: Annotated[
20
+ str | None,
21
+ typer.Option(
22
+ "--token",
23
+ help="Token for authentication. If not provided, the token from the specified account will be used.",
24
+ ),
25
+ ] = None,
26
+ base_url: Annotated[
27
+ str | None,
28
+ typer.Option(
29
+ "--base-url",
30
+ help="Base URL of the GitHub platform. If not provided, the base URL from the specified account will be used.",
31
+ ),
32
+ ] = None,
33
+ username: Annotated[
34
+ str | None,
35
+ typer.Option(
36
+ "--username",
37
+ help="The username of the user to retrieve contextual information for.",
38
+ ),
39
+ ] = None,
40
+ subject_type: Annotated[
41
+ str | None,
42
+ typer.Option(
43
+ "--subject-type",
44
+ help="The type of subject for the hovercard (e.g., 'organization', 'repository', 'issue', 'pull_request').",
45
+ ),
46
+ ] = None,
47
+ subject_id: Annotated[
48
+ str | None,
49
+ typer.Option(
50
+ "--subject-id",
51
+ help="The ID of the subject for the hovercard.",
52
+ ),
53
+ ] = None,
54
+ ) -> None:
55
+ """Get contextual information about a user on GitHub.
56
+
57
+ Args:
58
+ ctx: Typer context.
59
+ username: The username of the user to retrieve contextual information for.
60
+ account_name: Name of the account to use for authentication.
61
+ token: Token for authentication.
62
+ base_url: Base URL of the GitHub platform.
63
+ subject_type: The type of subject for the hovercard.
64
+ subject_id: The ID of the subject for the hovercard.
65
+
66
+ """
67
+ import json # noqa: PLC0415
68
+ import logging # noqa: PLC0415
69
+
70
+ from ghnova.cli.utils.auth import get_auth_params # noqa: PLC0415
71
+ from ghnova.client.github import GitHub # noqa: PLC0415
72
+
73
+ logger = logging.getLogger("ghnova")
74
+
75
+ if username is None:
76
+ logger.error("Username must be provided to retrieve contextual information.")
77
+ raise typer.Exit(code=1)
78
+
79
+ token, base_url = get_auth_params(
80
+ config_path=ctx.obj["config_path"],
81
+ account_name=account_name,
82
+ token=token,
83
+ base_url=base_url,
84
+ )
85
+
86
+ try:
87
+ with GitHub(token=token, base_url=base_url) as client:
88
+ user_client = client.user
89
+ data, status_code, etag_value, last_modified_value = user_client.get_contextual_information(
90
+ username=username,
91
+ subject_type=subject_type,
92
+ subject_id=subject_id,
93
+ )
94
+ result = {
95
+ "data": data,
96
+ "metadata": {
97
+ "status_code": status_code,
98
+ "etag": etag_value,
99
+ "last_modified": last_modified_value,
100
+ },
101
+ }
102
+ print(json.dumps(result, indent=4))
103
+ except Exception as e:
104
+ logger.error("Error retrieving contextual information: %s", e)
105
+ raise typer.Exit(code=1) from e
ghnova/cli/user/get.py ADDED
@@ -0,0 +1,98 @@
1
+ """Get command for user CLI."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from typing import Annotated
6
+
7
+ import typer
8
+
9
+
10
+ def get_command( # noqa: PLR0913
11
+ ctx: typer.Context,
12
+ account_name: Annotated[
13
+ str | None,
14
+ typer.Option(
15
+ "--account-name",
16
+ help="Name of the account to use for authentication.",
17
+ ),
18
+ ] = None,
19
+ token: Annotated[
20
+ str | None,
21
+ typer.Option(
22
+ "--token",
23
+ help="Token for authentication. If not provided, the token from the specified account will be used.",
24
+ ),
25
+ ] = None,
26
+ base_url: Annotated[
27
+ str | None,
28
+ typer.Option(
29
+ "--base-url",
30
+ help="Base URL of the GitHub platform. If not provided, the base URL from the specified account will be used.",
31
+ ),
32
+ ] = None,
33
+ username: Annotated[
34
+ str | None,
35
+ typer.Option(
36
+ "--username",
37
+ help="Username of the user to retrieve. If not provided, the authenticated user's information will be retrieved.",
38
+ ),
39
+ ] = None,
40
+ account_id: Annotated[
41
+ int | None,
42
+ typer.Option(
43
+ "--account-id",
44
+ help="Account ID of the user to retrieve.",
45
+ ),
46
+ ] = None,
47
+ etag: Annotated[
48
+ str | None,
49
+ typer.Option("--etag", help="ETag from a previous request for caching purposes."),
50
+ ] = None,
51
+ last_modified: Annotated[
52
+ str | None,
53
+ typer.Option("--last-modified", help="Last-Modified header from a previous request for caching purposes."),
54
+ ] = None,
55
+ ):
56
+ """Retrieve user information from GitHub.
57
+
58
+ Args:
59
+ ctx: Typer context.
60
+ account_name: Name of the account to use for authentication.
61
+ token: Token for authentication.
62
+ base_url: Base URL of the GitHub platform.
63
+ username: Username of the user to retrieve.
64
+ account_id: Account ID of the user to retrieve.
65
+ etag: ETag from a previous request for caching purposes.
66
+ last_modified: Last-Modified header from a previous request for caching purposes.
67
+
68
+ """
69
+ import json # noqa: PLC0415
70
+ import logging # noqa: PLC0415
71
+
72
+ from ghnova.cli.utils.auth import get_auth_params # noqa: PLC0415
73
+ from ghnova.client.github import GitHub # noqa: PLC0415
74
+
75
+ logger = logging.getLogger("ghnova")
76
+
77
+ token, base_url = get_auth_params(
78
+ config_path=ctx.obj["config_path"], account_name=account_name, token=token, base_url=base_url
79
+ )
80
+
81
+ try:
82
+ with GitHub(token=token, base_url=base_url) as client:
83
+ user_client = client.user
84
+ data, status_code, etag_value, last_modified_value = user_client.get_user(
85
+ username=username, account_id=account_id, etag=etag, last_modified=last_modified
86
+ )
87
+ result = {
88
+ "data": data,
89
+ "metadata": {
90
+ "status_code": status_code,
91
+ "etag": etag_value,
92
+ "last_modified": last_modified_value,
93
+ },
94
+ }
95
+ print(json.dumps(result, indent=2, default=str))
96
+ except Exception as e:
97
+ logger.error("Error retrieving user information: %s", e)
98
+ raise typer.Exit(code=1) from e
@@ -0,0 +1,78 @@
1
+ """CLI for listing users."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from typing import Annotated
6
+
7
+ import typer
8
+
9
+
10
+ def list_command( # noqa: D103, PLR0913
11
+ ctx: typer.Context,
12
+ account_name: Annotated[
13
+ str | None,
14
+ typer.Option(
15
+ "--account-name",
16
+ help="Name of the account to use for authentication.",
17
+ ),
18
+ ] = None,
19
+ token: Annotated[
20
+ str | None,
21
+ typer.Option(
22
+ "--token",
23
+ help="Token for authentication. If not provided, the token from the specified account will be used.",
24
+ ),
25
+ ] = None,
26
+ base_url: Annotated[
27
+ str | None,
28
+ typer.Option(
29
+ "--base-url",
30
+ help="Base URL of the GitHub platform. If not provided, the base URL from the specified account will be used.",
31
+ ),
32
+ ] = None,
33
+ since: Annotated[
34
+ int | None,
35
+ typer.Option(
36
+ "--since",
37
+ help="A user ID. Only return users with an ID greater than this ID.",
38
+ ),
39
+ ] = None,
40
+ per_page: Annotated[int | None, typer.Option("--per-page", help="Number of results per page (max 100).")] = None,
41
+ etag: Annotated[
42
+ str | None, typer.Option("--etag", help="ETag from a previous request for caching purposes.")
43
+ ] = None,
44
+ last_modified: Annotated[
45
+ str | None,
46
+ typer.Option("--last-modified", help="Last-Modified header from a previous request for caching purposes."),
47
+ ] = None,
48
+ ):
49
+ import json # noqa: PLC0415
50
+ import logging # noqa: PLC0415
51
+
52
+ from ghnova.cli.utils.auth import get_auth_params # noqa: PLC0415
53
+ from ghnova.client.github import GitHub # noqa: PLC0415
54
+
55
+ logger = logging.getLogger("ghnova")
56
+
57
+ token, base_url = get_auth_params(
58
+ config_path=ctx.obj["config_path"], account_name=account_name, token=token, base_url=base_url
59
+ )
60
+
61
+ try:
62
+ with GitHub(token=token, base_url=base_url) as client:
63
+ user_client = client.user
64
+ data, status_code, etag_value, last_modified_value = user_client.list_users(
65
+ since=since, per_page=per_page, etag=etag, last_modified=last_modified
66
+ )
67
+ result = {
68
+ "data": data,
69
+ "metadata": {
70
+ "status_code": status_code,
71
+ "etag": etag_value,
72
+ "last_modified": last_modified_value,
73
+ },
74
+ }
75
+ typer.echo(json.dumps(result, indent=2, default=str))
76
+ except Exception as e:
77
+ logger.error("Error listing users: %s", e)
78
+ raise typer.Exit(code=1) from e
@@ -0,0 +1,27 @@
1
+ """User CLI commands for ghnova."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import typer
6
+
7
+ user_app = typer.Typer(
8
+ name="user",
9
+ help="Manage git users.",
10
+ rich_markup_mode="rich",
11
+ )
12
+
13
+
14
+ def register_commands() -> None:
15
+ """Register user subcommands."""
16
+ from ghnova.cli.user.ctx_info import contextual_information_command # noqa: PLC0415
17
+ from ghnova.cli.user.get import get_command # noqa: PLC0415
18
+ from ghnova.cli.user.list import list_command # noqa: PLC0415
19
+ from ghnova.cli.user.update import update_command # noqa: PLC0415
20
+
21
+ user_app.command(name="get")(get_command)
22
+ user_app.command(name="list")(list_command)
23
+ user_app.command(name="update")(update_command)
24
+ user_app.command(name="ctx-info")(contextual_information_command)
25
+
26
+
27
+ register_commands()