github-agent 1.4.1__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.
- github_agent/__init__.py +1 -1
- github_agent/__main__.py +2 -2
- github_agent/api/api_client_base.py +22 -21
- github_agent/api/api_client_branches.py +2 -14
- github_agent/api/api_client_comments.py +333 -0
- github_agent/api/api_client_commits.py +2 -4
- github_agent/api/api_client_contents.py +2 -10
- github_agent/api/api_client_dependabot.py +1 -9
- github_agent/api/api_client_issues.py +2 -8
- github_agent/api/api_client_orgs.py +2 -20
- github_agent/api/api_client_pages.py +2 -14
- github_agent/api/api_client_pulls.py +2 -14
- github_agent/api/api_client_releases.py +2 -12
- github_agent/api/api_client_repos.py +20 -24
- github_agent/api/api_client_search.py +2 -2
- github_agent/api/api_client_workflows.py +2 -18
- github_agent/api_client.py +2 -0
- github_agent/auth.py +94 -106
- github_agent/connectors/mcp_source_presets.json +1 -1
- github_agent/connectors/tool_schema_fingerprints.json +8 -0
- github_agent/github_gql.py +23 -9
- github_agent/github_http.py +120 -0
- github_agent/kg_ingest.py +240 -227
- github_agent/mcp/mcp_action.py +209 -184
- github_agent/mcp/mcp_branch.py +97 -70
- github_agent/mcp/mcp_collaborator.py +77 -59
- github_agent/mcp/mcp_commit.py +46 -33
- github_agent/mcp/mcp_content.py +120 -106
- github_agent/mcp/mcp_dependabot.py +140 -105
- github_agent/mcp/mcp_issue.py +129 -98
- github_agent/mcp/mcp_org.py +258 -199
- github_agent/mcp/mcp_pull.py +279 -204
- github_agent/mcp/mcp_release.py +123 -111
- github_agent/mcp/mcp_repo.py +383 -316
- github_agent/mcp/mcp_search.py +7 -2
- github_agent/mcp_server.py +2708 -1576
- github_agent/ontology/__init__.py +2 -2
- github_agent/ontology/certification.json +36 -0
- github_agent/ontology/fixtures/records.json +21 -0
- github_agent/ontology/github.ttl +1 -1
- github_agent/ontology/mappings/source.yaml +20 -0
- github_agent/ontology/migrations/manifest.json +6 -0
- github_agent/ontology/shapes/connector.shacl.ttl +224 -0
- github_agent/skills/github-actions-ci-review/WORKFLOW.md +82 -0
- github_agent/skills/github-agent-operations/SKILL.md +33 -0
- github_agent/skills/github-agent-operations/agents/openai.yaml +4 -0
- github_agent/skills/github-agent-operations/references/catalog.md +17 -0
- github_agent/skills/github-backlog-planner/SKILL.md +8 -6
- github_agent/skills/github-backlog-planner/WORKFLOW.md +98 -0
- github_agent/skills/github-backlog-planner/scripts/build_plan.py +92 -63
- github_agent/skills/github-ci-failure-sweep/WORKFLOW.md +96 -0
- github_agent/skills/github-ci-failure-sweep/scripts/summarize_runs.py +69 -34
- github_agent/skills/github-dependabot-review/WORKFLOW.md +91 -0
- github_agent/skills/github-issue-tracking/WORKFLOW.md +83 -0
- github_agent/skills/github-org-remediation-loop/SKILL.md +9 -19
- github_agent/skills/github-org-remediation-loop/WORKFLOW.md +267 -0
- github_agent/skills/github-org-remediation-loop/scripts/classify_fix_risk.py +43 -15
- github_agent/skills/github-pr-review-sweep/SKILL.md +10 -8
- github_agent/skills/github-pr-review-sweep/WORKFLOW.md +95 -0
- github_agent/skills/github-pr-review-sweep/scripts/summarize_prs.py +48 -30
- github_agent/skills/github-project-provisioner/SKILL.md +1 -1
- github_agent/skills/github-project-provisioner/WORKFLOW.md +51 -0
- github_agent/skills/github-project-provisioner/scripts/enable_pages.py +26 -25
- github_agent/skills/github-pull-request-create/WORKFLOW.md +70 -0
- github_agent/skills/github-pull-request-review/WORKFLOW.md +121 -0
- github_agent/skills/github-repository-management/WORKFLOW.md +90 -0
- github_agent/skills/github-tools/WORKFLOW.md +141 -0
- github_agent/skills/github-tools/scripts/fetch_comments.py +31 -27
- github_agent/skills/github-tools/scripts/inspect_pr_checks.py +127 -81
- github_agent/skills/github-triage-resolver/SKILL.md +20 -14
- github_agent/skills/github-triage-resolver/WORKFLOW.md +99 -0
- github_agent/skills/github-triage-resolver/references/resolution-rules.md +16 -10
- github_agent/skills/github-triage-resolver/scripts/classify_safe.py +146 -73
- github_agent/skills/github-triage-resolver/scripts/gh_write.py +28 -52
- {github_agent-1.4.1.dist-info → github_agent-2.3.0.dist-info}/METADATA +134 -119
- github_agent-2.3.0.dist-info/RECORD +129 -0
- {github_agent-1.4.1.dist-info → github_agent-2.3.0.dist-info}/WHEEL +1 -1
- github_agent-2.3.0.dist-info/entry_points.txt +11 -0
- {github_agent-1.4.1.dist-info → github_agent-2.3.0.dist-info}/top_level.txt +4 -0
- scripts/security_sanitizer.py +247 -89
- scripts/verify_api_integration.py +153 -112
- tests/conftest.py +89 -2
- tests/test_action_discovery.py +72 -1
- tests/test_api_client_endpoints.py +5 -3
- tests/test_api_validation.py +5 -3
- tests/test_auth_client_edge_cases.py +88 -104
- tests/test_comment_crud.py +890 -0
- tests/test_github_agent_api_brute_force_coverage.py +89 -70
- tests/test_github_agent_brute_force_coverage.py +61 -64
- tests/test_github_gql.py +24 -4
- tests/test_github_http.py +165 -0
- tests/test_hardening.py +1 -5
- tests/test_init_dynamics.py +4 -1
- tests/test_kg_ingest.py +110 -117
- tests/test_mcp_coverage.py +275 -14
- tests/test_mcp_package_dispatch.py +725 -0
- tests/test_org_crud.py +53 -14
- tests/test_pages.py +1 -11
- tests/test_repo_secrets_characterization.py +198 -0
- tests/test_skill_backlog_build_plan.py +150 -0
- tests/test_skill_ci_failure_sweep_summarize_runs.py +128 -0
- tests/test_skill_github_tools_fetch_comments.py +108 -0
- tests/test_skill_github_tools_inspect_pr_checks.py +179 -0
- tests/test_skill_org_remediation_classify_fix_risk.py +163 -0
- tests/test_skill_pr_review_sweep_summarize_prs.py +138 -0
- tests/test_skill_triage_resolver_classify_safe.py +286 -0
- tests/test_startup.py +16 -24
- github_agent/agent_server.py +0 -85
- github_agent/prompts/code_collaboration_specialist.json +0 -43
- github_agent/prompts/main_agent.json +0 -20
- github_agent-1.4.1.dist-info/RECORD +0 -99
- github_agent-1.4.1.dist-info/entry_points.txt +0 -15
- scripts/validate_a2a_agent.py +0 -139
- tests/test_concept_parity.py +0 -87
- {github_agent-1.4.1.dist-info → github_agent-2.3.0.dist-info}/licenses/LICENSE +0 -0
github_agent/__init__.py
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
__version__ = "
|
|
1
|
+
__version__ = "2.3.0"
|
github_agent/__main__.py
CHANGED
|
@@ -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
|
|
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:
|
|
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
|
-
|
|
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.
|
|
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(
|
|
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(
|
|
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(
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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())
|