aurelian 0.3.2__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 (254) hide show
  1. aurelian/__init__.py +9 -0
  2. aurelian/agents/__init__.py +0 -0
  3. aurelian/agents/amigo/__init__.py +3 -0
  4. aurelian/agents/amigo/amigo_agent.py +77 -0
  5. aurelian/agents/amigo/amigo_config.py +85 -0
  6. aurelian/agents/amigo/amigo_evals.py +73 -0
  7. aurelian/agents/amigo/amigo_gradio.py +52 -0
  8. aurelian/agents/amigo/amigo_mcp.py +152 -0
  9. aurelian/agents/amigo/amigo_tools.py +152 -0
  10. aurelian/agents/biblio/__init__.py +42 -0
  11. aurelian/agents/biblio/biblio_agent.py +94 -0
  12. aurelian/agents/biblio/biblio_config.py +40 -0
  13. aurelian/agents/biblio/biblio_gradio.py +67 -0
  14. aurelian/agents/biblio/biblio_mcp.py +115 -0
  15. aurelian/agents/biblio/biblio_tools.py +164 -0
  16. aurelian/agents/biblio_agent.py +46 -0
  17. aurelian/agents/checklist/__init__.py +44 -0
  18. aurelian/agents/checklist/checklist_agent.py +85 -0
  19. aurelian/agents/checklist/checklist_config.py +28 -0
  20. aurelian/agents/checklist/checklist_gradio.py +70 -0
  21. aurelian/agents/checklist/checklist_mcp.py +86 -0
  22. aurelian/agents/checklist/checklist_tools.py +141 -0
  23. aurelian/agents/checklist/content/checklists.yaml +7 -0
  24. aurelian/agents/checklist/content/streams.csv +136 -0
  25. aurelian/agents/checklist_agent.py +40 -0
  26. aurelian/agents/chemistry/__init__.py +3 -0
  27. aurelian/agents/chemistry/chemistry_agent.py +46 -0
  28. aurelian/agents/chemistry/chemistry_config.py +71 -0
  29. aurelian/agents/chemistry/chemistry_evals.py +79 -0
  30. aurelian/agents/chemistry/chemistry_gradio.py +50 -0
  31. aurelian/agents/chemistry/chemistry_mcp.py +120 -0
  32. aurelian/agents/chemistry/chemistry_tools.py +121 -0
  33. aurelian/agents/chemistry/image_agent.py +15 -0
  34. aurelian/agents/d4d/__init__.py +30 -0
  35. aurelian/agents/d4d/d4d_agent.py +72 -0
  36. aurelian/agents/d4d/d4d_config.py +46 -0
  37. aurelian/agents/d4d/d4d_gradio.py +58 -0
  38. aurelian/agents/d4d/d4d_mcp.py +71 -0
  39. aurelian/agents/d4d/d4d_tools.py +157 -0
  40. aurelian/agents/d4d_agent.py +64 -0
  41. aurelian/agents/diagnosis/__init__.py +33 -0
  42. aurelian/agents/diagnosis/diagnosis_agent.py +53 -0
  43. aurelian/agents/diagnosis/diagnosis_config.py +48 -0
  44. aurelian/agents/diagnosis/diagnosis_evals.py +76 -0
  45. aurelian/agents/diagnosis/diagnosis_gradio.py +52 -0
  46. aurelian/agents/diagnosis/diagnosis_mcp.py +141 -0
  47. aurelian/agents/diagnosis/diagnosis_tools.py +204 -0
  48. aurelian/agents/diagnosis_agent.py +28 -0
  49. aurelian/agents/draw/__init__.py +3 -0
  50. aurelian/agents/draw/draw_agent.py +39 -0
  51. aurelian/agents/draw/draw_config.py +26 -0
  52. aurelian/agents/draw/draw_gradio.py +50 -0
  53. aurelian/agents/draw/draw_mcp.py +94 -0
  54. aurelian/agents/draw/draw_tools.py +100 -0
  55. aurelian/agents/draw/judge_agent.py +18 -0
  56. aurelian/agents/filesystem/__init__.py +0 -0
  57. aurelian/agents/filesystem/filesystem_config.py +27 -0
  58. aurelian/agents/filesystem/filesystem_gradio.py +49 -0
  59. aurelian/agents/filesystem/filesystem_mcp.py +89 -0
  60. aurelian/agents/filesystem/filesystem_tools.py +95 -0
  61. aurelian/agents/filesystem/py.typed +0 -0
  62. aurelian/agents/github/__init__.py +0 -0
  63. aurelian/agents/github/github_agent.py +83 -0
  64. aurelian/agents/github/github_cli.py +248 -0
  65. aurelian/agents/github/github_config.py +22 -0
  66. aurelian/agents/github/github_gradio.py +152 -0
  67. aurelian/agents/github/github_mcp.py +252 -0
  68. aurelian/agents/github/github_tools.py +408 -0
  69. aurelian/agents/github/github_tools.py.tmp +413 -0
  70. aurelian/agents/goann/__init__.py +13 -0
  71. aurelian/agents/goann/documents/Transcription_Factors_Annotation_Guidelines.md +1000 -0
  72. aurelian/agents/goann/documents/Transcription_Factors_Annotation_Guidelines.pdf +0 -0
  73. aurelian/agents/goann/documents/Transcription_Factors_Annotation_Guidelines_Paper.md +693 -0
  74. aurelian/agents/goann/documents/Transcription_Factors_Annotation_Guidelines_Paper.pdf +0 -0
  75. aurelian/agents/goann/goann_agent.py +90 -0
  76. aurelian/agents/goann/goann_config.py +90 -0
  77. aurelian/agents/goann/goann_evals.py +104 -0
  78. aurelian/agents/goann/goann_gradio.py +62 -0
  79. aurelian/agents/goann/goann_mcp.py +0 -0
  80. aurelian/agents/goann/goann_tools.py +65 -0
  81. aurelian/agents/gocam/__init__.py +43 -0
  82. aurelian/agents/gocam/documents/DNA-binding transcription factor activity annotation guidelines.docx +0 -0
  83. aurelian/agents/gocam/documents/DNA-binding transcription factor activity annotation guidelines.pdf +0 -0
  84. aurelian/agents/gocam/documents/DNA-binding_transcription_factor_activity_annotation_guidelines.md +100 -0
  85. aurelian/agents/gocam/documents/E3 ubiquitin ligases.docx +0 -0
  86. aurelian/agents/gocam/documents/E3 ubiquitin ligases.pdf +0 -0
  87. aurelian/agents/gocam/documents/E3_ubiquitin_ligases.md +134 -0
  88. aurelian/agents/gocam/documents/GO-CAM annotation guidelines README.docx +0 -0
  89. aurelian/agents/gocam/documents/GO-CAM annotation guidelines README.pdf +0 -0
  90. aurelian/agents/gocam/documents/GO-CAM modelling guidelines TO DO.docx +0 -0
  91. aurelian/agents/gocam/documents/GO-CAM modelling guidelines TO DO.pdf +0 -0
  92. aurelian/agents/gocam/documents/GO-CAM_annotation_guidelines_README.md +1 -0
  93. aurelian/agents/gocam/documents/GO-CAM_modelling_guidelines_TO_DO.md +3 -0
  94. aurelian/agents/gocam/documents/How to annotate complexes in GO-CAM.docx +0 -0
  95. aurelian/agents/gocam/documents/How to annotate complexes in GO-CAM.pdf +0 -0
  96. aurelian/agents/gocam/documents/How to annotate molecular adaptors.docx +0 -0
  97. aurelian/agents/gocam/documents/How to annotate molecular adaptors.pdf +0 -0
  98. aurelian/agents/gocam/documents/How to annotate sequestering proteins.docx +0 -0
  99. aurelian/agents/gocam/documents/How to annotate sequestering proteins.pdf +0 -0
  100. aurelian/agents/gocam/documents/How_to_annotate_complexes_in_GO-CAM.md +29 -0
  101. aurelian/agents/gocam/documents/How_to_annotate_molecular_adaptors.md +31 -0
  102. aurelian/agents/gocam/documents/How_to_annotate_sequestering_proteins.md +42 -0
  103. aurelian/agents/gocam/documents/Molecular adaptor activity.docx +0 -0
  104. aurelian/agents/gocam/documents/Molecular adaptor activity.pdf +0 -0
  105. aurelian/agents/gocam/documents/Molecular carrier activity.docx +0 -0
  106. aurelian/agents/gocam/documents/Molecular carrier activity.pdf +0 -0
  107. aurelian/agents/gocam/documents/Molecular_adaptor_activity.md +51 -0
  108. aurelian/agents/gocam/documents/Molecular_carrier_activity.md +41 -0
  109. aurelian/agents/gocam/documents/Protein sequestering activity.docx +0 -0
  110. aurelian/agents/gocam/documents/Protein sequestering activity.pdf +0 -0
  111. aurelian/agents/gocam/documents/Protein_sequestering_activity.md +50 -0
  112. aurelian/agents/gocam/documents/Signaling receptor activity annotation guidelines.docx +0 -0
  113. aurelian/agents/gocam/documents/Signaling receptor activity annotation guidelines.pdf +0 -0
  114. aurelian/agents/gocam/documents/Signaling_receptor_activity_annotation_guidelines.md +187 -0
  115. aurelian/agents/gocam/documents/Transcription coregulator activity.docx +0 -0
  116. aurelian/agents/gocam/documents/Transcription coregulator activity.pdf +0 -0
  117. aurelian/agents/gocam/documents/Transcription_coregulator_activity.md +36 -0
  118. aurelian/agents/gocam/documents/Transporter activity annotation annotation guidelines.docx +0 -0
  119. aurelian/agents/gocam/documents/Transporter activity annotation annotation guidelines.pdf +0 -0
  120. aurelian/agents/gocam/documents/Transporter_activity_annotation_annotation_guidelines.md +43 -0
  121. Regulatory Processes in GO-CAM.docx +0 -0
  122. Regulatory Processes in GO-CAM.pdf +0 -0
  123. aurelian/agents/gocam/documents/WIP_-_Regulation_and_Regulatory_Processes_in_GO-CAM.md +31 -0
  124. aurelian/agents/gocam/documents/md/DNA-binding_transcription_factor_activity_annotation_guidelines.md +131 -0
  125. aurelian/agents/gocam/documents/md/E3_ubiquitin_ligases.md +166 -0
  126. aurelian/agents/gocam/documents/md/GO-CAM_annotation_guidelines_README.md +1 -0
  127. aurelian/agents/gocam/documents/md/GO-CAM_modelling_guidelines_TO_DO.md +5 -0
  128. aurelian/agents/gocam/documents/md/How_to_annotate_complexes_in_GO-CAM.md +28 -0
  129. aurelian/agents/gocam/documents/md/How_to_annotate_molecular_adaptors.md +19 -0
  130. aurelian/agents/gocam/documents/md/How_to_annotate_sequestering_proteins.md +38 -0
  131. aurelian/agents/gocam/documents/md/Molecular_adaptor_activity.md +52 -0
  132. aurelian/agents/gocam/documents/md/Molecular_carrier_activity.md +59 -0
  133. aurelian/agents/gocam/documents/md/Protein_sequestering_activity.md +52 -0
  134. aurelian/agents/gocam/documents/md/Signaling_receptor_activity_annotation_guidelines.md +271 -0
  135. aurelian/agents/gocam/documents/md/Transcription_coregulator_activity.md +54 -0
  136. aurelian/agents/gocam/documents/md/Transporter_activity_annotation_annotation_guidelines.md +38 -0
  137. aurelian/agents/gocam/documents/md/WIP_-_Regulation_and_Regulatory_Processes_in_GO-CAM.md +39 -0
  138. aurelian/agents/gocam/documents/pandoc_md/Signaling_receptor_activity_annotation_guidelines.md +334 -0
  139. aurelian/agents/gocam/gocam_agent.py +240 -0
  140. aurelian/agents/gocam/gocam_config.py +85 -0
  141. aurelian/agents/gocam/gocam_curator_agent.py +46 -0
  142. aurelian/agents/gocam/gocam_evals.py +67 -0
  143. aurelian/agents/gocam/gocam_gradio.py +89 -0
  144. aurelian/agents/gocam/gocam_mcp.py +224 -0
  145. aurelian/agents/gocam/gocam_tools.py +294 -0
  146. aurelian/agents/linkml/__init__.py +0 -0
  147. aurelian/agents/linkml/linkml_agent.py +62 -0
  148. aurelian/agents/linkml/linkml_config.py +48 -0
  149. aurelian/agents/linkml/linkml_evals.py +66 -0
  150. aurelian/agents/linkml/linkml_gradio.py +45 -0
  151. aurelian/agents/linkml/linkml_mcp.py +186 -0
  152. aurelian/agents/linkml/linkml_tools.py +102 -0
  153. aurelian/agents/literature/__init__.py +3 -0
  154. aurelian/agents/literature/literature_agent.py +55 -0
  155. aurelian/agents/literature/literature_config.py +35 -0
  156. aurelian/agents/literature/literature_gradio.py +52 -0
  157. aurelian/agents/literature/literature_mcp.py +174 -0
  158. aurelian/agents/literature/literature_tools.py +182 -0
  159. aurelian/agents/monarch/__init__.py +25 -0
  160. aurelian/agents/monarch/monarch_agent.py +44 -0
  161. aurelian/agents/monarch/monarch_config.py +45 -0
  162. aurelian/agents/monarch/monarch_gradio.py +51 -0
  163. aurelian/agents/monarch/monarch_mcp.py +65 -0
  164. aurelian/agents/monarch/monarch_tools.py +113 -0
  165. aurelian/agents/oak/__init__.py +0 -0
  166. aurelian/agents/oak/oak_config.py +27 -0
  167. aurelian/agents/oak/oak_gradio.py +57 -0
  168. aurelian/agents/ontology_mapper/__init__.py +31 -0
  169. aurelian/agents/ontology_mapper/ontology_mapper_agent.py +56 -0
  170. aurelian/agents/ontology_mapper/ontology_mapper_config.py +50 -0
  171. aurelian/agents/ontology_mapper/ontology_mapper_evals.py +108 -0
  172. aurelian/agents/ontology_mapper/ontology_mapper_gradio.py +58 -0
  173. aurelian/agents/ontology_mapper/ontology_mapper_mcp.py +81 -0
  174. aurelian/agents/ontology_mapper/ontology_mapper_tools.py +147 -0
  175. aurelian/agents/phenopackets/__init__.py +3 -0
  176. aurelian/agents/phenopackets/phenopackets_agent.py +58 -0
  177. aurelian/agents/phenopackets/phenopackets_config.py +72 -0
  178. aurelian/agents/phenopackets/phenopackets_evals.py +99 -0
  179. aurelian/agents/phenopackets/phenopackets_gradio.py +55 -0
  180. aurelian/agents/phenopackets/phenopackets_mcp.py +178 -0
  181. aurelian/agents/phenopackets/phenopackets_tools.py +127 -0
  182. aurelian/agents/rag/__init__.py +40 -0
  183. aurelian/agents/rag/rag_agent.py +83 -0
  184. aurelian/agents/rag/rag_config.py +80 -0
  185. aurelian/agents/rag/rag_gradio.py +67 -0
  186. aurelian/agents/rag/rag_mcp.py +107 -0
  187. aurelian/agents/rag/rag_tools.py +189 -0
  188. aurelian/agents/rag_agent.py +54 -0
  189. aurelian/agents/robot/__init__.py +0 -0
  190. aurelian/agents/robot/assets/__init__.py +3 -0
  191. aurelian/agents/robot/assets/template.md +384 -0
  192. aurelian/agents/robot/robot_config.py +25 -0
  193. aurelian/agents/robot/robot_gradio.py +46 -0
  194. aurelian/agents/robot/robot_mcp.py +100 -0
  195. aurelian/agents/robot/robot_ontology_agent.py +139 -0
  196. aurelian/agents/robot/robot_tools.py +50 -0
  197. aurelian/agents/talisman/__init__.py +3 -0
  198. aurelian/agents/talisman/talisman_agent.py +126 -0
  199. aurelian/agents/talisman/talisman_config.py +66 -0
  200. aurelian/agents/talisman/talisman_gradio.py +50 -0
  201. aurelian/agents/talisman/talisman_mcp.py +168 -0
  202. aurelian/agents/talisman/talisman_tools.py +720 -0
  203. aurelian/agents/ubergraph/__init__.py +40 -0
  204. aurelian/agents/ubergraph/ubergraph_agent.py +71 -0
  205. aurelian/agents/ubergraph/ubergraph_config.py +79 -0
  206. aurelian/agents/ubergraph/ubergraph_gradio.py +48 -0
  207. aurelian/agents/ubergraph/ubergraph_mcp.py +69 -0
  208. aurelian/agents/ubergraph/ubergraph_tools.py +118 -0
  209. aurelian/agents/uniprot/__init__.py +37 -0
  210. aurelian/agents/uniprot/uniprot_agent.py +43 -0
  211. aurelian/agents/uniprot/uniprot_config.py +43 -0
  212. aurelian/agents/uniprot/uniprot_evals.py +99 -0
  213. aurelian/agents/uniprot/uniprot_gradio.py +48 -0
  214. aurelian/agents/uniprot/uniprot_mcp.py +168 -0
  215. aurelian/agents/uniprot/uniprot_tools.py +136 -0
  216. aurelian/agents/web/__init__.py +0 -0
  217. aurelian/agents/web/web_config.py +27 -0
  218. aurelian/agents/web/web_gradio.py +48 -0
  219. aurelian/agents/web/web_mcp.py +50 -0
  220. aurelian/agents/web/web_tools.py +108 -0
  221. aurelian/chat.py +23 -0
  222. aurelian/cli.py +800 -0
  223. aurelian/dependencies/__init__.py +0 -0
  224. aurelian/dependencies/workdir.py +78 -0
  225. aurelian/mcp/__init__.py +0 -0
  226. aurelian/mcp/amigo_mcp_test.py +86 -0
  227. aurelian/mcp/config_generator.py +123 -0
  228. aurelian/mcp/example_config.json +43 -0
  229. aurelian/mcp/generate_sample_config.py +37 -0
  230. aurelian/mcp/gocam_mcp_test.py +126 -0
  231. aurelian/mcp/linkml_mcp_tools.py +190 -0
  232. aurelian/mcp/mcp_discovery.py +87 -0
  233. aurelian/mcp/mcp_test.py +31 -0
  234. aurelian/mcp/phenopackets_mcp_test.py +103 -0
  235. aurelian/tools/__init__.py +0 -0
  236. aurelian/tools/web/__init__.py +0 -0
  237. aurelian/tools/web/url_download.py +51 -0
  238. aurelian/utils/__init__.py +0 -0
  239. aurelian/utils/async_utils.py +15 -0
  240. aurelian/utils/data_utils.py +32 -0
  241. aurelian/utils/documentation_manager.py +59 -0
  242. aurelian/utils/doi_fetcher.py +238 -0
  243. aurelian/utils/ontology_utils.py +68 -0
  244. aurelian/utils/pdf_fetcher.py +23 -0
  245. aurelian/utils/process_logs.py +100 -0
  246. aurelian/utils/pubmed_utils.py +238 -0
  247. aurelian/utils/pytest_report_to_markdown.py +67 -0
  248. aurelian/utils/robot_ontology_utils.py +112 -0
  249. aurelian/utils/search_utils.py +95 -0
  250. aurelian-0.3.2.dist-info/LICENSE +22 -0
  251. aurelian-0.3.2.dist-info/METADATA +105 -0
  252. aurelian-0.3.2.dist-info/RECORD +254 -0
  253. aurelian-0.3.2.dist-info/WHEEL +4 -0
  254. aurelian-0.3.2.dist-info/entry_points.txt +3 -0
@@ -0,0 +1,252 @@
1
+ """
2
+ MCP wrapper for the GitHub agent.
3
+
4
+ This module provides a multi-component protocol wrapper for the GitHub agent,
5
+ allowing it to be used via the MCP protocol.
6
+ """
7
+ from typing import Dict, List, Optional, Union, Any
8
+
9
+ from mcp.agent import Session
10
+ from mcp.telemetry import metadata_event
11
+ from pydantic_ai import RunContext
12
+
13
+ from aurelian.agents.github.github_config import GitHubDependencies
14
+ from aurelian.agents.github.github_tools import (
15
+ list_pull_requests,
16
+ view_pull_request,
17
+ list_issues,
18
+ view_issue,
19
+ get_pr_closing_issues,
20
+ get_commit_before_pr,
21
+ search_code,
22
+ clone_repository
23
+ )
24
+ from aurelian.dependencies.workdir import WorkDir
25
+
26
+
27
+ class GitHubMCP:
28
+ """MCP wrapper for the GitHub agent."""
29
+
30
+ def __init__(self, workdir: WorkDir):
31
+ """
32
+ Initialize the GitHub MCP wrapper.
33
+
34
+ Args:
35
+ workdir: The working directory for the agent
36
+ """
37
+ self.deps = GitHubDependencies()
38
+ self.deps.workdir = workdir
39
+
40
+ def _create_context(self) -> RunContext[GitHubDependencies]:
41
+ """Create a RunContext with the dependencies."""
42
+ return RunContext[GitHubDependencies](
43
+ deps=self.deps,
44
+ model="openai:gpt-4o",
45
+ usage=None,
46
+ prompt=""
47
+ )
48
+
49
+ def list_pull_requests(
50
+ self,
51
+ state: str = "open",
52
+ limit: int = 10,
53
+ label: Optional[str] = None,
54
+ author: Optional[str] = None,
55
+ base_branch: Optional[str] = None,
56
+ repo: Optional[str] = None,
57
+ session: Optional[Session] = None
58
+ ) -> List[Dict[str, Any]]:
59
+ """
60
+ List pull requests from GitHub.
61
+
62
+ Args:
63
+ state: Filter by state (open, closed, merged, all)
64
+ limit: Maximum number of PRs to return
65
+ label: Filter by label
66
+ author: Filter by author
67
+ base_branch: Filter by base branch
68
+ repo: Repository to list PRs from (format: owner/repo), defaults to current repo
69
+ session: Optional MCP session
70
+
71
+ Returns:
72
+ List of pull requests as dictionaries
73
+ """
74
+ if session:
75
+ metadata_event(session, {"tool": "list_pull_requests"})
76
+
77
+ ctx = self._create_context()
78
+ return list_pull_requests(ctx, state, limit, label, author, base_branch, repo)
79
+
80
+ def view_pull_request(
81
+ self,
82
+ pr_number: Union[int, str],
83
+ repo: Optional[str] = None,
84
+ session: Optional[Session] = None
85
+ ) -> Dict[str, Any]:
86
+ """
87
+ Get detailed information about a specific pull request.
88
+
89
+ Args:
90
+ pr_number: The pull request number
91
+ repo: Repository the PR belongs to (format: owner/repo), defaults to current repo
92
+ session: Optional MCP session
93
+
94
+ Returns:
95
+ Pull request details as a dictionary
96
+ """
97
+ if session:
98
+ metadata_event(session, {"tool": "view_pull_request", "pr_number": pr_number})
99
+
100
+ ctx = self._create_context()
101
+ return view_pull_request(ctx, pr_number, repo)
102
+
103
+ def list_issues(
104
+ self,
105
+ state: str = "open",
106
+ limit: int = 10,
107
+ label: Optional[str] = None,
108
+ author: Optional[str] = None,
109
+ assignee: Optional[str] = None,
110
+ repo: Optional[str] = None,
111
+ session: Optional[Session] = None
112
+ ) -> List[Dict[str, Any]]:
113
+ """
114
+ List issues from GitHub.
115
+
116
+ Args:
117
+ state: Filter by state (open, closed, all)
118
+ limit: Maximum number of issues to return
119
+ label: Filter by label
120
+ author: Filter by author
121
+ assignee: Filter by assignee
122
+ repo: Repository to list issues from (format: owner/repo), defaults to current repo
123
+ session: Optional MCP session
124
+
125
+ Returns:
126
+ List of issues as dictionaries
127
+ """
128
+ if session:
129
+ metadata_event(session, {"tool": "list_issues"})
130
+
131
+ ctx = self._create_context()
132
+ return list_issues(ctx, state, limit, label, author, assignee, repo)
133
+
134
+ def view_issue(
135
+ self,
136
+ issue_number: Union[int, str],
137
+ repo: Optional[str] = None,
138
+ session: Optional[Session] = None
139
+ ) -> Dict[str, Any]:
140
+ """
141
+ Get detailed information about a specific issue.
142
+
143
+ Args:
144
+ issue_number: The issue number
145
+ repo: Repository the issue belongs to (format: owner/repo), defaults to current repo
146
+ session: Optional MCP session
147
+
148
+ Returns:
149
+ Issue details as a dictionary
150
+ """
151
+ if session:
152
+ metadata_event(session, {"tool": "view_issue", "issue_number": issue_number})
153
+
154
+ ctx = self._create_context()
155
+ return view_issue(ctx, issue_number, repo)
156
+
157
+ def get_pr_closing_issues(
158
+ self,
159
+ pr_number: Union[int, str],
160
+ repo: Optional[str] = None,
161
+ session: Optional[Session] = None
162
+ ) -> List[Dict[str, Any]]:
163
+ """
164
+ Get the issues that will be closed by a specific pull request.
165
+
166
+ Args:
167
+ pr_number: The pull request number
168
+ repo: Repository the PR belongs to (format: owner/repo), defaults to current repo
169
+ session: Optional MCP session
170
+
171
+ Returns:
172
+ List of issues closed by the PR
173
+ """
174
+ if session:
175
+ metadata_event(session, {"tool": "get_pr_closing_issues", "pr_number": pr_number})
176
+
177
+ ctx = self._create_context()
178
+ return get_pr_closing_issues(ctx, pr_number, repo)
179
+
180
+ def get_commit_before_pr(
181
+ self,
182
+ pr_number: Union[int, str],
183
+ repo: Optional[str] = None,
184
+ session: Optional[Session] = None
185
+ ) -> Dict[str, str]:
186
+ """
187
+ Get the commit hash before a PR branch was created.
188
+ This identifies the commit from which the PR branch was created.
189
+
190
+ Args:
191
+ pr_number: The pull request number
192
+ repo: Repository the PR belongs to (format: owner/repo), defaults to current repo
193
+ session: Optional MCP session
194
+
195
+ Returns:
196
+ Dictionary with commit hash and message
197
+ """
198
+ if session:
199
+ metadata_event(session, {"tool": "get_commit_before_pr", "pr_number": pr_number})
200
+
201
+ ctx = self._create_context()
202
+ return get_commit_before_pr(ctx, pr_number, repo)
203
+
204
+ def search_code(
205
+ self,
206
+ query: str,
207
+ repo: Optional[str] = None,
208
+ session: Optional[Session] = None
209
+ ) -> List[Dict[str, Any]]:
210
+ """
211
+ Search for code in a repository.
212
+
213
+ Args:
214
+ query: The search query
215
+ repo: Repository to search in (format: owner/repo), defaults to current repo
216
+ session: Optional MCP session
217
+
218
+ Returns:
219
+ List of code matches as dictionaries
220
+ """
221
+ if session:
222
+ metadata_event(session, {"tool": "search_code", "query": query})
223
+
224
+ ctx = self._create_context()
225
+ return search_code(ctx, query, repo)
226
+
227
+ def clone_repository(
228
+ self,
229
+ repo: str,
230
+ directory: Optional[str] = None,
231
+ branch: Optional[str] = None,
232
+ depth: Optional[int] = None,
233
+ session: Optional[Session] = None
234
+ ) -> str:
235
+ """
236
+ Clone a GitHub repository to a local directory.
237
+
238
+ Args:
239
+ repo: Repository to clone (format: owner/repo)
240
+ directory: Directory name to clone into (defaults to repo name)
241
+ branch: Branch to clone (defaults to default branch)
242
+ depth: Depth limit for clone (use for shallow clones)
243
+ session: Optional MCP session
244
+
245
+ Returns:
246
+ Path to the cloned repository
247
+ """
248
+ if session:
249
+ metadata_event(session, {"tool": "clone_repository", "repo": repo})
250
+
251
+ ctx = self._create_context()
252
+ return clone_repository(ctx, repo, directory, branch, depth)
@@ -0,0 +1,408 @@
1
+ """
2
+ Tools for interacting with GitHub via command line tools (gh and git).
3
+ """
4
+ import json
5
+ import subprocess
6
+ import os
7
+ import asyncio
8
+ from typing import Dict, List, Optional, Union, Any, Protocol
9
+
10
+ from pydantic_ai import RunContext
11
+
12
+ from aurelian.dependencies.workdir import HasWorkdir
13
+
14
+ # Field constants for GitHub API responses
15
+ NUMBER = "number"
16
+ TITLE = "title"
17
+ AUTHOR = "author"
18
+ URL = "url"
19
+ STATE = "state"
20
+ CREATED_AT = "createdAt"
21
+ CLOSED_AT = "closedAt"
22
+ BODY = "body"
23
+ LABELS = "labels"
24
+ ASSIGNEES = "assignees"
25
+ COMMENTS = "comments"
26
+ BASE_REF_NAME = "baseRefName"
27
+ HEAD_REF_NAME = "headRefName"
28
+ IS_DRAFT = "isDraft"
29
+ COMMITS = "commits"
30
+ FILES = "files"
31
+ REVIEWS = "reviews"
32
+ CLOSING_ISSUES = "closingIssues"
33
+ PATH = "path"
34
+ REPOSITORY = "repository"
35
+ TEXT_MATCHES = "textMatches"
36
+
37
+ # Field groupings for common use cases
38
+ CORE_FIELDS = [NUMBER, TITLE, AUTHOR, URL, STATE, CREATED_AT, CLOSED_AT, BODY]
39
+ ISSUE_FIELDS = CORE_FIELDS + [LABELS, ASSIGNEES]
40
+ ISSUE_DETAIL_FIELDS = ISSUE_FIELDS + [COMMENTS]
41
+ PR_FIELDS = CORE_FIELDS + [BASE_REF_NAME, HEAD_REF_NAME, IS_DRAFT, LABELS]
42
+ PR_DETAIL_FIELDS = PR_FIELDS + [COMMITS, FILES, COMMENTS, REVIEWS, CLOSING_ISSUES]
43
+ SEARCH_CODE_FIELDS = [PATH, REPOSITORY, TEXT_MATCHES]
44
+
45
+ async def _run_gh_command(args: List[str], cwd: Optional[str] = None) -> str:
46
+ """
47
+ Run a GitHub CLI command and return the output.
48
+
49
+ Args:
50
+ args: List of command line arguments to pass to gh
51
+ cwd: Current working directory in which to run the command
52
+
53
+ Returns:
54
+ The command output as a string
55
+
56
+ Raises:
57
+ subprocess.CalledProcessError: If the command fails
58
+ """
59
+ cmd = ["gh"] + args
60
+ proc = await asyncio.create_subprocess_exec(
61
+ *cmd,
62
+ stdout=asyncio.subprocess.PIPE,
63
+ stderr=asyncio.subprocess.PIPE,
64
+ cwd=cwd
65
+ )
66
+ stdout, stderr = await proc.communicate()
67
+
68
+ if proc.returncode != 0:
69
+ error_msg = stderr.decode('utf-8').strip()
70
+ raise subprocess.CalledProcessError(proc.returncode, cmd, stdout, stderr)
71
+
72
+ return stdout.decode('utf-8').strip()
73
+
74
+ async def list_pull_requests(
75
+ ctx: RunContext[HasWorkdir],
76
+ state: str = "open",
77
+ limit: int = 10,
78
+ label: Optional[str] = None,
79
+ author: Optional[str] = None,
80
+ base_branch: Optional[str] = None,
81
+ repo: Optional[str] = None
82
+ ) -> List[Dict[str, Any]]:
83
+ """
84
+ List pull requests from GitHub.
85
+
86
+ Args:
87
+ ctx: Run context with workdir dependency
88
+ state: Filter by state (open, closed, merged, all)
89
+ limit: Maximum number of PRs to return
90
+ label: Filter by label
91
+ author: Filter by author
92
+ base_branch: Filter by base branch
93
+ repo: Repository to list PRs from (format: owner/repo), defaults to current repo
94
+
95
+ Returns:
96
+ List of pull requests as dictionaries
97
+ """
98
+ # Get workdir location - handle both string and object workdirs
99
+ workdir = ctx.deps.workdir
100
+ if hasattr(workdir, 'location'):
101
+ workdir = workdir.location
102
+
103
+ args = ["pr", "list", "--json", ",".join(PR_FIELDS)]
104
+
105
+ if state and state != "all":
106
+ args.extend(["--state", state])
107
+
108
+ if limit:
109
+ args.extend(["--limit", str(limit)])
110
+
111
+ if label:
112
+ args.extend(["--label", label])
113
+
114
+ if author:
115
+ args.extend(["--author", author])
116
+
117
+ if base_branch:
118
+ args.extend(["--base", base_branch])
119
+
120
+ if repo:
121
+ args.extend(["--repo", repo])
122
+
123
+ output = await _run_gh_command(args, cwd=workdir)
124
+ return json.loads(output)
125
+
126
+ async def view_pull_request(
127
+ ctx: RunContext[HasWorkdir],
128
+ pr_number: Union[int, str],
129
+ repo: Optional[str] = None
130
+ ) -> Dict[str, Any]:
131
+ """
132
+ Get detailed information about a specific pull request.
133
+
134
+ Args:
135
+ ctx: Run context with workdir dependency
136
+ pr_number: The pull request number
137
+ repo: Repository the PR belongs to (format: owner/repo), defaults to current repo
138
+
139
+ Returns:
140
+ Pull request details as a dictionary
141
+ """
142
+ # Get workdir location - handle both string and object workdirs
143
+ workdir = ctx.deps.workdir
144
+ if hasattr(workdir, 'location'):
145
+ workdir = workdir.location
146
+
147
+ args = ["pr", "view", str(pr_number), "--json", ",".join(PR_DETAIL_FIELDS)]
148
+
149
+ if repo:
150
+ args.extend(["--repo", repo])
151
+
152
+ output = await _run_gh_command(args, cwd=workdir)
153
+ return json.loads(output)
154
+
155
+ async def list_issues(
156
+ ctx: RunContext[HasWorkdir],
157
+ state: str = "open",
158
+ limit: int = 10,
159
+ label: Optional[str] = None,
160
+ author: Optional[str] = None,
161
+ assignee: Optional[str] = None,
162
+ repo: Optional[str] = None
163
+ ) -> List[Dict[str, Any]]:
164
+ """
165
+ List issues from GitHub.
166
+
167
+ Args:
168
+ ctx: Run context with workdir dependency
169
+ state: Filter by state (open, closed, all)
170
+ limit: Maximum number of issues to return
171
+ label: Filter by label
172
+ author: Filter by author
173
+ assignee: Filter by assignee
174
+ repo: Repository to list issues from (format: owner/repo), defaults to current repo
175
+
176
+ Returns:
177
+ List of issues as dictionaries
178
+ """
179
+ # Get workdir location - handle both string and object workdirs
180
+ workdir = ctx.deps.workdir
181
+ if hasattr(workdir, 'location'):
182
+ workdir = workdir.location
183
+
184
+ args = ["issue", "list", "--json", ",".join(ISSUE_FIELDS)]
185
+
186
+ if state and state != "all":
187
+ args.extend(["--state", state])
188
+
189
+ if limit:
190
+ args.extend(["--limit", str(limit)])
191
+
192
+ if label:
193
+ args.extend(["--label", label])
194
+
195
+ if author:
196
+ args.extend(["--author", author])
197
+
198
+ if assignee:
199
+ args.extend(["--assignee", assignee])
200
+
201
+ if repo:
202
+ args.extend(["--repo", repo])
203
+
204
+ output = await _run_gh_command(args, cwd=workdir)
205
+ return json.loads(output)
206
+
207
+ async def view_issue(
208
+ ctx: RunContext[HasWorkdir],
209
+ issue_number: Union[int, str],
210
+ repo: Optional[str] = None
211
+ ) -> Dict[str, Any]:
212
+ """
213
+ Get detailed information about a specific issue.
214
+
215
+ Args:
216
+ ctx: Run context with workdir dependency
217
+ issue_number: The issue number
218
+ repo: Repository the issue belongs to (format: owner/repo), defaults to current repo
219
+
220
+ Returns:
221
+ Issue details as a dictionary
222
+ """
223
+ # Get workdir location - handle both string and object workdirs
224
+ workdir = ctx.deps.workdir
225
+ if hasattr(workdir, 'location'):
226
+ workdir = workdir.location
227
+
228
+ args = ["issue", "view", str(issue_number), "--json", ",".join(ISSUE_DETAIL_FIELDS)]
229
+
230
+ if repo:
231
+ args.extend(["--repo", repo])
232
+
233
+ output = await _run_gh_command(args, cwd=workdir)
234
+ return json.loads(output)
235
+
236
+ async def get_pr_closing_issues(
237
+ ctx: RunContext[HasWorkdir],
238
+ pr_number: Union[int, str],
239
+ repo: Optional[str] = None
240
+ ) -> List[Dict[str, Any]]:
241
+ """
242
+ Get the issues that will be closed by a specific pull request.
243
+
244
+ Args:
245
+ ctx: Run context with workdir dependency
246
+ pr_number: The pull request number
247
+ repo: Repository the PR belongs to (format: owner/repo), defaults to current repo
248
+
249
+ Returns:
250
+ List of issues closed by the PR
251
+ """
252
+ pr_details = await view_pull_request(ctx, pr_number, repo)
253
+ return pr_details.get("closingIssues", [])
254
+
255
+ async def _run_git_command(args: List[str], cwd: Optional[str] = None) -> str:
256
+ """
257
+ Run a git command and return the output.
258
+
259
+ Args:
260
+ args: List of command line arguments to pass to git
261
+ cwd: Current working directory in which to run the command
262
+
263
+ Returns:
264
+ The command output as a string
265
+
266
+ Raises:
267
+ subprocess.CalledProcessError: If the command fails
268
+ """
269
+ cmd = ["git"] + args
270
+ proc = await asyncio.create_subprocess_exec(
271
+ *cmd,
272
+ stdout=asyncio.subprocess.PIPE,
273
+ stderr=asyncio.subprocess.PIPE,
274
+ cwd=cwd
275
+ )
276
+ stdout, stderr = await proc.communicate()
277
+
278
+ if proc.returncode != 0:
279
+ error_msg = stderr.decode('utf-8').strip()
280
+ raise subprocess.CalledProcessError(proc.returncode, cmd, stdout, stderr)
281
+
282
+ return stdout.decode('utf-8').strip()
283
+
284
+ async def get_commit_before_pr(
285
+ ctx: RunContext[HasWorkdir],
286
+ pr_number: Union[int, str],
287
+ repo: Optional[str] = None
288
+ ) -> Dict[str, str]:
289
+ """
290
+ Get the commit hash before a PR branch was created.
291
+ This identifies the commit from which the PR branch was created.
292
+
293
+ Args:
294
+ ctx: Run context with workdir dependency
295
+ pr_number: The pull request number
296
+ repo: Repository the PR belongs to (format: owner/repo), defaults to current repo
297
+
298
+ Returns:
299
+ Dictionary with commit hash and message
300
+ """
301
+ # Get workdir location - handle both string and object workdirs
302
+ workdir = ctx.deps.workdir
303
+ if hasattr(workdir, 'location'):
304
+ workdir = workdir.location
305
+
306
+ # First get PR details to identify the base and head branches
307
+ pr_details = await view_pull_request(ctx, pr_number, repo)
308
+ base_branch = pr_details["baseRefName"]
309
+ head_branch = pr_details["headRefName"]
310
+
311
+ # Get the first commit on the PR branch
312
+ first_pr_commit = await _run_git_command(
313
+ ["log", base_branch + ".." + head_branch, "--reverse", "--format=%H", "--max-count=1"],
314
+ cwd=workdir
315
+ )
316
+
317
+ # Get the parent commit of the first PR commit (the commit before the PR started)
318
+ base_commit = await _run_git_command(["rev-parse", first_pr_commit + "^"], cwd=workdir)
319
+ commit_message = await _run_git_command(["log", "-1", "--format=%B", base_commit], cwd=workdir)
320
+
321
+ return {
322
+ "hash": base_commit,
323
+ "message": commit_message
324
+ }
325
+
326
+ async def search_code(
327
+ ctx: RunContext[HasWorkdir],
328
+ query: str,
329
+ repo: Optional[str] = None
330
+ ) -> List[Dict[str, Any]]:
331
+ """
332
+ Search for code in a repository.
333
+
334
+ Args:
335
+ ctx: Run context with workdir dependency
336
+ query: The search query
337
+ repo: Repository to search in (format: owner/repo), defaults to current repo
338
+
339
+ Returns:
340
+ List of code matches as dictionaries
341
+ """
342
+ # Get workdir location - handle both string and object workdirs
343
+ workdir = ctx.deps.workdir
344
+ if hasattr(workdir, 'location'):
345
+ workdir = workdir.location
346
+
347
+ args = ["search", "code", query, "--json", ",".join(SEARCH_CODE_FIELDS)]
348
+
349
+ if repo:
350
+ query = f"repo:{repo} {query}"
351
+ args[2] = query
352
+
353
+ output = await _run_gh_command(args, cwd=workdir)
354
+ return json.loads(output)
355
+
356
+ async def clone_repository(
357
+ ctx: RunContext[HasWorkdir],
358
+ repo: str,
359
+ directory: Optional[str] = None,
360
+ branch: Optional[str] = None,
361
+ depth: Optional[int] = None
362
+ ) -> str:
363
+ """
364
+ Clone a GitHub repository to a local directory.
365
+
366
+ Args:
367
+ ctx: Run context with workdir dependency
368
+ repo: Repository to clone (format: owner/repo)
369
+ directory: Directory name to clone into (defaults to repo name)
370
+ branch: Branch to clone (defaults to default branch)
371
+ depth: Depth limit for clone (use for shallow clones)
372
+
373
+ Returns:
374
+ Path to the cloned repository
375
+ """
376
+ # Get workdir location - handle both string and object workdirs
377
+ workdir = ctx.deps.workdir
378
+ if hasattr(workdir, 'location'):
379
+ workdir = workdir.location
380
+
381
+ # Build command
382
+ args = ["repo", "clone", repo]
383
+
384
+ # Add optional directory
385
+ if directory:
386
+ args.append(directory)
387
+ else:
388
+ # Default to repo name
389
+ directory = repo.split("/")[-1]
390
+
391
+ # Add branch if specified
392
+ if branch:
393
+ args.extend(["--branch", branch])
394
+
395
+ # Add depth if specified
396
+ if depth:
397
+ args.extend(["--depth", str(depth)])
398
+
399
+ # Run the clone command
400
+ await _run_gh_command(args, cwd=workdir)
401
+
402
+ # Return the full path to the cloned repo
403
+ if os.path.isabs(workdir):
404
+ return os.path.join(workdir, directory)
405
+ else:
406
+ # If workdir is relative, make it absolute
407
+ abs_workdir = os.path.abspath(workdir)
408
+ return os.path.join(abs_workdir, directory)