fastmcp-agents-library-agent-github 0.5.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- fastmcp_agents_library_agent_github-0.5.1/.gitignore +183 -0
- fastmcp_agents_library_agent_github-0.5.1/PKG-INFO +10 -0
- fastmcp_agents_library_agent_github-0.5.1/README.md +0 -0
- fastmcp_agents_library_agent_github-0.5.1/fastmcp_agents/library/agent/github/__init__.py +0 -0
- fastmcp_agents_library_agent_github-0.5.1/fastmcp_agents/library/agent/github/agents.py.disabled +22 -0
- fastmcp_agents_library_agent_github-0.5.1/fastmcp_agents/library/agent/github/gather_background.py +92 -0
- fastmcp_agents_library_agent_github-0.5.1/fastmcp_agents/library/agent/github/investigate_issue.py +67 -0
- fastmcp_agents_library_agent_github-0.5.1/fastmcp_agents/library/agent/github/issue_triage.py.disabled +191 -0
- fastmcp_agents_library_agent_github-0.5.1/fastmcp_agents/library/agent/github/respond_to_issue.py +98 -0
- fastmcp_agents_library_agent_github-0.5.1/fastmcp_agents/library/agent/github/shared/git.py +2 -0
- fastmcp_agents_library_agent_github-0.5.1/fastmcp_agents/library/agent/github/shared/helpers.py +213 -0
- fastmcp_agents_library_agent_github-0.5.1/fastmcp_agents/library/agent/github/shared/logging.py +113 -0
- fastmcp_agents_library_agent_github-0.5.1/fastmcp_agents/library/agent/github/shared/mcp.py +16 -0
- fastmcp_agents_library_agent_github-0.5.1/fastmcp_agents/library/agent/github/shared/prompts.py +38 -0
- fastmcp_agents_library_agent_github-0.5.1/pyproject.toml +54 -0
@@ -0,0 +1,183 @@
|
|
1
|
+
# Byte-compiled / optimized / DLL files
|
2
|
+
__pycache__/
|
3
|
+
*.py[cod]
|
4
|
+
*$py.class
|
5
|
+
|
6
|
+
# C extensions
|
7
|
+
*.so
|
8
|
+
|
9
|
+
# Distribution / packaging
|
10
|
+
.Python
|
11
|
+
build/
|
12
|
+
develop-eggs/
|
13
|
+
dist/
|
14
|
+
downloads/
|
15
|
+
eggs/
|
16
|
+
.eggs/
|
17
|
+
lib/
|
18
|
+
lib64/
|
19
|
+
parts/
|
20
|
+
sdist/
|
21
|
+
var/
|
22
|
+
wheels/
|
23
|
+
share/python-wheels/
|
24
|
+
*.egg-info/
|
25
|
+
.installed.cfg
|
26
|
+
*.egg
|
27
|
+
MANIFEST
|
28
|
+
|
29
|
+
# PyInstaller
|
30
|
+
# Usually these files are written by a python script from a template
|
31
|
+
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
32
|
+
*.manifest
|
33
|
+
*.spec
|
34
|
+
|
35
|
+
# Installer logs
|
36
|
+
pip-log.txt
|
37
|
+
pip-delete-this-directory.txt
|
38
|
+
|
39
|
+
# Unit test / coverage reports
|
40
|
+
htmlcov/
|
41
|
+
.tox/
|
42
|
+
.nox/
|
43
|
+
.coverage
|
44
|
+
.coverage.*
|
45
|
+
.cache
|
46
|
+
nosetests.xml
|
47
|
+
coverage.xml
|
48
|
+
*.cover
|
49
|
+
*.py,cover
|
50
|
+
.hypothesis/
|
51
|
+
.pytest_cache/
|
52
|
+
cover/
|
53
|
+
|
54
|
+
# Translations
|
55
|
+
*.mo
|
56
|
+
*.pot
|
57
|
+
|
58
|
+
# Django stuff:
|
59
|
+
*.log
|
60
|
+
local_settings.py
|
61
|
+
db.sqlite3
|
62
|
+
db.sqlite3-journal
|
63
|
+
|
64
|
+
# Flask stuff:
|
65
|
+
instance/
|
66
|
+
.webassets-cache
|
67
|
+
|
68
|
+
# Scrapy stuff:
|
69
|
+
.scrapy
|
70
|
+
|
71
|
+
# Sphinx documentation
|
72
|
+
docs/_build/
|
73
|
+
|
74
|
+
# PyBuilder
|
75
|
+
.pybuilder/
|
76
|
+
target/
|
77
|
+
|
78
|
+
# Jupyter Notebook
|
79
|
+
.ipynb_checkpoints
|
80
|
+
|
81
|
+
# IPython
|
82
|
+
profile_default/
|
83
|
+
ipython_config.py
|
84
|
+
|
85
|
+
# pyenv
|
86
|
+
# For a library or package, you might want to ignore these files since the code is
|
87
|
+
# intended to run in multiple environments; otherwise, check them in:
|
88
|
+
# .python-version
|
89
|
+
|
90
|
+
# pipenv
|
91
|
+
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
|
92
|
+
# However, in case of collaboration, if having platform-specific dependencies or dependencies
|
93
|
+
# having no cross-platform support, pipenv may install dependencies that don't work, or not
|
94
|
+
# install all needed dependencies.
|
95
|
+
#Pipfile.lock
|
96
|
+
|
97
|
+
# UV
|
98
|
+
# Similar to Pipfile.lock, it is generally recommended to include uv.lock in version control.
|
99
|
+
# This is especially recommended for binary packages to ensure reproducibility, and is more
|
100
|
+
# commonly ignored for libraries.
|
101
|
+
#uv.lock
|
102
|
+
|
103
|
+
# poetry
|
104
|
+
# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
|
105
|
+
# This is especially recommended for binary packages to ensure reproducibility, and is more
|
106
|
+
# commonly ignored for libraries.
|
107
|
+
# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
|
108
|
+
#poetry.lock
|
109
|
+
|
110
|
+
# pdm
|
111
|
+
# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
|
112
|
+
#pdm.lock
|
113
|
+
# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
|
114
|
+
# in version control.
|
115
|
+
# https://pdm.fming.dev/latest/usage/project/#working-with-version-control
|
116
|
+
.pdm.toml
|
117
|
+
.pdm-python
|
118
|
+
.pdm-build/
|
119
|
+
|
120
|
+
# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
|
121
|
+
__pypackages__/
|
122
|
+
|
123
|
+
# Celery stuff
|
124
|
+
celerybeat-schedule
|
125
|
+
celerybeat.pid
|
126
|
+
|
127
|
+
# SageMath parsed files
|
128
|
+
*.sage.py
|
129
|
+
|
130
|
+
# Environments
|
131
|
+
.env
|
132
|
+
.env.*
|
133
|
+
.venv
|
134
|
+
env/
|
135
|
+
venv/
|
136
|
+
ENV/
|
137
|
+
env.bak/
|
138
|
+
venv.bak/
|
139
|
+
|
140
|
+
# Spyder project settings
|
141
|
+
.spyderproject
|
142
|
+
.spyproject
|
143
|
+
|
144
|
+
# Rope project settings
|
145
|
+
.ropeproject
|
146
|
+
|
147
|
+
# mkdocs documentation
|
148
|
+
/site
|
149
|
+
|
150
|
+
# mypy
|
151
|
+
.mypy_cache/
|
152
|
+
.dmypy.json
|
153
|
+
dmypy.json
|
154
|
+
|
155
|
+
# Pyre type checker
|
156
|
+
.pyre/
|
157
|
+
|
158
|
+
# pytype static type analyzer
|
159
|
+
.pytype/
|
160
|
+
|
161
|
+
# Cython debug symbols
|
162
|
+
cython_debug/
|
163
|
+
|
164
|
+
# PyCharm
|
165
|
+
# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
|
166
|
+
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
|
167
|
+
# and can be added to the global gitignore or merged into this file. For a more nuclear
|
168
|
+
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
|
169
|
+
#.idea/
|
170
|
+
|
171
|
+
# Ruff stuff:
|
172
|
+
.ruff_cache/
|
173
|
+
|
174
|
+
# PyPI configuration file
|
175
|
+
.pypirc
|
176
|
+
secrets
|
177
|
+
|
178
|
+
prompt_suggestions
|
179
|
+
agent-working-dir
|
180
|
+
|
181
|
+
**/*.bak
|
182
|
+
|
183
|
+
storage
|
@@ -0,0 +1,10 @@
|
|
1
|
+
Metadata-Version: 2.4
|
2
|
+
Name: fastmcp-agents-library-agent-github
|
3
|
+
Version: 0.5.1
|
4
|
+
Summary: Agents for GitHub
|
5
|
+
Requires-Python: >=3.13
|
6
|
+
Requires-Dist: asyncstdlib>=3.13.1
|
7
|
+
Requires-Dist: fastmcp-agents-library-agent-simple-code
|
8
|
+
Requires-Dist: fastmcp-agents-library-mcp
|
9
|
+
Requires-Dist: fastmcp-ai-agent-bridge-pydantic-ai>=0.1.2
|
10
|
+
Requires-Dist: gitpython>=3.1.44
|
File without changes
|
File without changes
|
fastmcp_agents_library_agent_github-0.5.1/fastmcp_agents/library/agent/github/agents.py.disabled
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
"""
|
2
|
+
This agent is used to triage issues on a GitHub repository.
|
3
|
+
"""
|
4
|
+
|
5
|
+
from fastmcp_agents.core.models.server_builder import FastMCPAgents
|
6
|
+
from fastmcp_agents.library.agent.github.issue_triage import GithubIssueTriageAgent
|
7
|
+
from fastmcp_agents.library.agent.github.shared.mcp import github_mcp_server
|
8
|
+
|
9
|
+
mcp_servers = {
|
10
|
+
"github": github_mcp_server,
|
11
|
+
}
|
12
|
+
|
13
|
+
|
14
|
+
server = FastMCPAgents(
|
15
|
+
name="triage-github-issue",
|
16
|
+
mcp=mcp_servers,
|
17
|
+
agents=[GithubIssueTriageAgent()],
|
18
|
+
).to_server()
|
19
|
+
|
20
|
+
|
21
|
+
if __name__ == "__main__":
|
22
|
+
server.run(transport="sse")
|
fastmcp_agents_library_agent_github-0.5.1/fastmcp_agents/library/agent/github/gather_background.py
ADDED
@@ -0,0 +1,92 @@
|
|
1
|
+
"""
|
2
|
+
This agent is used to triage issues on a GitHub repository.
|
3
|
+
"""
|
4
|
+
|
5
|
+
import os
|
6
|
+
from typing import Any, Literal
|
7
|
+
|
8
|
+
from fastmcp import FastMCP
|
9
|
+
from fastmcp.tools.tool import Tool
|
10
|
+
from fastmcp_ai_agent_bridge.pydantic_ai import FastMCPToolset
|
11
|
+
from pydantic import BaseModel
|
12
|
+
from pydantic_ai import Agent
|
13
|
+
|
14
|
+
from fastmcp_agents.library.agent.github.shared.logging import configure_console_logging
|
15
|
+
from fastmcp_agents.library.mcp.github import repo_restricted_github_mcp
|
16
|
+
from fastmcp_agents.library.mcp.nickclyde import duckduckgo_mcp
|
17
|
+
|
18
|
+
configure_console_logging()
|
19
|
+
|
20
|
+
gather_background_instructions = """
|
21
|
+
## Persona & Goal:
|
22
|
+
You are a helpful assistant to an open source maintainer. You triage issues posted on a GitHub repository, looking
|
23
|
+
to connect them with previous issues posted, open or closed pull requests, and discussions.
|
24
|
+
|
25
|
+
{mindset_instructions}
|
26
|
+
{confidence_levels}
|
27
|
+
{section_guidelines}
|
28
|
+
|
29
|
+
You will perform multiple searches against the repository across issues, pull requests, and discussions to identify
|
30
|
+
and relevant information for the issue. If you find a relevant related item, you will review the comments or discussion
|
31
|
+
under that item to determine if it is related to the issue and how it might be related.
|
32
|
+
|
33
|
+
Your goal is to "connect the dots", and gather all related information to assist the maintainer in investigating the issue.
|
34
|
+
"""
|
35
|
+
|
36
|
+
|
37
|
+
def mcp_servers_factory(owner: str, repo: str) -> dict[str, Any]:
|
38
|
+
return {
|
39
|
+
"duckduckgo": duckduckgo_mcp(),
|
40
|
+
"github": repo_restricted_github_mcp(owner=owner, repo=repo, read_only=True),
|
41
|
+
}
|
42
|
+
|
43
|
+
|
44
|
+
def repo_restricted_toolset_factory(owner: str, repo: str) -> FastMCPToolset:
|
45
|
+
return FastMCPToolset.from_mcp_config(
|
46
|
+
mcp_config=mcp_servers_factory(
|
47
|
+
owner=owner,
|
48
|
+
repo=repo,
|
49
|
+
)
|
50
|
+
)
|
51
|
+
|
52
|
+
|
53
|
+
class GitHubRelatedIssue(BaseModel):
|
54
|
+
issue_title: str
|
55
|
+
issue_number: int
|
56
|
+
confidence: Literal["high", "medium", "low"]
|
57
|
+
reason: str
|
58
|
+
|
59
|
+
|
60
|
+
class GitHubIssueSummary(BaseModel):
|
61
|
+
issue_title: str
|
62
|
+
issue_number: int
|
63
|
+
detailed_summary: str
|
64
|
+
related_issues: list[GitHubRelatedIssue]
|
65
|
+
|
66
|
+
|
67
|
+
gather_background_agent = Agent[Any, GitHubIssueSummary](
|
68
|
+
model=os.environ.get("MODEL"),
|
69
|
+
system_prompt=gather_background_instructions,
|
70
|
+
output_type=GitHubIssueSummary,
|
71
|
+
)
|
72
|
+
|
73
|
+
server = FastMCP[None](name="gather-github-issue-background")
|
74
|
+
|
75
|
+
|
76
|
+
async def gather_background(owner: str, repo: str, issue_number: int) -> GitHubIssueSummary:
|
77
|
+
result = await gather_background_agent.run(
|
78
|
+
user_prompt=[
|
79
|
+
f"The issue number to gather background information for is {issue_number}.",
|
80
|
+
],
|
81
|
+
toolsets=[repo_restricted_toolset_factory(owner=owner, repo=repo)],
|
82
|
+
)
|
83
|
+
|
84
|
+
return result.output
|
85
|
+
|
86
|
+
|
87
|
+
gather_background_tool = Tool.from_function(fn=gather_background)
|
88
|
+
|
89
|
+
server.add_tool(tool=gather_background_tool)
|
90
|
+
|
91
|
+
if __name__ == "__main__":
|
92
|
+
server.run(transport="sse")
|
fastmcp_agents_library_agent_github-0.5.1/fastmcp_agents/library/agent/github/investigate_issue.py
ADDED
@@ -0,0 +1,67 @@
|
|
1
|
+
from pathlib import Path
|
2
|
+
|
3
|
+
from fastmcp import FastMCP
|
4
|
+
from fastmcp.tools.tool import Tool
|
5
|
+
from fastmcp_ai_agent_bridge.pydantic_ai import FastMCPToolset
|
6
|
+
from pydantic import AnyHttpUrl
|
7
|
+
|
8
|
+
from fastmcp_agents.library.agent.github.gather_background import GitHubIssueSummary, gather_background, gather_background_tool
|
9
|
+
from fastmcp_agents.library.agent.github.respond_to_issue import respond_to_issue
|
10
|
+
from fastmcp_agents.library.agent.simple_code.investigate import InvestigationResponse, investigate_code, investigate_code_tool
|
11
|
+
|
12
|
+
fastmcp = FastMCP[None](name="github-issue-triage")
|
13
|
+
|
14
|
+
fastmcp.add_tool(gather_background_tool)
|
15
|
+
fastmcp.add_tool(investigate_code_tool)
|
16
|
+
|
17
|
+
toolset = FastMCPToolset(fastmcp=fastmcp)
|
18
|
+
|
19
|
+
|
20
|
+
async def investigate_github_issue(
|
21
|
+
*,
|
22
|
+
owner: str,
|
23
|
+
repo: str,
|
24
|
+
reply_to_owner: str | None = None,
|
25
|
+
reply_to_repo: str | None = None,
|
26
|
+
reply_to_issue_number: int | None = None,
|
27
|
+
code_path: Path | None = None,
|
28
|
+
issue_number: int,
|
29
|
+
reply_to_issue: bool,
|
30
|
+
) -> tuple[GitHubIssueSummary, InvestigationResponse, str | None]:
|
31
|
+
issue_summary: GitHubIssueSummary = await gather_background(owner=owner, repo=repo, issue_number=issue_number)
|
32
|
+
|
33
|
+
code_repository: Path | AnyHttpUrl
|
34
|
+
|
35
|
+
if code_path:
|
36
|
+
code_repository = code_path
|
37
|
+
elif owner and repo:
|
38
|
+
code_repository = AnyHttpUrl(f"https://github.com/{owner}/{repo}.git")
|
39
|
+
else:
|
40
|
+
msg = "Either code_path or owner and repo must be provided"
|
41
|
+
raise ValueError(msg)
|
42
|
+
|
43
|
+
investigation_response: InvestigationResponse = await investigate_code(
|
44
|
+
task=issue_summary.detailed_summary,
|
45
|
+
code_repository=code_repository,
|
46
|
+
)
|
47
|
+
|
48
|
+
if not reply_to_issue:
|
49
|
+
return issue_summary, investigation_response, None
|
50
|
+
|
51
|
+
response = await respond_to_issue(
|
52
|
+
owner=reply_to_owner or owner,
|
53
|
+
repo=reply_to_repo or repo,
|
54
|
+
issue_number=reply_to_issue_number or issue_number,
|
55
|
+
issue_summary=issue_summary,
|
56
|
+
investigation=investigation_response,
|
57
|
+
)
|
58
|
+
|
59
|
+
return issue_summary, investigation_response, response
|
60
|
+
|
61
|
+
|
62
|
+
investigate_github_issue_tool = Tool.from_function(fn=investigate_github_issue)
|
63
|
+
|
64
|
+
fastmcp.add_tool(tool=investigate_github_issue_tool)
|
65
|
+
|
66
|
+
if __name__ == "__main__":
|
67
|
+
fastmcp.run(transport="sse")
|
@@ -0,0 +1,191 @@
|
|
1
|
+
"""
|
2
|
+
This agent is used to triage issues on a GitHub repository.
|
3
|
+
"""
|
4
|
+
|
5
|
+
from fastmcp import Context
|
6
|
+
from fastmcp.client.client import CallToolResult, Client
|
7
|
+
from fastmcp.client.transports import FastMCPTransport
|
8
|
+
|
9
|
+
from fastmcp_agents.core.agents.task import TaskAgent
|
10
|
+
from fastmcp_agents.core.models.server_builder import FastMCPAgents
|
11
|
+
from fastmcp_agents.library.agent.github.shared.git import quick_clone_github_repo
|
12
|
+
from fastmcp_agents.library.agent.github.shared.helpers import generate_background, get_issue_background
|
13
|
+
from fastmcp_agents.library.agent.github.shared.mcp import github_mcp_server
|
14
|
+
from fastmcp_agents.library.agent.simple_code.investigate import server_builder as code_investigate_server_builder
|
15
|
+
|
16
|
+
issue_triage_instructions = """
|
17
|
+
## Persona & Goal:
|
18
|
+
You are an expert software investigator and debugger. Your primary mission is to perform a deep, methodical analysis of issues by
|
19
|
+
understanding the exact code paths, conditions, and edge cases that lead to the bug.
|
20
|
+
|
21
|
+
{mindset_instructions}
|
22
|
+
{confidence_levels}
|
23
|
+
{section_guidelines}
|
24
|
+
|
25
|
+
## Analysis Process:
|
26
|
+
1. Initial Code Investigation
|
27
|
+
- Use the code agent to examine the relevant code files
|
28
|
+
- Look for the specific code mentioned in the issue
|
29
|
+
- Identify related code paths and dependencies
|
30
|
+
- Document the exact code structure and patterns
|
31
|
+
- Document your thought process as you examine the code
|
32
|
+
|
33
|
+
2. Behavior Hypothesis
|
34
|
+
- Formulate hypotheses about the intended behavior
|
35
|
+
- Consider mathematical correctness (e.g., division by zero is undefined)
|
36
|
+
- Consider user experience expectations
|
37
|
+
- Consider programming language conventions
|
38
|
+
- Document your reasoning for each hypothesis
|
39
|
+
- Evaluate each hypothesis against the code
|
40
|
+
|
41
|
+
3. Issue Context Analysis
|
42
|
+
- Extract key terms and patterns from the code investigation
|
43
|
+
- Use these terms to search for related issues
|
44
|
+
- Look for similar error handling patterns
|
45
|
+
- Identify broader patterns in the codebase
|
46
|
+
- Document how these findings affect your hypotheses
|
47
|
+
|
48
|
+
4. Test Analysis
|
49
|
+
- Examine test files to understand current error handling
|
50
|
+
- Look for similar test cases
|
51
|
+
- Identify gaps in test coverage
|
52
|
+
- Document existing error handling patterns
|
53
|
+
- Evaluate if tests align with your hypotheses
|
54
|
+
|
55
|
+
5. Related Issues Search
|
56
|
+
- Use specific terms from code investigation
|
57
|
+
- Search for similar error patterns
|
58
|
+
- Look for related documentation issues
|
59
|
+
- Consider feature requests that might be impacted
|
60
|
+
- Document any found relationships with confidence levels
|
61
|
+
- Use expandable sections for low confidence relationships
|
62
|
+
- Consider how related issues affect your hypotheses
|
63
|
+
- List recent issues in the repository to catch related issues that might not match search terms
|
64
|
+
- Review issue titles and bodies for semantic relationships
|
65
|
+
- Look for patterns in how issues are reported and handled
|
66
|
+
- Consider both open and recently closed issues for context
|
67
|
+
|
68
|
+
**Desired Outcome:** Produce a comprehensive analysis posted as a comment on GitHub issue `$github_issue_number`. This analysis must:
|
69
|
+
* Begin with a clear code investigation summary
|
70
|
+
* Present your hypotheses about the intended behavior
|
71
|
+
* Document your thought process and reasoning
|
72
|
+
* Explain why you believe each hypothesis is correct or incorrect
|
73
|
+
* Document exact conditions that trigger the behavior
|
74
|
+
* Include a proposed test case that verifies the correct behavior
|
75
|
+
* Explain why the behavior occurs at a code level
|
76
|
+
* Reference specific lines of code and their behavior
|
77
|
+
* Include a section on related issues found during the search
|
78
|
+
* Use specific terms from the code investigation in the analysis
|
79
|
+
* Conclude with a clear recommendation about whether this is a bug or expected behavior
|
80
|
+
"""
|
81
|
+
|
82
|
+
|
83
|
+
mcp_servers = {
|
84
|
+
"github": github_mcp_server,
|
85
|
+
}
|
86
|
+
|
87
|
+
|
88
|
+
def triage_issue(
|
89
|
+
issue_repository_owner: str,
|
90
|
+
issue_repository: str,
|
91
|
+
issue_number: int,
|
92
|
+
code_repository_owner: str | None = None,
|
93
|
+
code_repository: str | None = None,
|
94
|
+
):
|
95
|
+
"""Triage a GitHub issue."""
|
96
|
+
|
97
|
+
if code_repository_owner is None or code_repository is None:
|
98
|
+
code_repository_owner = issue_repository_owner
|
99
|
+
code_repository = issue_repository
|
100
|
+
|
101
|
+
|
102
|
+
class GithubIssueTriageAgent(TaskAgent):
|
103
|
+
"""An agent that can triage GitHub issues."""
|
104
|
+
|
105
|
+
name: str = "triage_github_issue"
|
106
|
+
|
107
|
+
instructions: str | None = issue_triage_instructions
|
108
|
+
|
109
|
+
async def __call__(
|
110
|
+
self,
|
111
|
+
ctx: Context,
|
112
|
+
issue_repository_owner: str,
|
113
|
+
issue_repository: str,
|
114
|
+
issue_number: int,
|
115
|
+
code_repository_owner: str | None = None,
|
116
|
+
code_repository: str | None = None,
|
117
|
+
):
|
118
|
+
"""Triage a GitHub issue."""
|
119
|
+
|
120
|
+
if code_repository_owner is None or code_repository is None:
|
121
|
+
code_repository_owner = issue_repository_owner
|
122
|
+
code_repository = issue_repository
|
123
|
+
|
124
|
+
issue_content, comments_content, related_issues_content = await get_issue_background(
|
125
|
+
ctx,
|
126
|
+
issue_repository_owner,
|
127
|
+
issue_repository,
|
128
|
+
issue_number,
|
129
|
+
)
|
130
|
+
|
131
|
+
background: str = generate_background(issue_content, comments_content)
|
132
|
+
|
133
|
+
code_agent_task = """
|
134
|
+
We are investigating the code base for the repository {code_repository_owner}/{code_repository}.
|
135
|
+
|
136
|
+
{background}
|
137
|
+
|
138
|
+
The codebase has been cloned to the following path: `{repo_path}`
|
139
|
+
|
140
|
+
Please investigate the codebase to understand the issue and formulate a detailed analysis of the issue.
|
141
|
+
|
142
|
+
Please provide your analysis as a single markdown block.
|
143
|
+
"""
|
144
|
+
|
145
|
+
with quick_clone_github_repo(code_repository_owner, code_repository) as repo_path:
|
146
|
+
code_agent_server = code_investigate_server_builder(root_directory=repo_path)
|
147
|
+
code_agent_client = Client[FastMCPTransport](transport=code_agent_server)
|
148
|
+
|
149
|
+
async with code_agent_client:
|
150
|
+
code_agent_response: CallToolResult = await code_agent_client.call_tool(
|
151
|
+
"ask_issue_background",
|
152
|
+
arguments={
|
153
|
+
"task": code_agent_task.format(
|
154
|
+
code_repository_owner=code_repository_owner,
|
155
|
+
code_repository=code_repository,
|
156
|
+
background=background,
|
157
|
+
repo_path=repo_path,
|
158
|
+
),
|
159
|
+
},
|
160
|
+
)
|
161
|
+
|
162
|
+
github_agent_task = """
|
163
|
+
Background:
|
164
|
+
```markdown
|
165
|
+
{background}
|
166
|
+
```
|
167
|
+
|
168
|
+
We've identified the following potential related issues:
|
169
|
+
```markdown
|
170
|
+
{github_related_issues}
|
171
|
+
```
|
172
|
+
|
173
|
+
We have dispatched a code agent to investigate the codebase related to the issue. They have provided the following analysis:
|
174
|
+
|
175
|
+
```markdown
|
176
|
+
{code_agent_response}
|
177
|
+
```
|
178
|
+
|
179
|
+
Please formulate a detailed response that can be posted as a comment on the issue.
|
180
|
+
"""
|
181
|
+
|
182
|
+
|
183
|
+
server = FastMCPAgents(
|
184
|
+
name="triage-github-issue",
|
185
|
+
mcp=mcp_servers,
|
186
|
+
agents=[GithubIssueTriageAgent()],
|
187
|
+
).to_server()
|
188
|
+
|
189
|
+
|
190
|
+
if __name__ == "__main__":
|
191
|
+
server.run(transport="sse")
|
fastmcp_agents_library_agent_github-0.5.1/fastmcp_agents/library/agent/github/respond_to_issue.py
ADDED
@@ -0,0 +1,98 @@
|
|
1
|
+
"""
|
2
|
+
This agent is used to triage issues on a GitHub repository.
|
3
|
+
"""
|
4
|
+
|
5
|
+
import os
|
6
|
+
from textwrap import dedent
|
7
|
+
from typing import Any
|
8
|
+
|
9
|
+
import yaml
|
10
|
+
from fastmcp import FastMCP
|
11
|
+
from fastmcp_ai_agent_bridge.pydantic_ai import FastMCPToolset
|
12
|
+
from pydantic_ai import Agent
|
13
|
+
|
14
|
+
from fastmcp_agents.library.agent.github.gather_background import GitHubIssueSummary
|
15
|
+
from fastmcp_agents.library.agent.github.shared.logging import configure_console_logging
|
16
|
+
from fastmcp_agents.library.agent.simple_code.investigate import InvestigationResponse
|
17
|
+
from fastmcp_agents.library.mcp.github import repo_restricted_github_mcp
|
18
|
+
|
19
|
+
configure_console_logging()
|
20
|
+
|
21
|
+
respond_instructions = """
|
22
|
+
## Persona & Goal:
|
23
|
+
You are a helpful assistant to an open source maintainer. You receive a bunch of background about an issue
|
24
|
+
and a triage performed by another maintainer and you reponse on the issue.
|
25
|
+
|
26
|
+
{mindset_instructions}
|
27
|
+
{confidence_levels}
|
28
|
+
{section_guidelines}
|
29
|
+
|
30
|
+
Your goal is to reflect all of the details of the triage and background while producing a nicely formatted markdown response
|
31
|
+
in the GitHub comment. When you complete the task and report success include the body of the reponse of the comment.
|
32
|
+
"""
|
33
|
+
|
34
|
+
|
35
|
+
def mcp_servers_factory(owner: str, repo: str) -> dict[str, Any]:
|
36
|
+
return {
|
37
|
+
"github": repo_restricted_github_mcp(
|
38
|
+
owner=owner,
|
39
|
+
repo=repo,
|
40
|
+
issues=True,
|
41
|
+
pull_requests=False,
|
42
|
+
discussions=False,
|
43
|
+
repository=False,
|
44
|
+
),
|
45
|
+
}
|
46
|
+
|
47
|
+
|
48
|
+
def repo_restricted_toolset_factory(owner: str, repo: str) -> FastMCPToolset:
|
49
|
+
return FastMCPToolset.from_mcp_config(
|
50
|
+
mcp_config=mcp_servers_factory(
|
51
|
+
owner=owner,
|
52
|
+
repo=repo,
|
53
|
+
)
|
54
|
+
)
|
55
|
+
|
56
|
+
|
57
|
+
issue_response_agent = Agent[Any, str](
|
58
|
+
model=os.environ.get("MODEL"),
|
59
|
+
system_prompt=respond_instructions,
|
60
|
+
output_type=str,
|
61
|
+
)
|
62
|
+
|
63
|
+
server = FastMCP[None](name="respond-to-github-issue-background")
|
64
|
+
|
65
|
+
|
66
|
+
async def respond_to_issue(
|
67
|
+
owner: str,
|
68
|
+
repo: str,
|
69
|
+
issue_number: int,
|
70
|
+
issue_summary: GitHubIssueSummary,
|
71
|
+
investigation: InvestigationResponse | None = None,
|
72
|
+
) -> str:
|
73
|
+
background_yaml = yaml.safe_dump(issue_summary.model_dump())
|
74
|
+
investigation_yaml = yaml.safe_dump(data=investigation.model_dump()) if investigation else None
|
75
|
+
|
76
|
+
task = dedent(
|
77
|
+
text=f"""The issue number to reply to is {issue_number}.
|
78
|
+
|
79
|
+
The background information is as follows:
|
80
|
+
{background_yaml}
|
81
|
+
"""
|
82
|
+
).strip()
|
83
|
+
|
84
|
+
if investigation:
|
85
|
+
task += dedent(
|
86
|
+
text=f"""
|
87
|
+
The investigation response is as follows:
|
88
|
+
{investigation_yaml}
|
89
|
+
"""
|
90
|
+
).strip()
|
91
|
+
|
92
|
+
run_result = await issue_response_agent.run(
|
93
|
+
user_prompt=task, toolsets=[repo_restricted_toolset_factory(owner=owner, repo=repo)], output_type=str
|
94
|
+
)
|
95
|
+
|
96
|
+
print(run_result.output)
|
97
|
+
|
98
|
+
return run_result.output
|
fastmcp_agents_library_agent_github-0.5.1/fastmcp_agents/library/agent/github/shared/helpers.py
ADDED
@@ -0,0 +1,213 @@
|
|
1
|
+
from typing import Annotated, Literal
|
2
|
+
|
3
|
+
from fastmcp import Context
|
4
|
+
from fastmcp.tools import FunctionTool
|
5
|
+
from fastmcp.tools.tool import ToolResult
|
6
|
+
from fastmcp.tools.tool_transform import ArgTransform, TransformedTool
|
7
|
+
|
8
|
+
from fastmcp_agents.core.agents.base import FastMCPTool
|
9
|
+
from fastmcp_agents.core.utilities.mcp import get_text_from_tool_result
|
10
|
+
|
11
|
+
# async def get_issue(ctx: Context, owner: str, repo: str, issue_number: int) -> ToolResult:
|
12
|
+
# github_issue: ToolResult = await ctx.fastmcp._call_tool(
|
13
|
+
# "get_issue",
|
14
|
+
# arguments={
|
15
|
+
# "owner": owner,
|
16
|
+
# "repo": repo,
|
17
|
+
# "issue_number": issue_number,
|
18
|
+
# },
|
19
|
+
# )
|
20
|
+
|
21
|
+
# return github_issue
|
22
|
+
|
23
|
+
|
24
|
+
# async def get_issue_comments(ctx: Context, owner: str, repo: str, issue_number: int) -> ToolResult:
|
25
|
+
# github_issue_comments: ToolResult = await ctx.fastmcp._call_tool(
|
26
|
+
# "get_issue_comments",
|
27
|
+
# arguments={
|
28
|
+
# "owner": owner,
|
29
|
+
# "repo": repo,
|
30
|
+
# "per_page": 100,
|
31
|
+
# "issue_number": issue_number,
|
32
|
+
# },
|
33
|
+
# )
|
34
|
+
|
35
|
+
# return github_issue_comments
|
36
|
+
|
37
|
+
|
38
|
+
# class RelatedKeywords(BaseModel):
|
39
|
+
# keywords: list[str]
|
40
|
+
|
41
|
+
|
42
|
+
# class RelatedIssues(BaseModel):
|
43
|
+
# issue_numbers: list[int]
|
44
|
+
|
45
|
+
|
46
|
+
# async def related_issues_completion(ctx: Context, owner: str, repo: str, background: str) -> list[ContentBlock]:
|
47
|
+
# def provide_keywords(keywords: list[str]) -> RelatedKeywords:
|
48
|
+
# return RelatedKeywords(keywords=keywords)
|
49
|
+
|
50
|
+
# _, _, pending_tool_call = await ctx.completions.tool(
|
51
|
+
# system_prompt="""Review the background information for the following GitHub issue and provide a list of important
|
52
|
+
# keywords that can be used to search for related issues. Provide no more than 5 keywords.""",
|
53
|
+
# messages=background,
|
54
|
+
# tools=[FunctionTool.from_function(fn=provide_keywords)],
|
55
|
+
# )
|
56
|
+
|
57
|
+
# _, result = await pending_tool_call.run()
|
58
|
+
|
59
|
+
# if isinstance(result, ToolError):
|
60
|
+
# raise result
|
61
|
+
|
62
|
+
# keywords: list[str] = result.structured_content.get("keywords", []) if result.structured_content else []
|
63
|
+
|
64
|
+
# related_issues_search: ToolResult = await search_issues(ctx, owner, repo, keywords)
|
65
|
+
|
66
|
+
# def provide_issue_numbers(issue_numbers: list[int]) -> RelatedIssues:
|
67
|
+
# return RelatedIssues(issue_numbers=issue_numbers)
|
68
|
+
|
69
|
+
# _, _, pending_tool_call = await ctx.completions.tool(
|
70
|
+
# system_prompt="""
|
71
|
+
# Review the background information for the following GitHub issue and the provided list of search results and provide a
|
72
|
+
# list of likely related issue numbers from the search results.",
|
73
|
+
# """,
|
74
|
+
# messages=[
|
75
|
+
# background,
|
76
|
+
# str(related_issues_search.structured_content),
|
77
|
+
# ],
|
78
|
+
# tools=[FunctionTool.from_function(fn=provide_issue_numbers)],
|
79
|
+
# )
|
80
|
+
|
81
|
+
# _, result = await pending_tool_call.run()
|
82
|
+
|
83
|
+
# if isinstance(result, ToolError):
|
84
|
+
# raise result
|
85
|
+
|
86
|
+
# issue_numbers: list[int] = result.structured_content.get("issue_numbers", []) if result.structured_content else []
|
87
|
+
|
88
|
+
# related_issues: list[ToolResult] = [await get_issue(ctx, owner, repo, issue_number) for issue_number in issue_numbers]
|
89
|
+
|
90
|
+
# issues: list[ContentBlock] = []
|
91
|
+
|
92
|
+
# for related_issue in related_issues:
|
93
|
+
# issues.extend(related_issue.content)
|
94
|
+
|
95
|
+
# return issues
|
96
|
+
|
97
|
+
|
98
|
+
async def search_issues(
|
99
|
+
ctx: Context,
|
100
|
+
owner: str,
|
101
|
+
repo: str,
|
102
|
+
keywords: Annotated[list[str], "The keywords to search for. Maximum of 5 keywords."],
|
103
|
+
state: Literal["all", "open", "closed"] = "all",
|
104
|
+
) -> ToolResult:
|
105
|
+
"""Search for issues by keyword in a GitHub repo. If more than 5 keywords are provided, only the first 5 will be used."""
|
106
|
+
|
107
|
+
search_query_parts: list[str] = ["is:issue", f"repo:{owner}/{repo}"]
|
108
|
+
|
109
|
+
if state == "open":
|
110
|
+
search_query_parts.append("state:open")
|
111
|
+
elif state == "closed":
|
112
|
+
search_query_parts.append("state:closed")
|
113
|
+
|
114
|
+
search_query_parts.append(" OR ".join(keywords[:5]))
|
115
|
+
|
116
|
+
github_issues: ToolResult = await ctx.fastmcp._call_tool(
|
117
|
+
"search_issues",
|
118
|
+
arguments={
|
119
|
+
"q": " ".join(search_query_parts),
|
120
|
+
},
|
121
|
+
)
|
122
|
+
|
123
|
+
return github_issues
|
124
|
+
|
125
|
+
|
126
|
+
search_issues_tool = FunctionTool.from_function(fn=search_issues)
|
127
|
+
|
128
|
+
|
129
|
+
async def get_issue_background(
|
130
|
+
tools: dict[str, FastMCPTool],
|
131
|
+
owner: str,
|
132
|
+
repo: str,
|
133
|
+
issue_number: int,
|
134
|
+
) -> tuple[str, str]:
|
135
|
+
get_issue_tool: FastMCPTool | None = tools.get("get_issue")
|
136
|
+
|
137
|
+
if get_issue_tool is None:
|
138
|
+
msg = "get_issue tool not found"
|
139
|
+
raise ValueError(msg)
|
140
|
+
|
141
|
+
get_issue_comments_tool: FastMCPTool | None = tools.get("get_issue_comments")
|
142
|
+
|
143
|
+
if get_issue_comments_tool is None:
|
144
|
+
msg = "get_issue_comments tool not found"
|
145
|
+
raise ValueError(msg)
|
146
|
+
|
147
|
+
issue = await get_issue_tool.run(arguments={"owner": owner, "repo": repo, "issue_number": issue_number})
|
148
|
+
|
149
|
+
issue_content: str = get_text_from_tool_result(issue)
|
150
|
+
|
151
|
+
comments = await get_issue_comments_tool.run(arguments={"owner": owner, "repo": repo, "issue_number": issue_number})
|
152
|
+
|
153
|
+
comments_content: str = get_text_from_tool_result(comments)
|
154
|
+
|
155
|
+
return issue_content, comments_content
|
156
|
+
|
157
|
+
|
158
|
+
def owner_repo_args_transform_factory(owner: str, repo: str) -> dict[str, ArgTransform]:
|
159
|
+
return {
|
160
|
+
"owner": ArgTransform(default=owner, hide=True),
|
161
|
+
"repo": ArgTransform(default=repo, hide=True),
|
162
|
+
}
|
163
|
+
|
164
|
+
|
165
|
+
async def restricted_get_issue_tool_factory(tools: dict[str, FastMCPTool], owner: str, repo: str) -> TransformedTool:
|
166
|
+
if get_issue_tool := tools.get("get_issue"):
|
167
|
+
return TransformedTool.from_tool(
|
168
|
+
get_issue_tool,
|
169
|
+
transform_args=owner_repo_args_transform_factory(owner, repo),
|
170
|
+
)
|
171
|
+
|
172
|
+
msg = "get_issue tool not found"
|
173
|
+
raise ValueError(msg)
|
174
|
+
|
175
|
+
|
176
|
+
async def restricted_get_pull_request_tool_factory(tools: dict[str, FastMCPTool], owner: str, repo: str) -> TransformedTool:
|
177
|
+
if get_pull_request_tool := tools.get("get_pull_request"):
|
178
|
+
return TransformedTool.from_tool(
|
179
|
+
get_pull_request_tool,
|
180
|
+
transform_args=owner_repo_args_transform_factory(owner, repo),
|
181
|
+
)
|
182
|
+
|
183
|
+
msg = "get_pull_request tool not found"
|
184
|
+
raise ValueError(msg)
|
185
|
+
|
186
|
+
|
187
|
+
async def restricted_search_issues_tool_factory(owner: str, repo: str) -> TransformedTool:
|
188
|
+
return TransformedTool.from_tool(
|
189
|
+
search_issues_tool,
|
190
|
+
transform_args=owner_repo_args_transform_factory(owner, repo),
|
191
|
+
)
|
192
|
+
|
193
|
+
|
194
|
+
def generate_background(issue_content: str, comments_content: str, extra: str | None = None) -> str:
|
195
|
+
"""Generate a background for an issue."""
|
196
|
+
background: str = f"""
|
197
|
+
An issue has been reported in the repo.
|
198
|
+
|
199
|
+
```markdown
|
200
|
+
{issue_content}
|
201
|
+
```
|
202
|
+
|
203
|
+
There may be some discussion in the issue comments.
|
204
|
+
|
205
|
+
```markdown
|
206
|
+
{comments_content}
|
207
|
+
```
|
208
|
+
"""
|
209
|
+
|
210
|
+
if extra:
|
211
|
+
background += f"\n\n{extra}"
|
212
|
+
|
213
|
+
return background
|
fastmcp_agents_library_agent_github-0.5.1/fastmcp_agents/library/agent/github/shared/logging.py
ADDED
@@ -0,0 +1,113 @@
|
|
1
|
+
import json
|
2
|
+
from datetime import UTC, datetime
|
3
|
+
|
4
|
+
import logfire
|
5
|
+
from logfire import ConsoleOptions
|
6
|
+
from opentelemetry.sdk.trace import ReadableSpan
|
7
|
+
from opentelemetry.sdk.trace.export import ConsoleSpanExporter, SimpleSpanProcessor
|
8
|
+
from pydantic_ai import Agent
|
9
|
+
|
10
|
+
|
11
|
+
def get_tool_names_from_span(span: ReadableSpan) -> list[str]:
|
12
|
+
if not span.attributes:
|
13
|
+
return []
|
14
|
+
|
15
|
+
if not (model_request_parameters := span.attributes.get("model_request_parameters")):
|
16
|
+
return []
|
17
|
+
|
18
|
+
if not isinstance(model_request_parameters, str):
|
19
|
+
return []
|
20
|
+
|
21
|
+
deserialized_model_request_parameters = json.loads(model_request_parameters)
|
22
|
+
|
23
|
+
if not (function_tools := deserialized_model_request_parameters.get("function_tools")):
|
24
|
+
return []
|
25
|
+
|
26
|
+
return [tool["name"] for tool in function_tools]
|
27
|
+
|
28
|
+
|
29
|
+
def get_picked_tools_from_span(span: ReadableSpan) -> list[str]:
|
30
|
+
if not span.attributes:
|
31
|
+
return []
|
32
|
+
|
33
|
+
if not (events := span.attributes.get("events")):
|
34
|
+
return []
|
35
|
+
|
36
|
+
if not isinstance(events, str):
|
37
|
+
return []
|
38
|
+
|
39
|
+
deserialized_events = json.loads(events)
|
40
|
+
|
41
|
+
assistant_event = deserialized_events[-1]
|
42
|
+
|
43
|
+
if not (message := assistant_event.get("message")):
|
44
|
+
return []
|
45
|
+
|
46
|
+
if not (tool_calls := message.get("tool_calls")):
|
47
|
+
return []
|
48
|
+
|
49
|
+
return [
|
50
|
+
tool_call.get(
|
51
|
+
"function",
|
52
|
+
{},
|
53
|
+
).get(
|
54
|
+
"name",
|
55
|
+
"<unknown>",
|
56
|
+
)
|
57
|
+
for tool_call in tool_calls
|
58
|
+
if tool_call.get("type") == "function"
|
59
|
+
]
|
60
|
+
|
61
|
+
|
62
|
+
ADDT_FORMAT_SPAN_NAMES = {"running tool"}
|
63
|
+
|
64
|
+
|
65
|
+
def format_span(span: ReadableSpan) -> str:
|
66
|
+
timestamp: datetime | None = datetime.fromtimestamp(span.start_time / 1_000_000_000, tz=UTC) if span.start_time else None
|
67
|
+
|
68
|
+
span_message = span.name
|
69
|
+
|
70
|
+
message = "{timestamp} - {span_message}\n"
|
71
|
+
default_message = message.format(timestamp=timestamp, span_message=span_message)
|
72
|
+
|
73
|
+
if not span.attributes:
|
74
|
+
return default_message
|
75
|
+
|
76
|
+
if not span.name.startswith("chat ") and span.name not in ADDT_FORMAT_SPAN_NAMES:
|
77
|
+
return default_message
|
78
|
+
|
79
|
+
match span.name:
|
80
|
+
case "running tool":
|
81
|
+
model_name: str | None = str(span.attributes.get("gen_ai.request.model"))
|
82
|
+
tool_name: str | None = str(span.attributes.get("gen_ai.tool.name"))
|
83
|
+
tool_arguments: str | None = str(span.attributes.get("tool_arguments"))
|
84
|
+
tool_response: str | None = str(span.attributes.get("tool_response"))
|
85
|
+
|
86
|
+
span_message = f"Model called {tool_name} with arguments: {tool_arguments} returned: {tool_response[:200]}"
|
87
|
+
|
88
|
+
case _ if span.name.startswith("chat "):
|
89
|
+
model_name: str | None = str(span.attributes.get("gen_ai.request.model"))
|
90
|
+
# tool_names: list[str] = get_tool_names_from_span(span)
|
91
|
+
picked_tools: list[str] = get_picked_tools_from_span(span)
|
92
|
+
span_message = f"Model: {model_name} -- Picked tools: {picked_tools}"
|
93
|
+
|
94
|
+
case _:
|
95
|
+
span_message = span.name
|
96
|
+
|
97
|
+
return message.format(timestamp=timestamp, span_message=span_message)
|
98
|
+
|
99
|
+
|
100
|
+
def configure_console_logging():
|
101
|
+
Agent.instrument_all()
|
102
|
+
|
103
|
+
_ = logfire.configure(
|
104
|
+
send_to_logfire=False,
|
105
|
+
console=ConsoleOptions(),
|
106
|
+
additional_span_processors=[
|
107
|
+
SimpleSpanProcessor(
|
108
|
+
span_exporter=ConsoleSpanExporter(
|
109
|
+
formatter=format_span,
|
110
|
+
)
|
111
|
+
)
|
112
|
+
],
|
113
|
+
)
|
@@ -0,0 +1,16 @@
|
|
1
|
+
import os
|
2
|
+
|
3
|
+
github_mcp_server = {
|
4
|
+
"command": "docker",
|
5
|
+
"args": [
|
6
|
+
"run",
|
7
|
+
"-i",
|
8
|
+
"--rm",
|
9
|
+
"-e",
|
10
|
+
"GITHUB_PERSONAL_ACCESS_TOKEN",
|
11
|
+
"ghcr.io/github/github-mcp-server",
|
12
|
+
],
|
13
|
+
"env": {
|
14
|
+
"GITHUB_PERSONAL_ACCESS_TOKEN": os.getenv("GITHUB_PERSONAL_ACCESS_TOKEN"),
|
15
|
+
},
|
16
|
+
}
|
fastmcp_agents_library_agent_github-0.5.1/fastmcp_agents/library/agent/github/shared/prompts.py
ADDED
@@ -0,0 +1,38 @@
|
|
1
|
+
confidence_levels = """
|
2
|
+
## Confidence Levels and Presentation:
|
3
|
+
* High Confidence (90-100%):
|
4
|
+
- Present findings directly in the main response
|
5
|
+
- Provide clear evidence and explanations
|
6
|
+
- Include specific code references
|
7
|
+
* Medium Confidence (50-89%):
|
8
|
+
- Present findings in the main response
|
9
|
+
- Clearly state confidence level
|
10
|
+
- Explain why you're not completely certain
|
11
|
+
* Low Confidence (0-49%):
|
12
|
+
- Hide findings in an expandable section using GitHub's details/summary syntax:
|
13
|
+
```markdown
|
14
|
+
<details>
|
15
|
+
<summary>Low Confidence Findings (Click to expand)</summary>
|
16
|
+
|
17
|
+
[Your low confidence findings here]
|
18
|
+
</details>
|
19
|
+
```
|
20
|
+
- Explain why confidence is low
|
21
|
+
- Suggest what additional information would increase confidence
|
22
|
+
"""
|
23
|
+
|
24
|
+
mindset_instructions = """
|
25
|
+
## Mindset: Approach each task with:
|
26
|
+
* Accuracy - ensure findings are truly relevant
|
27
|
+
* Clarity - present findings in a clear, organized manner
|
28
|
+
* Honesty - be explicit about confidence levels and hide low confidence findings in expandable sections
|
29
|
+
"""
|
30
|
+
|
31
|
+
section_guidelines = """
|
32
|
+
## Section Guidelines:
|
33
|
+
* Only include sections that are relevant to the current task
|
34
|
+
* Skip sections where you have no findings or insights to share
|
35
|
+
* If a section would be empty, omit it entirely rather than including it with no content
|
36
|
+
* Focus on quality over quantity - better to have fewer, well-analyzed sections than many empty ones
|
37
|
+
* If you're unsure whether a section is relevant, err on the side of omitting it
|
38
|
+
"""
|
@@ -0,0 +1,54 @@
|
|
1
|
+
[project]
|
2
|
+
name = "fastmcp-agents-library-agent-github"
|
3
|
+
version = "0.5.1"
|
4
|
+
description = "Agents for GitHub"
|
5
|
+
readme = "README.md"
|
6
|
+
requires-python = ">=3.13"
|
7
|
+
|
8
|
+
dependencies = [
|
9
|
+
"asyncstdlib>=3.13.1",
|
10
|
+
"fastmcp-agents-library-agent-simple-code",
|
11
|
+
"fastmcp-agents-library-mcp",
|
12
|
+
"fastmcp-ai-agent-bridge-pydantic-ai>=0.1.2",
|
13
|
+
"gitpython>=3.1.44",
|
14
|
+
]
|
15
|
+
|
16
|
+
[project.scripts]
|
17
|
+
github-issue-triage = "fastmcp_agents.library.agent.github.issue_triage:main"
|
18
|
+
github-gather-background = "fastmcp_agents.library.agent.github.gather_background:main"
|
19
|
+
|
20
|
+
[dependency-groups]
|
21
|
+
dev = [
|
22
|
+
"pytest-asyncio>=1.1.0",
|
23
|
+
"pytest-dotenv",
|
24
|
+
"ruff>=0.12.4",
|
25
|
+
]
|
26
|
+
|
27
|
+
[tool.uv.sources]
|
28
|
+
fastmcp-agents-library-agent-simple-code = { workspace = true }
|
29
|
+
fastmcp-agents-library-mcp = { workspace = true }
|
30
|
+
|
31
|
+
[build-system]
|
32
|
+
requires = ["hatchling"]
|
33
|
+
build-backend = "hatchling.build"
|
34
|
+
|
35
|
+
[tool.pytest.ini_options]
|
36
|
+
markers = [
|
37
|
+
"skip_on_ci: Skip running the test when running on CI",
|
38
|
+
]
|
39
|
+
include =["./tests"]
|
40
|
+
ignore = ["./playground"]
|
41
|
+
addopts = "-s"
|
42
|
+
env_files = [".env"]
|
43
|
+
|
44
|
+
[tool.hatch.build.targets.wheel]
|
45
|
+
packages = ["src/fastmcp_agents"]
|
46
|
+
|
47
|
+
[tool.hatch.build.targets.sdist]
|
48
|
+
packages = ["src/fastmcp_agents"]
|
49
|
+
|
50
|
+
[tool.ruff]
|
51
|
+
extend="../../../pyproject.toml"
|
52
|
+
|
53
|
+
[tool.pyright]
|
54
|
+
extends = "../../../pyproject.toml"
|