github-agent 1.4.2__py3-none-any.whl → 2.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 (115) hide show
  1. github_agent/__init__.py +1 -1
  2. github_agent/__main__.py +2 -2
  3. github_agent/api/api_client_base.py +22 -21
  4. github_agent/api/api_client_branches.py +2 -14
  5. github_agent/api/api_client_comments.py +333 -0
  6. github_agent/api/api_client_commits.py +2 -4
  7. github_agent/api/api_client_contents.py +2 -10
  8. github_agent/api/api_client_dependabot.py +1 -9
  9. github_agent/api/api_client_issues.py +2 -8
  10. github_agent/api/api_client_orgs.py +2 -20
  11. github_agent/api/api_client_pages.py +2 -14
  12. github_agent/api/api_client_pulls.py +2 -14
  13. github_agent/api/api_client_releases.py +2 -12
  14. github_agent/api/api_client_repos.py +2 -22
  15. github_agent/api/api_client_search.py +2 -2
  16. github_agent/api/api_client_workflows.py +2 -18
  17. github_agent/api_client.py +2 -0
  18. github_agent/auth.py +94 -106
  19. github_agent/connectors/mcp_source_presets.json +1 -1
  20. github_agent/connectors/tool_schema_fingerprints.json +8 -0
  21. github_agent/github_gql.py +23 -9
  22. github_agent/github_http.py +120 -0
  23. github_agent/kg_ingest.py +240 -227
  24. github_agent/mcp/mcp_action.py +209 -184
  25. github_agent/mcp/mcp_branch.py +97 -70
  26. github_agent/mcp/mcp_collaborator.py +77 -59
  27. github_agent/mcp/mcp_commit.py +46 -33
  28. github_agent/mcp/mcp_content.py +120 -106
  29. github_agent/mcp/mcp_dependabot.py +140 -105
  30. github_agent/mcp/mcp_issue.py +129 -98
  31. github_agent/mcp/mcp_org.py +258 -199
  32. github_agent/mcp/mcp_pull.py +279 -204
  33. github_agent/mcp/mcp_release.py +123 -111
  34. github_agent/mcp/mcp_repo.py +379 -315
  35. github_agent/mcp/mcp_search.py +7 -2
  36. github_agent/mcp_server.py +2704 -1575
  37. github_agent/ontology/__init__.py +2 -2
  38. github_agent/ontology/certification.json +36 -0
  39. github_agent/ontology/fixtures/records.json +21 -0
  40. github_agent/ontology/github.ttl +1 -1
  41. github_agent/ontology/mappings/source.yaml +20 -0
  42. github_agent/ontology/migrations/manifest.json +6 -0
  43. github_agent/ontology/shapes/connector.shacl.ttl +224 -0
  44. github_agent/skills/github-actions-ci-review/WORKFLOW.md +82 -0
  45. github_agent/skills/github-agent-operations/SKILL.md +33 -0
  46. github_agent/skills/github-agent-operations/agents/openai.yaml +4 -0
  47. github_agent/skills/github-agent-operations/references/catalog.md +17 -0
  48. github_agent/skills/github-backlog-planner/SKILL.md +8 -6
  49. github_agent/skills/github-backlog-planner/WORKFLOW.md +98 -0
  50. github_agent/skills/github-backlog-planner/scripts/build_plan.py +92 -63
  51. github_agent/skills/github-ci-failure-sweep/WORKFLOW.md +96 -0
  52. github_agent/skills/github-ci-failure-sweep/scripts/summarize_runs.py +69 -34
  53. github_agent/skills/github-dependabot-review/WORKFLOW.md +91 -0
  54. github_agent/skills/github-issue-tracking/WORKFLOW.md +83 -0
  55. github_agent/skills/github-org-remediation-loop/SKILL.md +9 -19
  56. github_agent/skills/github-org-remediation-loop/WORKFLOW.md +267 -0
  57. github_agent/skills/github-org-remediation-loop/scripts/classify_fix_risk.py +43 -15
  58. github_agent/skills/github-pr-review-sweep/SKILL.md +10 -8
  59. github_agent/skills/github-pr-review-sweep/WORKFLOW.md +95 -0
  60. github_agent/skills/github-pr-review-sweep/scripts/summarize_prs.py +48 -30
  61. github_agent/skills/github-project-provisioner/SKILL.md +1 -1
  62. github_agent/skills/github-project-provisioner/WORKFLOW.md +51 -0
  63. github_agent/skills/github-project-provisioner/scripts/enable_pages.py +26 -25
  64. github_agent/skills/github-pull-request-create/WORKFLOW.md +70 -0
  65. github_agent/skills/github-pull-request-review/WORKFLOW.md +121 -0
  66. github_agent/skills/github-repository-management/WORKFLOW.md +90 -0
  67. github_agent/skills/github-tools/WORKFLOW.md +141 -0
  68. github_agent/skills/github-tools/scripts/fetch_comments.py +31 -27
  69. github_agent/skills/github-tools/scripts/inspect_pr_checks.py +124 -78
  70. github_agent/skills/github-triage-resolver/SKILL.md +20 -14
  71. github_agent/skills/github-triage-resolver/WORKFLOW.md +99 -0
  72. github_agent/skills/github-triage-resolver/references/resolution-rules.md +16 -10
  73. github_agent/skills/github-triage-resolver/scripts/classify_safe.py +146 -73
  74. github_agent/skills/github-triage-resolver/scripts/gh_write.py +28 -52
  75. {github_agent-1.4.2.dist-info → github_agent-2.3.0.dist-info}/METADATA +133 -118
  76. github_agent-2.3.0.dist-info/RECORD +129 -0
  77. {github_agent-1.4.2.dist-info → github_agent-2.3.0.dist-info}/WHEEL +1 -1
  78. github_agent-2.3.0.dist-info/entry_points.txt +11 -0
  79. {github_agent-1.4.2.dist-info → github_agent-2.3.0.dist-info}/top_level.txt +4 -0
  80. scripts/security_sanitizer.py +247 -89
  81. scripts/verify_api_integration.py +153 -112
  82. tests/conftest.py +89 -2
  83. tests/test_action_discovery.py +72 -1
  84. tests/test_api_client_endpoints.py +5 -3
  85. tests/test_api_validation.py +5 -3
  86. tests/test_auth_client_edge_cases.py +88 -104
  87. tests/test_comment_crud.py +890 -0
  88. tests/test_github_agent_api_brute_force_coverage.py +89 -70
  89. tests/test_github_agent_brute_force_coverage.py +61 -64
  90. tests/test_github_gql.py +24 -4
  91. tests/test_github_http.py +165 -0
  92. tests/test_hardening.py +1 -5
  93. tests/test_init_dynamics.py +4 -1
  94. tests/test_kg_ingest.py +110 -117
  95. tests/test_mcp_coverage.py +275 -14
  96. tests/test_mcp_package_dispatch.py +725 -0
  97. tests/test_org_crud.py +5 -14
  98. tests/test_pages.py +1 -11
  99. tests/test_repo_secrets_characterization.py +198 -0
  100. tests/test_skill_backlog_build_plan.py +150 -0
  101. tests/test_skill_ci_failure_sweep_summarize_runs.py +128 -0
  102. tests/test_skill_github_tools_fetch_comments.py +108 -0
  103. tests/test_skill_github_tools_inspect_pr_checks.py +179 -0
  104. tests/test_skill_org_remediation_classify_fix_risk.py +163 -0
  105. tests/test_skill_pr_review_sweep_summarize_prs.py +138 -0
  106. tests/test_skill_triage_resolver_classify_safe.py +286 -0
  107. tests/test_startup.py +16 -24
  108. github_agent/agent_server.py +0 -85
  109. github_agent/prompts/code_collaboration_specialist.json +0 -43
  110. github_agent/prompts/main_agent.json +0 -20
  111. github_agent-1.4.2.dist-info/RECORD +0 -99
  112. github_agent-1.4.2.dist-info/entry_points.txt +0 -15
  113. scripts/validate_a2a_agent.py +0 -139
  114. tests/test_concept_parity.py +0 -87
  115. {github_agent-1.4.2.dist-info → github_agent-2.3.0.dist-info}/licenses/LICENSE +0 -0
github_agent/__init__.py CHANGED
@@ -1 +1 @@
1
- __version__ = "0.1.0"
1
+ __version__ = "2.3.0"
github_agent/__main__.py CHANGED
@@ -1,4 +1,4 @@
1
- from github_agent.agent_server import agent_server
1
+ from github_agent.mcp_server import mcp_server
2
2
 
3
3
  if __name__ == "__main__":
4
- agent_server()
4
+ mcp_server()
@@ -7,17 +7,20 @@ from concurrent.futures import ThreadPoolExecutor, as_completed
7
7
  from typing import TypeVar
8
8
 
9
9
  import requests
10
- import urllib3
11
- from agent_utilities.base_utilities import get_logger
12
- from agent_utilities.exceptions import (
10
+ from agent_connector_sdk.exceptions import (
13
11
  AuthError,
14
12
  MissingParameterError,
15
13
  UnauthorizedError,
16
14
  )
15
+ from agent_connector_sdk.tls.profile import ResolvedTLSProfile
16
+ from agent_connector_sdk.tls.resolve import resolve_tls_profile
17
+ from agent_connector_sdk.utilities import get_logger
17
18
  from pydantic import BaseModel
18
19
 
19
20
  logger = get_logger(__name__)
20
21
  T = TypeVar("T", bound=BaseModel)
22
+ _VerifyValue = bool | str
23
+ _MANDATORY_VERIFY: _VerifyValue = True
21
24
 
22
25
 
23
26
  def _default_timeout() -> tuple[float, float]:
@@ -50,10 +53,12 @@ class _TimeoutAdapter(requests.adapters.HTTPAdapter):
50
53
  request: requests.PreparedRequest,
51
54
  stream: bool = False,
52
55
  timeout: None | float | tuple[float, float] | tuple[float, None] = None,
53
- verify: bool | str = True,
56
+ verify: _VerifyValue = _MANDATORY_VERIFY,
54
57
  cert: None | bytes | str | tuple[bytes | str, bytes | str] = None,
55
58
  proxies: Mapping[str, str] | None = None,
56
59
  ) -> requests.Response:
60
+ if verify is False:
61
+ raise ValueError("transport certificate verification cannot be disabled")
57
62
  if timeout is None:
58
63
  timeout = self._timeout
59
64
  return super().send(
@@ -71,8 +76,7 @@ class BaseApiClient:
71
76
  self,
72
77
  url: str | None = "https://api.github.com",
73
78
  token: str | None = None,
74
- proxies: dict | None = None,
75
- verify: bool = True,
79
+ tls_profile: ResolvedTLSProfile | None = None,
76
80
  debug: bool = False,
77
81
  ):
78
82
  if debug:
@@ -83,7 +87,8 @@ class BaseApiClient:
83
87
  if url is None:
84
88
  raise MissingParameterError
85
89
 
86
- self._session = requests.Session()
90
+ self.tls_profile = tls_profile or resolve_tls_profile("github")
91
+ self._session = self.tls_profile.configure_requests_session(requests.Session())
87
92
  _adapter = _TimeoutAdapter(_default_timeout())
88
93
  self._session.mount("https://", _adapter)
89
94
  self._session.mount("http://", _adapter)
@@ -92,13 +97,8 @@ class BaseApiClient:
92
97
  "Accept": "application/vnd.github+json",
93
98
  "X-GitHub-Api-Version": "2022-11-28",
94
99
  }
95
- self.verify = verify
96
- self.proxies = proxies
97
100
  self.debug = debug
98
101
 
99
- if self.verify is False:
100
- urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
101
-
102
102
  if token:
103
103
  self.headers["Authorization"] = f"Bearer {token}"
104
104
  else:
@@ -108,14 +108,17 @@ class BaseApiClient:
108
108
  response = self._session.get(
109
109
  url=f"{self.url}/user",
110
110
  headers=self.headers,
111
- verify=self.verify,
112
- proxies=self.proxies,
113
111
  )
114
112
  if response.status_code in (401, 403):
115
- logger.error(f"Authentication Error: {response.text}")
113
+ logger.error("GitHub authentication was rejected")
116
114
  raise AuthError if response.status_code == 401 else UnauthorizedError
117
115
  except requests.exceptions.RequestException as e:
118
- logger.error(f"Connection Error: {str(e)}")
116
+ logger.error("Operation failed: error_type=%s", type(e).__name__)
117
+
118
+ def close(self) -> None:
119
+ """Release transport resources and runtime-only TLS material."""
120
+ self._session.close()
121
+ self.tls_profile.cleanup()
119
122
 
120
123
  def _fetch_next_page(
121
124
  self, endpoint: str, model: T, header: dict, page: int
@@ -127,8 +130,6 @@ class BaseApiClient:
127
130
  url=f"{self.url}{endpoint}" if endpoint.startswith("/") else endpoint,
128
131
  params=model.api_parameters, # type: ignore[attr-defined]
129
132
  headers=header,
130
- verify=self.verify,
131
- proxies=self.proxies,
132
133
  )
133
134
  response.raise_for_status()
134
135
  page_data = response.json()
@@ -157,8 +158,6 @@ class BaseApiClient:
157
158
  url=initial_url,
158
159
  params=model.api_parameters, # type: ignore[attr-defined]
159
160
  headers=self.headers,
160
- verify=self.verify,
161
- proxies=self.proxies,
162
161
  )
163
162
  response.raise_for_status()
164
163
  initial_data = response.json()
@@ -199,6 +198,8 @@ class BaseApiClient:
199
198
  try:
200
199
  all_data.extend(future.result())
201
200
  except Exception as e:
202
- logger.error(f"Error fetching page: {str(e)}")
201
+ logger.error(
202
+ "Operation failed: error_type=%s", type(e).__name__
203
+ )
203
204
 
204
205
  return response, all_data
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env python
2
- from agent_utilities.decorators import require_auth
3
- from agent_utilities.exceptions import (
2
+ from agent_connector_sdk.exceptions import (
4
3
  ParameterError,
4
+ require_auth,
5
5
  )
6
6
  from pydantic import ValidationError
7
7
 
@@ -38,8 +38,6 @@ class Api(BaseApiClient):
38
38
  url=f"{self.url}/repos/{owner}/{repo}/git/refs",
39
39
  json=payload,
40
40
  headers=self.headers,
41
- verify=self.verify,
42
- proxies=self.proxies,
43
41
  )
44
42
  response.raise_for_status()
45
43
  return Response(response=response, data=response.json())
@@ -52,8 +50,6 @@ class Api(BaseApiClient):
52
50
  response = self._session.delete(
53
51
  url=f"{self.url}/repos/{owner}/{repo}/git/refs/heads/{branch}",
54
52
  headers=self.headers,
55
- verify=self.verify,
56
- proxies=self.proxies,
57
53
  )
58
54
  response.raise_for_status()
59
55
  return Response(response=response, data={"status": "deleted"})
@@ -64,8 +60,6 @@ class Api(BaseApiClient):
64
60
  response = self._session.get(
65
61
  url=f"{self.url}/repos/{owner}/{repo}/branches/{branch}",
66
62
  headers=self.headers,
67
- verify=self.verify,
68
- proxies=self.proxies,
69
63
  )
70
64
  response.raise_for_status()
71
65
  try:
@@ -80,8 +74,6 @@ class Api(BaseApiClient):
80
74
  response = self._session.get(
81
75
  url=f"{self.url}/repos/{owner}/{repo}/branches/{branch}/protection",
82
76
  headers=self.headers,
83
- verify=self.verify,
84
- proxies=self.proxies,
85
77
  )
86
78
  response.raise_for_status()
87
79
  return Response(response=response, data=response.json())
@@ -95,8 +87,6 @@ class Api(BaseApiClient):
95
87
  url=f"{self.url}/repos/{owner}/{repo}/branches/{branch}/protection",
96
88
  json=protection_config,
97
89
  headers=self.headers,
98
- verify=self.verify,
99
- proxies=self.proxies,
100
90
  )
101
91
  response.raise_for_status()
102
92
  return Response(response=response, data=response.json())
@@ -107,8 +97,6 @@ class Api(BaseApiClient):
107
97
  response = self._session.delete(
108
98
  url=f"{self.url}/repos/{owner}/{repo}/branches/{branch}/protection",
109
99
  headers=self.headers,
110
- verify=self.verify,
111
- proxies=self.proxies,
112
100
  )
113
101
  response.raise_for_status()
114
102
  return Response(response=response, data={"status": "protection_deleted"})
@@ -0,0 +1,333 @@
1
+ #!/usr/bin/env python
2
+ from agent_connector_sdk.exceptions import require_auth
3
+
4
+ from github_agent.api.api_client_base import BaseApiClient
5
+ from github_agent.github_response_models import Response
6
+
7
+
8
+ class Api(BaseApiClient):
9
+ # --- Issue / pull-request comments -----------------------------------
10
+ # A pull request IS an issue on GitHub, so these endpoints work for both.
11
+
12
+ @require_auth
13
+ def list_issue_comments(
14
+ self, owner: str, repo: str, issue_number: int, **filters
15
+ ) -> Response:
16
+ """List comments on an issue or pull request.
17
+
18
+ GET /repos/{owner}/{repo}/issues/{issue_number}/comments. Optional
19
+ filters passed straight through as query parameters (since, per_page,
20
+ page). A pull request IS an issue on GitHub, so this also lists a
21
+ PR's top-level (non-review) comments. Returns the raw JSON list of
22
+ comments in ``Response.data``.
23
+ """
24
+ params = {k: v for k, v in filters.items() if v is not None}
25
+ response = self._session.get(
26
+ url=f"{self.url}/repos/{owner}/{repo}/issues/{issue_number}/comments",
27
+ params=params or None,
28
+ headers=self.headers,
29
+ )
30
+ response.raise_for_status()
31
+ return Response(response=response, data=response.json())
32
+
33
+ @require_auth
34
+ def list_repo_issue_comments(self, owner: str, repo: str, **filters) -> Response:
35
+ """List every issue/PR comment in a repository.
36
+
37
+ GET /repos/{owner}/{repo}/issues/comments. Optional filters passed
38
+ straight through as query parameters (sort, direction, since,
39
+ per_page, page). Returns the raw JSON list of comments in
40
+ ``Response.data``.
41
+ """
42
+ params = {k: v for k, v in filters.items() if v is not None}
43
+ response = self._session.get(
44
+ url=f"{self.url}/repos/{owner}/{repo}/issues/comments",
45
+ params=params or None,
46
+ headers=self.headers,
47
+ )
48
+ response.raise_for_status()
49
+ return Response(response=response, data=response.json())
50
+
51
+ @require_auth
52
+ def get_issue_comment(self, owner: str, repo: str, comment_id: int) -> Response:
53
+ """Get a single issue/PR comment.
54
+
55
+ GET /repos/{owner}/{repo}/issues/comments/{comment_id}.
56
+ """
57
+ response = self._session.get(
58
+ url=f"{self.url}/repos/{owner}/{repo}/issues/comments/{comment_id}",
59
+ headers=self.headers,
60
+ )
61
+ response.raise_for_status()
62
+ return Response(response=response, data=response.json())
63
+
64
+ @require_auth
65
+ def create_issue_comment(
66
+ self, owner: str, repo: str, issue_number: int, body: str
67
+ ) -> Response:
68
+ """Create a comment on an issue or pull request.
69
+
70
+ POST /repos/{owner}/{repo}/issues/{issue_number}/comments. A pull
71
+ request IS an issue on GitHub, so this is also how you reply to a PR
72
+ (a top-level comment — for an inline code review comment see
73
+ create_review_comment).
74
+ """
75
+ response = self._session.post(
76
+ url=f"{self.url}/repos/{owner}/{repo}/issues/{issue_number}/comments",
77
+ json={"body": body},
78
+ headers=self.headers,
79
+ )
80
+ response.raise_for_status()
81
+ return Response(response=response, data=response.json())
82
+
83
+ @require_auth
84
+ def update_issue_comment(
85
+ self, owner: str, repo: str, comment_id: int, body: str
86
+ ) -> Response:
87
+ """Edit an issue/PR comment.
88
+
89
+ PATCH /repos/{owner}/{repo}/issues/comments/{comment_id}.
90
+ """
91
+ response = self._session.patch(
92
+ url=f"{self.url}/repos/{owner}/{repo}/issues/comments/{comment_id}",
93
+ json={"body": body},
94
+ headers=self.headers,
95
+ )
96
+ response.raise_for_status()
97
+ return Response(response=response, data=response.json())
98
+
99
+ @require_auth
100
+ def delete_issue_comment(self, owner: str, repo: str, comment_id: int) -> Response:
101
+ """Permanently delete an issue/PR comment.
102
+
103
+ DELETE /repos/{owner}/{repo}/issues/comments/{comment_id} (HTTP 204).
104
+ """
105
+ response = self._session.delete(
106
+ url=f"{self.url}/repos/{owner}/{repo}/issues/comments/{comment_id}",
107
+ headers=self.headers,
108
+ )
109
+ response.raise_for_status()
110
+ return Response(response=response, data={"status": "deleted"})
111
+
112
+ # --- Pull-request review comments (inline code comments) -------------
113
+
114
+ @require_auth
115
+ def list_review_comments(
116
+ self, owner: str, repo: str, pull_number: int, **filters
117
+ ) -> Response:
118
+ """List review (inline code) comments on a pull request.
119
+
120
+ GET /repos/{owner}/{repo}/pulls/{pull_number}/comments. Optional
121
+ filters passed straight through as query parameters (since, per_page,
122
+ page). Returns the raw JSON list of comments in ``Response.data``.
123
+ """
124
+ params = {k: v for k, v in filters.items() if v is not None}
125
+ response = self._session.get(
126
+ url=f"{self.url}/repos/{owner}/{repo}/pulls/{pull_number}/comments",
127
+ params=params or None,
128
+ headers=self.headers,
129
+ )
130
+ response.raise_for_status()
131
+ return Response(response=response, data=response.json())
132
+
133
+ @require_auth
134
+ def list_repo_review_comments(self, owner: str, repo: str, **filters) -> Response:
135
+ """List every pull-request review comment in a repository.
136
+
137
+ GET /repos/{owner}/{repo}/pulls/comments. Optional filters passed
138
+ straight through as query parameters (sort, direction, since,
139
+ per_page, page). Returns the raw JSON list of comments in
140
+ ``Response.data``.
141
+ """
142
+ params = {k: v for k, v in filters.items() if v is not None}
143
+ response = self._session.get(
144
+ url=f"{self.url}/repos/{owner}/{repo}/pulls/comments",
145
+ params=params or None,
146
+ headers=self.headers,
147
+ )
148
+ response.raise_for_status()
149
+ return Response(response=response, data=response.json())
150
+
151
+ @require_auth
152
+ def get_review_comment(self, owner: str, repo: str, comment_id: int) -> Response:
153
+ """Get a single pull-request review comment.
154
+
155
+ GET /repos/{owner}/{repo}/pulls/comments/{comment_id}.
156
+ """
157
+ response = self._session.get(
158
+ url=f"{self.url}/repos/{owner}/{repo}/pulls/comments/{comment_id}",
159
+ headers=self.headers,
160
+ )
161
+ response.raise_for_status()
162
+ return Response(response=response, data=response.json())
163
+
164
+ @require_auth
165
+ def create_review_comment(
166
+ self, owner: str, repo: str, pull_number: int, body: str, **kwargs
167
+ ) -> Response:
168
+ """Create an inline review comment on a pull request's diff.
169
+
170
+ POST /repos/{owner}/{repo}/pulls/{pull_number}/comments. Requires
171
+ either a new-thread payload (``commit_id``, ``path``, plus ``line`` +
172
+ ``side``, or ``start_line`` + ``start_side`` for a multi-line
173
+ comment, or the legacy ``position``) or ``in_reply_to`` (an existing
174
+ review comment id) to reply within its thread — the simpler
175
+ create_review_comment_reply wraps that second case.
176
+ """
177
+ payload = {"body": body, **kwargs}
178
+ response = self._session.post(
179
+ url=f"{self.url}/repos/{owner}/{repo}/pulls/{pull_number}/comments",
180
+ json=payload,
181
+ headers=self.headers,
182
+ )
183
+ response.raise_for_status()
184
+ return Response(response=response, data=response.json())
185
+
186
+ @require_auth
187
+ def create_review_comment_reply(
188
+ self, owner: str, repo: str, pull_number: int, comment_id: int, body: str
189
+ ) -> Response:
190
+ """Reply to an existing pull-request review comment thread.
191
+
192
+ POST /repos/{owner}/{repo}/pulls/{pull_number}/comments/{comment_id}/replies.
193
+ Simpler than create_review_comment(in_reply_to=...) — no commit_id or
194
+ path required.
195
+ """
196
+ response = self._session.post(
197
+ url=(
198
+ f"{self.url}/repos/{owner}/{repo}/pulls/{pull_number}/comments/"
199
+ f"{comment_id}/replies"
200
+ ),
201
+ json={"body": body},
202
+ headers=self.headers,
203
+ )
204
+ response.raise_for_status()
205
+ return Response(response=response, data=response.json())
206
+
207
+ @require_auth
208
+ def update_review_comment(
209
+ self, owner: str, repo: str, comment_id: int, body: str
210
+ ) -> Response:
211
+ """Edit a pull-request review comment.
212
+
213
+ PATCH /repos/{owner}/{repo}/pulls/comments/{comment_id}.
214
+ """
215
+ response = self._session.patch(
216
+ url=f"{self.url}/repos/{owner}/{repo}/pulls/comments/{comment_id}",
217
+ json={"body": body},
218
+ headers=self.headers,
219
+ )
220
+ response.raise_for_status()
221
+ return Response(response=response, data=response.json())
222
+
223
+ @require_auth
224
+ def delete_review_comment(self, owner: str, repo: str, comment_id: int) -> Response:
225
+ """Permanently delete a pull-request review comment.
226
+
227
+ DELETE /repos/{owner}/{repo}/pulls/comments/{comment_id} (HTTP 204).
228
+ """
229
+ response = self._session.delete(
230
+ url=f"{self.url}/repos/{owner}/{repo}/pulls/comments/{comment_id}",
231
+ headers=self.headers,
232
+ )
233
+ response.raise_for_status()
234
+ return Response(response=response, data={"status": "deleted"})
235
+
236
+ # --- Commit comments ---------------------------------------------------
237
+
238
+ @require_auth
239
+ def list_commit_comments(
240
+ self, owner: str, repo: str, sha: str, **filters
241
+ ) -> Response:
242
+ """List comments on a single commit.
243
+
244
+ GET /repos/{owner}/{repo}/commits/{sha}/comments. Optional filters
245
+ passed straight through as query parameters (per_page, page).
246
+ Returns the raw JSON list of comments in ``Response.data``.
247
+ """
248
+ params = {k: v for k, v in filters.items() if v is not None}
249
+ response = self._session.get(
250
+ url=f"{self.url}/repos/{owner}/{repo}/commits/{sha}/comments",
251
+ params=params or None,
252
+ headers=self.headers,
253
+ )
254
+ response.raise_for_status()
255
+ return Response(response=response, data=response.json())
256
+
257
+ @require_auth
258
+ def list_repo_commit_comments(self, owner: str, repo: str, **filters) -> Response:
259
+ """List every commit comment in a repository.
260
+
261
+ GET /repos/{owner}/{repo}/comments. Optional filters passed straight
262
+ through as query parameters (per_page, page). Returns the raw JSON
263
+ list of comments in ``Response.data``.
264
+ """
265
+ params = {k: v for k, v in filters.items() if v is not None}
266
+ response = self._session.get(
267
+ url=f"{self.url}/repos/{owner}/{repo}/comments",
268
+ params=params or None,
269
+ headers=self.headers,
270
+ )
271
+ response.raise_for_status()
272
+ return Response(response=response, data=response.json())
273
+
274
+ @require_auth
275
+ def get_commit_comment(self, owner: str, repo: str, comment_id: int) -> Response:
276
+ """Get a single commit comment.
277
+
278
+ GET /repos/{owner}/{repo}/comments/{comment_id}.
279
+ """
280
+ response = self._session.get(
281
+ url=f"{self.url}/repos/{owner}/{repo}/comments/{comment_id}",
282
+ headers=self.headers,
283
+ )
284
+ response.raise_for_status()
285
+ return Response(response=response, data=response.json())
286
+
287
+ @require_auth
288
+ def create_commit_comment(
289
+ self, owner: str, repo: str, sha: str, body: str, **kwargs
290
+ ) -> Response:
291
+ """Create a comment on a commit.
292
+
293
+ POST /repos/{owner}/{repo}/commits/{sha}/comments. Optionally anchor
294
+ the comment to a line of a file in the commit's diff via ``path``
295
+ (the file path) and ``line`` (the line number in the file).
296
+ """
297
+ payload = {"body": body, **kwargs}
298
+ response = self._session.post(
299
+ url=f"{self.url}/repos/{owner}/{repo}/commits/{sha}/comments",
300
+ json=payload,
301
+ headers=self.headers,
302
+ )
303
+ response.raise_for_status()
304
+ return Response(response=response, data=response.json())
305
+
306
+ @require_auth
307
+ def update_commit_comment(
308
+ self, owner: str, repo: str, comment_id: int, body: str
309
+ ) -> Response:
310
+ """Edit a commit comment.
311
+
312
+ PATCH /repos/{owner}/{repo}/comments/{comment_id}.
313
+ """
314
+ response = self._session.patch(
315
+ url=f"{self.url}/repos/{owner}/{repo}/comments/{comment_id}",
316
+ json={"body": body},
317
+ headers=self.headers,
318
+ )
319
+ response.raise_for_status()
320
+ return Response(response=response, data=response.json())
321
+
322
+ @require_auth
323
+ def delete_commit_comment(self, owner: str, repo: str, comment_id: int) -> Response:
324
+ """Permanently delete a commit comment.
325
+
326
+ DELETE /repos/{owner}/{repo}/comments/{comment_id} (HTTP 204).
327
+ """
328
+ response = self._session.delete(
329
+ url=f"{self.url}/repos/{owner}/{repo}/comments/{comment_id}",
330
+ headers=self.headers,
331
+ )
332
+ response.raise_for_status()
333
+ return Response(response=response, data={"status": "deleted"})
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env python
2
- from agent_utilities.decorators import require_auth
3
- from agent_utilities.exceptions import (
2
+ from agent_connector_sdk.exceptions import (
4
3
  ParameterError,
4
+ require_auth,
5
5
  )
6
6
  from pydantic import ValidationError
7
7
 
@@ -35,8 +35,6 @@ class Api(BaseApiClient):
35
35
  response = self._session.get(
36
36
  url=f"{self.url}/repos/{owner}/{repo}/commits/{sha}",
37
37
  headers=self.headers,
38
- verify=self.verify,
39
- proxies=self.proxies,
40
38
  )
41
39
  response.raise_for_status()
42
40
  try:
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env python
2
- from agent_utilities.decorators import require_auth
3
- from agent_utilities.exceptions import (
2
+ from agent_connector_sdk.exceptions import (
4
3
  ParameterError,
4
+ require_auth,
5
5
  )
6
6
  from pydantic import ValidationError
7
7
 
@@ -25,8 +25,6 @@ class Api(BaseApiClient):
25
25
  url=f"{self.url}/repos/{model.owner}/{model.repo}/contents/{model.path}",
26
26
  params=model.api_parameters,
27
27
  headers=self.headers,
28
- verify=self.verify,
29
- proxies=self.proxies,
30
28
  )
31
29
  response.raise_for_status()
32
30
  data = response.json()
@@ -50,8 +48,6 @@ class Api(BaseApiClient):
50
48
  url=f"{self.url}/repos/{owner}/{repo}/contents/{path}",
51
49
  json=payload,
52
50
  headers=self.headers,
53
- verify=self.verify,
54
- proxies=self.proxies,
55
51
  )
56
52
  response.raise_for_status()
57
53
  res_json = response.json()
@@ -80,8 +76,6 @@ class Api(BaseApiClient):
80
76
  url=f"{self.url}/repos/{owner}/{repo}/contents/{path}",
81
77
  json=payload,
82
78
  headers=self.headers,
83
- verify=self.verify,
84
- proxies=self.proxies,
85
79
  )
86
80
  response.raise_for_status()
87
81
  res_json = response.json()
@@ -103,8 +97,6 @@ class Api(BaseApiClient):
103
97
  url=f"{self.url}/repos/{owner}/{repo}/contents/{path}",
104
98
  json=payload,
105
99
  headers=self.headers,
106
- verify=self.verify,
107
- proxies=self.proxies,
108
100
  )
109
101
  response.raise_for_status()
110
102
  return Response(response=response, data=response.json())
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env python
2
2
  from typing import Any
3
3
 
4
- from agent_utilities.decorators import require_auth
4
+ from agent_connector_sdk.exceptions import require_auth
5
5
 
6
6
  from github_agent.api.api_client_base import BaseApiClient
7
7
  from github_agent.github_response_models import Response
@@ -22,8 +22,6 @@ class Api(BaseApiClient):
22
22
  url=f"{self.url}/repos/{owner}/{repo}/dependabot/alerts",
23
23
  params=params or None,
24
24
  headers=self.headers,
25
- verify=self.verify,
26
- proxies=self.proxies,
27
25
  )
28
26
  response.raise_for_status()
29
27
  return Response(response=response, data=response.json())
@@ -39,8 +37,6 @@ class Api(BaseApiClient):
39
37
  response = self._session.get(
40
38
  url=(f"{self.url}/repos/{owner}/{repo}/dependabot/alerts/{alert_number}"),
41
39
  headers=self.headers,
42
- verify=self.verify,
43
- proxies=self.proxies,
44
40
  )
45
41
  response.raise_for_status()
46
42
  return Response(response=response, data=response.json())
@@ -59,8 +55,6 @@ class Api(BaseApiClient):
59
55
  url=f"{self.url}/orgs/{org}/dependabot/alerts",
60
56
  params=params or None,
61
57
  headers=self.headers,
62
- verify=self.verify,
63
- proxies=self.proxies,
64
58
  )
65
59
  response.raise_for_status()
66
60
  return Response(response=response, data=response.json())
@@ -91,8 +85,6 @@ class Api(BaseApiClient):
91
85
  url=(f"{self.url}/repos/{owner}/{repo}/dependabot/alerts/{alert_number}"),
92
86
  json=payload,
93
87
  headers=self.headers,
94
- verify=self.verify,
95
- proxies=self.proxies,
96
88
  )
97
89
  response.raise_for_status()
98
90
  return Response(response=response, data=response.json())
@@ -1,8 +1,8 @@
1
1
  #!/usr/bin/env python
2
- from agent_utilities.decorators import require_auth
3
- from agent_utilities.exceptions import (
2
+ from agent_connector_sdk.exceptions import (
4
3
  MissingParameterError,
5
4
  ParameterError,
5
+ require_auth,
6
6
  )
7
7
  from pydantic import ValidationError
8
8
 
@@ -41,8 +41,6 @@ class Api(BaseApiClient):
41
41
  url=f"{self.url}/repos/{owner}/{repo}/issues",
42
42
  json=payload,
43
43
  headers=self.headers,
44
- verify=self.verify,
45
- proxies=self.proxies,
46
44
  )
47
45
  response.raise_for_status()
48
46
  parsed_data = Issue(**response.json())
@@ -58,8 +56,6 @@ class Api(BaseApiClient):
58
56
  url=f"{self.url}/repos/{owner}/{repo}/issues/{number}",
59
57
  json=kwargs,
60
58
  headers=self.headers,
61
- verify=self.verify,
62
- proxies=self.proxies,
63
59
  )
64
60
  response.raise_for_status()
65
61
  parsed_data = Issue(**response.json())
@@ -74,8 +70,6 @@ class Api(BaseApiClient):
74
70
  response = self._session.get(
75
71
  url=f"{self.url}/repos/{owner}/{repo}/issues/{number}",
76
72
  headers=self.headers,
77
- verify=self.verify,
78
- proxies=self.proxies,
79
73
  )
80
74
  response.raise_for_status()
81
75
  parsed_data = Issue(**response.json())