github-agent 0.2.12__tar.gz → 0.2.14__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.
- {github_agent-0.2.12 → github_agent-0.2.14}/PKG-INFO +4 -3
- {github_agent-0.2.12 → github_agent-0.2.14}/README.md +1 -1
- {github_agent-0.2.12 → github_agent-0.2.14}/github_agent/github_agent.py +73 -7
- github_agent-0.2.14/github_agent/skills/a2a_client/scripts/a2a_client.py +212 -0
- {github_agent-0.2.12 → github_agent-0.2.14}/github_agent/utils.py +38 -15
- {github_agent-0.2.12 → github_agent-0.2.14}/github_agent.egg-info/PKG-INFO +4 -3
- {github_agent-0.2.12 → github_agent-0.2.14}/github_agent.egg-info/SOURCES.txt +1 -0
- {github_agent-0.2.12 → github_agent-0.2.14}/github_agent.egg-info/requires.txt +2 -1
- {github_agent-0.2.12 → github_agent-0.2.14}/pyproject.toml +3 -2
- {github_agent-0.2.12 → github_agent-0.2.14}/LICENSE +0 -0
- {github_agent-0.2.12 → github_agent-0.2.14}/github_agent/__init__.py +0 -0
- {github_agent-0.2.12 → github_agent-0.2.14}/github_agent.egg-info/dependency_links.txt +0 -0
- {github_agent-0.2.12 → github_agent-0.2.14}/github_agent.egg-info/entry_points.txt +0 -0
- {github_agent-0.2.12 → github_agent-0.2.14}/github_agent.egg-info/top_level.txt +0 -0
- {github_agent-0.2.12 → github_agent-0.2.14}/scripts/validate_a2a_agent.py +0 -0
- {github_agent-0.2.12 → github_agent-0.2.14}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: github-agent
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.14
|
|
4
4
|
Summary: GitHub Agent for MCP
|
|
5
5
|
Author-email: Audel Rouhi <knucklessg1@gmail.com>
|
|
6
6
|
License: MIT
|
|
@@ -12,7 +12,8 @@ Classifier: Programming Language :: Python :: 3
|
|
|
12
12
|
Requires-Python: >=3.10
|
|
13
13
|
Description-Content-Type: text/markdown
|
|
14
14
|
License-File: LICENSE
|
|
15
|
-
Requires-Dist:
|
|
15
|
+
Requires-Dist: tree-sitter>=0.23.2
|
|
16
|
+
Requires-Dist: pydantic-ai-slim[a2a,ag-ui,anthropic,fastmcp,google,groq,huggingface,mistral,openai,web]>=1.60.0
|
|
16
17
|
Requires-Dist: pydantic-ai-skills>=v0.4.0
|
|
17
18
|
Requires-Dist: fastapi>=0.128.0
|
|
18
19
|
Requires-Dist: fastmcp
|
|
@@ -43,7 +44,7 @@ Dynamic: license-file
|
|
|
43
44
|

|
|
44
45
|

|
|
45
46
|
|
|
46
|
-
*Version: 0.2.
|
|
47
|
+
*Version: 0.2.14*
|
|
47
48
|
|
|
48
49
|
## Overview
|
|
49
50
|
|
|
@@ -39,7 +39,7 @@ from pydantic import ValidationError
|
|
|
39
39
|
from pydantic_ai.ui import SSE_CONTENT_TYPE
|
|
40
40
|
from pydantic_ai.ui.ag_ui import AGUIAdapter
|
|
41
41
|
|
|
42
|
-
__version__ = "0.2.
|
|
42
|
+
__version__ = "0.2.14"
|
|
43
43
|
|
|
44
44
|
logging.basicConfig(
|
|
45
45
|
level=logging.INFO,
|
|
@@ -363,7 +363,23 @@ def create_agent(
|
|
|
363
363
|
),
|
|
364
364
|
}
|
|
365
365
|
|
|
366
|
+
# 1. Identify Universal Skills
|
|
367
|
+
# Universal skills are those in the skills directory that do NOT start with the package prefix
|
|
368
|
+
package_prefix = "github-"
|
|
369
|
+
skills_path = get_skills_path()
|
|
370
|
+
universal_skill_dirs = []
|
|
371
|
+
|
|
372
|
+
if os.path.exists(skills_path):
|
|
373
|
+
for item in os.listdir(skills_path):
|
|
374
|
+
item_path = os.path.join(skills_path, item)
|
|
375
|
+
if os.path.isdir(item_path):
|
|
376
|
+
if not item.startswith(package_prefix):
|
|
377
|
+
universal_skill_dirs.append(item_path)
|
|
378
|
+
logger.info(f"Identified universal skill: {item}")
|
|
379
|
+
|
|
380
|
+
supervisor_skills = []
|
|
366
381
|
child_agents = {}
|
|
382
|
+
supervisor_skills_directories = [get_skills_path()]
|
|
367
383
|
|
|
368
384
|
for tag, (system_prompt, agent_name) in agent_defs.items():
|
|
369
385
|
tag_toolsets = []
|
|
@@ -381,21 +397,28 @@ def create_agent(
|
|
|
381
397
|
# Load specific skills for this tag
|
|
382
398
|
skill_dir_name = f"github-{tag.replace('_', '-')}"
|
|
383
399
|
|
|
400
|
+
child_skills_directories = []
|
|
401
|
+
|
|
384
402
|
# Check custom skills directory
|
|
385
403
|
if custom_skills_directory:
|
|
386
404
|
skill_dir_path = os.path.join(custom_skills_directory, skill_dir_name)
|
|
387
405
|
if os.path.exists(skill_dir_path):
|
|
388
|
-
|
|
389
|
-
logger.info(
|
|
390
|
-
f"Loaded specialized skills for {tag} from {skill_dir_path}"
|
|
391
|
-
)
|
|
406
|
+
child_skills_directories.append(skill_dir_path)
|
|
392
407
|
|
|
393
408
|
# Check default skills directory
|
|
394
409
|
default_skill_path = os.path.join(get_skills_path(), skill_dir_name)
|
|
395
410
|
if os.path.exists(default_skill_path):
|
|
396
|
-
|
|
411
|
+
child_skills_directories.append(default_skill_path)
|
|
412
|
+
|
|
413
|
+
# Append Universal Skills to ALL child agents
|
|
414
|
+
if universal_skill_dirs:
|
|
415
|
+
child_skills_directories.extend(universal_skill_dirs)
|
|
416
|
+
|
|
417
|
+
if child_skills_directories:
|
|
418
|
+
ts = SkillsToolset(directories=child_skills_directories)
|
|
419
|
+
tag_toolsets.append(ts)
|
|
397
420
|
logger.info(
|
|
398
|
-
f"Loaded specialized skills for {tag} from {
|
|
421
|
+
f"Loaded specialized skills for {tag} from {child_skills_directories}"
|
|
399
422
|
)
|
|
400
423
|
|
|
401
424
|
# Collect tool names for logging
|
|
@@ -434,11 +457,43 @@ def create_agent(
|
|
|
434
457
|
)
|
|
435
458
|
child_agents[tag] = agent
|
|
436
459
|
|
|
460
|
+
# Create Custom Agent if custom_skills_directory is provided
|
|
461
|
+
if custom_skills_directory:
|
|
462
|
+
custom_agent_tag = "custom_agent"
|
|
463
|
+
custom_agent_name = "Custom_Agent"
|
|
464
|
+
custom_agent_prompt = (
|
|
465
|
+
"You are the Custom Agent.\n"
|
|
466
|
+
"Your goal is to handle custom tasks or general tasks not covered by other specialists.\n"
|
|
467
|
+
"You have access to valid custom skills and universal skills."
|
|
468
|
+
)
|
|
469
|
+
|
|
470
|
+
custom_agent_skills_dirs = list(universal_skill_dirs)
|
|
471
|
+
custom_agent_skills_dirs.append(custom_skills_directory)
|
|
472
|
+
|
|
473
|
+
custom_toolsets = []
|
|
474
|
+
custom_toolsets.append(SkillsToolset(directories=custom_agent_skills_dirs))
|
|
475
|
+
|
|
476
|
+
custom_agent = Agent(
|
|
477
|
+
name=custom_agent_name,
|
|
478
|
+
system_prompt=custom_agent_prompt,
|
|
479
|
+
model=model,
|
|
480
|
+
model_settings=settings,
|
|
481
|
+
toolsets=custom_toolsets,
|
|
482
|
+
tool_timeout=DEFAULT_TOOL_TIMEOUT,
|
|
483
|
+
)
|
|
484
|
+
child_agents[custom_agent_tag] = custom_agent
|
|
485
|
+
|
|
486
|
+
if custom_skills_directory:
|
|
487
|
+
supervisor_skills_directories.append(custom_skills_directory)
|
|
488
|
+
supervisor_skills.append(SkillsToolset(directories=supervisor_skills_directories))
|
|
489
|
+
logger.info(f"Loaded supervisor skills from: {supervisor_skills_directories}")
|
|
490
|
+
|
|
437
491
|
supervisor = Agent(
|
|
438
492
|
name=AGENT_NAME,
|
|
439
493
|
system_prompt=SUPERVISOR_SYSTEM_PROMPT,
|
|
440
494
|
model=model,
|
|
441
495
|
model_settings=settings,
|
|
496
|
+
toolsets=supervisor_skills,
|
|
442
497
|
deps_type=Any,
|
|
443
498
|
)
|
|
444
499
|
|
|
@@ -707,6 +762,17 @@ def create_agent(
|
|
|
707
762
|
logger.exception(f"Error in Support Docs Agent: {e}")
|
|
708
763
|
return f"Error executing task for Support Docs Agent: {e}"
|
|
709
764
|
|
|
765
|
+
if "custom_agent" in child_agents:
|
|
766
|
+
|
|
767
|
+
@supervisor.tool
|
|
768
|
+
async def assign_task_to_custom_agent(ctx: RunContext[Any], task: str) -> str:
|
|
769
|
+
"""Assign a task to the Custom Agent."""
|
|
770
|
+
return (
|
|
771
|
+
await child_agents["custom_agent"]
|
|
772
|
+
.run(task, usage=ctx.usage, deps=ctx.deps)
|
|
773
|
+
.output
|
|
774
|
+
)
|
|
775
|
+
|
|
710
776
|
return supervisor
|
|
711
777
|
|
|
712
778
|
|
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
import asyncio
|
|
3
|
+
import httpx
|
|
4
|
+
import json
|
|
5
|
+
import uuid
|
|
6
|
+
import argparse
|
|
7
|
+
import sys
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
async def validate_agent_card(client, agent_url):
|
|
11
|
+
"""
|
|
12
|
+
Validates the agent by fetching its well-known agent card.
|
|
13
|
+
"""
|
|
14
|
+
card_url = f"{agent_url.rstrip('/')}/.well-known/agent-card.json"
|
|
15
|
+
print(f"Fetching agent card from: {card_url}")
|
|
16
|
+
try:
|
|
17
|
+
resp = await client.get(card_url)
|
|
18
|
+
if resp.status_code == 200:
|
|
19
|
+
try:
|
|
20
|
+
card_data = resp.json()
|
|
21
|
+
print(f"Agent Card Found: {json.dumps(card_data, indent=2)}")
|
|
22
|
+
return True
|
|
23
|
+
except json.JSONDecodeError:
|
|
24
|
+
print(f"Failed to decode agent card JSON from {card_url}")
|
|
25
|
+
return False
|
|
26
|
+
else:
|
|
27
|
+
print(f"Failed to fetch agent card. Status Code: {resp.status_code}")
|
|
28
|
+
return False
|
|
29
|
+
except httpx.RequestError as e:
|
|
30
|
+
print(f"Connection failed to {card_url}: {e}")
|
|
31
|
+
return False
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
async def send_message(client, agent_url, message_text):
|
|
35
|
+
"""
|
|
36
|
+
Sends a message to the agent via JSON-RPC.
|
|
37
|
+
"""
|
|
38
|
+
print(f"\nSending Message: '{message_text}' to {agent_url}")
|
|
39
|
+
|
|
40
|
+
payload = {
|
|
41
|
+
"jsonrpc": "2.0",
|
|
42
|
+
"method": "message/send",
|
|
43
|
+
"params": {
|
|
44
|
+
"message": {
|
|
45
|
+
"kind": "message",
|
|
46
|
+
"role": "user",
|
|
47
|
+
"parts": [{"kind": "text", "text": message_text}],
|
|
48
|
+
"messageId": str(uuid.uuid4()),
|
|
49
|
+
}
|
|
50
|
+
},
|
|
51
|
+
"id": 1,
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
try:
|
|
55
|
+
resp = await client.post(
|
|
56
|
+
agent_url, json=payload, headers={"Content-Type": "application/json"}
|
|
57
|
+
)
|
|
58
|
+
|
|
59
|
+
if resp.status_code != 200:
|
|
60
|
+
print(f"Error sending message. Status Code: {resp.status_code}")
|
|
61
|
+
print(resp.text)
|
|
62
|
+
return None
|
|
63
|
+
|
|
64
|
+
data = resp.json()
|
|
65
|
+
if "error" in data:
|
|
66
|
+
print(f"JSON-RPC Error: {data['error']}")
|
|
67
|
+
return None
|
|
68
|
+
|
|
69
|
+
if "result" in data and "id" in data["result"]:
|
|
70
|
+
task_id = data["result"]["id"]
|
|
71
|
+
print(f"Task Submitted with ID: {task_id}")
|
|
72
|
+
return task_id
|
|
73
|
+
else:
|
|
74
|
+
print(f"Unexpected response format: {data}")
|
|
75
|
+
return None
|
|
76
|
+
|
|
77
|
+
except httpx.RequestError as e:
|
|
78
|
+
print(f"Connection failed during message send: {e}")
|
|
79
|
+
return None
|
|
80
|
+
except json.JSONDecodeError:
|
|
81
|
+
print(f"Failed to decode response JSON: {resp.text}")
|
|
82
|
+
return None
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
async def poll_task(client, agent_url, task_id):
|
|
86
|
+
"""
|
|
87
|
+
Polls the task status until completion.
|
|
88
|
+
"""
|
|
89
|
+
print(f"Polling for result for Task ID: {task_id}...")
|
|
90
|
+
|
|
91
|
+
while True:
|
|
92
|
+
await asyncio.sleep(2)
|
|
93
|
+
poll_payload = {
|
|
94
|
+
"jsonrpc": "2.0",
|
|
95
|
+
"method": "tasks/get",
|
|
96
|
+
"params": {"id": task_id},
|
|
97
|
+
"id": 2,
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
try:
|
|
101
|
+
poll_resp = await client.post(
|
|
102
|
+
agent_url,
|
|
103
|
+
json=poll_payload,
|
|
104
|
+
headers={"Content-Type": "application/json"},
|
|
105
|
+
)
|
|
106
|
+
|
|
107
|
+
if poll_resp.status_code != 200:
|
|
108
|
+
print(f"Polling Failed: {poll_resp.status_code}")
|
|
109
|
+
print(f"Details: {poll_resp.text}")
|
|
110
|
+
break
|
|
111
|
+
|
|
112
|
+
poll_data = poll_resp.json()
|
|
113
|
+
|
|
114
|
+
if "error" in poll_data:
|
|
115
|
+
print(f"Polling Error: {poll_data['error']}")
|
|
116
|
+
break
|
|
117
|
+
|
|
118
|
+
if "result" in poll_data:
|
|
119
|
+
status = poll_data["result"].get("status", {})
|
|
120
|
+
state = status.get("state")
|
|
121
|
+
print(f"Task State: {state}")
|
|
122
|
+
|
|
123
|
+
if state not in ["submitted", "running", "working"]:
|
|
124
|
+
print(f"\nTask Finished with state: {state}")
|
|
125
|
+
return poll_data["result"]
|
|
126
|
+
else:
|
|
127
|
+
print(f"Unexpected polling response: {poll_data}")
|
|
128
|
+
break
|
|
129
|
+
|
|
130
|
+
except httpx.RequestError as e:
|
|
131
|
+
print(f"Connection failed during polling: {e}")
|
|
132
|
+
break
|
|
133
|
+
except json.JSONDecodeError:
|
|
134
|
+
print(f"Failed to decode polling response: {poll_resp.text}")
|
|
135
|
+
break
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
def print_result(result):
|
|
139
|
+
"""
|
|
140
|
+
Prints the final result from the agent.
|
|
141
|
+
"""
|
|
142
|
+
if not result:
|
|
143
|
+
return
|
|
144
|
+
|
|
145
|
+
history = result.get("history", [])
|
|
146
|
+
if history:
|
|
147
|
+
last_msg = None
|
|
148
|
+
# Find the last message that is NOT from the user (i.e., the agent's response)
|
|
149
|
+
for msg in reversed(history):
|
|
150
|
+
if msg.get("role") != "user":
|
|
151
|
+
last_msg = msg
|
|
152
|
+
break
|
|
153
|
+
|
|
154
|
+
if last_msg:
|
|
155
|
+
print("\n--- Agent Response ---")
|
|
156
|
+
if "parts" in last_msg:
|
|
157
|
+
for part in last_msg["parts"]:
|
|
158
|
+
if "text" in part:
|
|
159
|
+
print(part["text"])
|
|
160
|
+
elif "content" in part:
|
|
161
|
+
print(part["content"])
|
|
162
|
+
else:
|
|
163
|
+
print(f"Final Message (No parts): {last_msg}")
|
|
164
|
+
else:
|
|
165
|
+
print("\n--- No Agent Response Found in History ---")
|
|
166
|
+
|
|
167
|
+
# print(f"\nFull Result Debug:\n{json.dumps(result, indent=2)}")
|
|
168
|
+
|
|
169
|
+
|
|
170
|
+
async def main():
|
|
171
|
+
parser = argparse.ArgumentParser(
|
|
172
|
+
description="A2A Client for communicating with other agents."
|
|
173
|
+
)
|
|
174
|
+
parser.add_argument(
|
|
175
|
+
"--url",
|
|
176
|
+
required=True,
|
|
177
|
+
help="The base URL of the A2A Agent (e.g., http://agent.arpa/a2a/)",
|
|
178
|
+
)
|
|
179
|
+
parser.add_argument(
|
|
180
|
+
"--query", required=True, help="The message/query to send to the agent"
|
|
181
|
+
)
|
|
182
|
+
|
|
183
|
+
args = parser.parse_args()
|
|
184
|
+
|
|
185
|
+
agent_url = args.url
|
|
186
|
+
query = args.query
|
|
187
|
+
|
|
188
|
+
print("Initializing A2A Client...")
|
|
189
|
+
print(f"Target Agent: {agent_url}")
|
|
190
|
+
print(f"Query: {query}")
|
|
191
|
+
|
|
192
|
+
async with httpx.AsyncClient(timeout=60.0) as client:
|
|
193
|
+
# 1. Validate Agent
|
|
194
|
+
if not await validate_agent_card(client, agent_url):
|
|
195
|
+
print("Agent validation failed. Aborting.")
|
|
196
|
+
sys.exit(1)
|
|
197
|
+
|
|
198
|
+
# 2. Send Message
|
|
199
|
+
task_id = await send_message(client, agent_url, query)
|
|
200
|
+
if not task_id:
|
|
201
|
+
print("Failed to submit task. Aborting.")
|
|
202
|
+
sys.exit(1)
|
|
203
|
+
|
|
204
|
+
# 3. Poll for Result
|
|
205
|
+
result = await poll_task(client, agent_url, task_id)
|
|
206
|
+
|
|
207
|
+
# 4. Print Result
|
|
208
|
+
print_result(result)
|
|
209
|
+
|
|
210
|
+
|
|
211
|
+
if __name__ == "__main__":
|
|
212
|
+
asyncio.run(main())
|
|
@@ -2,9 +2,22 @@
|
|
|
2
2
|
# coding: utf-8
|
|
3
3
|
|
|
4
4
|
import os
|
|
5
|
+
import httpx
|
|
6
|
+
import pickle
|
|
7
|
+
import yaml
|
|
8
|
+
import logging
|
|
9
|
+
from pathlib import Path
|
|
10
|
+
from typing import Union, List, Any, Optional
|
|
11
|
+
import json
|
|
12
|
+
from importlib.resources import files, as_file
|
|
13
|
+
from pydantic_ai.models.openai import OpenAIChatModel
|
|
14
|
+
from pydantic_ai.models.google import GoogleModel
|
|
15
|
+
from pydantic_ai.models.huggingface import HuggingFaceModel
|
|
16
|
+
from pydantic_ai.models.groq import GroqModel
|
|
17
|
+
from pydantic_ai.models.mistral import MistralModel
|
|
18
|
+
from fasta2a import Skill
|
|
5
19
|
|
|
6
20
|
try:
|
|
7
|
-
|
|
8
21
|
from openai import AsyncOpenAI
|
|
9
22
|
from pydantic_ai.providers.openai import OpenAIProvider
|
|
10
23
|
except ImportError:
|
|
@@ -26,26 +39,16 @@ except ImportError:
|
|
|
26
39
|
MistralProvider = None
|
|
27
40
|
|
|
28
41
|
try:
|
|
42
|
+
from pydantic_ai.models.anthropic import AnthropicModel
|
|
29
43
|
from anthropic import AsyncAnthropic
|
|
30
44
|
from pydantic_ai.providers.anthropic import AnthropicProvider
|
|
31
45
|
except ImportError:
|
|
46
|
+
AnthropicModel = None
|
|
32
47
|
AsyncAnthropic = None
|
|
33
48
|
AnthropicProvider = None
|
|
34
49
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
import yaml
|
|
38
|
-
from pathlib import Path
|
|
39
|
-
from typing import Any, Union, List, Optional
|
|
40
|
-
import json
|
|
41
|
-
from importlib.resources import files, as_file
|
|
42
|
-
from pydantic_ai.models.openai import OpenAIChatModel
|
|
43
|
-
from pydantic_ai.models.anthropic import AnthropicModel
|
|
44
|
-
from pydantic_ai.models.google import GoogleModel
|
|
45
|
-
from pydantic_ai.models.huggingface import HuggingFaceModel
|
|
46
|
-
from pydantic_ai.models.groq import GroqModel
|
|
47
|
-
from pydantic_ai.models.mistral import MistralModel
|
|
48
|
-
from pydantic_ai_skills import Skill
|
|
50
|
+
|
|
51
|
+
logger = logging.getLogger(__name__)
|
|
49
52
|
|
|
50
53
|
|
|
51
54
|
def to_integer(string: Union[str, int] = None) -> int:
|
|
@@ -163,6 +166,9 @@ def load_model(file: str) -> Any:
|
|
|
163
166
|
def retrieve_package_name() -> str:
|
|
164
167
|
"""
|
|
165
168
|
Returns the top-level package name of the module that imported this utils.py.
|
|
169
|
+
|
|
170
|
+
Works reliably when utils.py is inside a proper package (with __init__.py or
|
|
171
|
+
implicit namespace package) and the caller does normal imports.
|
|
166
172
|
"""
|
|
167
173
|
if __package__:
|
|
168
174
|
top = __package__.partition(".")[0]
|
|
@@ -363,6 +369,16 @@ def create_model(
|
|
|
363
369
|
def extract_tool_tags(tool_def: Any) -> List[str]:
|
|
364
370
|
"""
|
|
365
371
|
Extracts tags from a tool definition object.
|
|
372
|
+
|
|
373
|
+
Found structure in debug:
|
|
374
|
+
tool_def.name (str)
|
|
375
|
+
tool_def.meta (dict) -> {'fastmcp': {'tags': ['tag']}}
|
|
376
|
+
|
|
377
|
+
This function checks multiple paths to be robust:
|
|
378
|
+
1. tool_def.meta['fastmcp']['tags']
|
|
379
|
+
2. tool_def.meta['tags']
|
|
380
|
+
3. tool_def.metadata['tags'] (legacy/alternative wrapper)
|
|
381
|
+
4. tool_def.metadata.get('meta')... (nested path)
|
|
366
382
|
"""
|
|
367
383
|
tags_list = []
|
|
368
384
|
|
|
@@ -409,3 +425,10 @@ def tool_in_tag(tool_def: Any, tag: str) -> bool:
|
|
|
409
425
|
return True
|
|
410
426
|
else:
|
|
411
427
|
return False
|
|
428
|
+
|
|
429
|
+
|
|
430
|
+
def filter_tools_by_tag(tools: List[Any], tag: str) -> List[Any]:
|
|
431
|
+
"""
|
|
432
|
+
Filters a list of tools for a given tag.
|
|
433
|
+
"""
|
|
434
|
+
return [t for t in tools if tool_in_tag(t, tag)]
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: github-agent
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.14
|
|
4
4
|
Summary: GitHub Agent for MCP
|
|
5
5
|
Author-email: Audel Rouhi <knucklessg1@gmail.com>
|
|
6
6
|
License: MIT
|
|
@@ -12,7 +12,8 @@ Classifier: Programming Language :: Python :: 3
|
|
|
12
12
|
Requires-Python: >=3.10
|
|
13
13
|
Description-Content-Type: text/markdown
|
|
14
14
|
License-File: LICENSE
|
|
15
|
-
Requires-Dist:
|
|
15
|
+
Requires-Dist: tree-sitter>=0.23.2
|
|
16
|
+
Requires-Dist: pydantic-ai-slim[a2a,ag-ui,anthropic,fastmcp,google,groq,huggingface,mistral,openai,web]>=1.60.0
|
|
16
17
|
Requires-Dist: pydantic-ai-skills>=v0.4.0
|
|
17
18
|
Requires-Dist: fastapi>=0.128.0
|
|
18
19
|
Requires-Dist: fastmcp
|
|
@@ -43,7 +44,7 @@ Dynamic: license-file
|
|
|
43
44
|

|
|
44
45
|

|
|
45
46
|
|
|
46
|
-
*Version: 0.2.
|
|
47
|
+
*Version: 0.2.14*
|
|
47
48
|
|
|
48
49
|
## Overview
|
|
49
50
|
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
|
|
1
|
+
tree-sitter>=0.23.2
|
|
2
|
+
pydantic-ai-slim[a2a,ag-ui,anthropic,fastmcp,google,groq,huggingface,mistral,openai,web]>=1.60.0
|
|
2
3
|
pydantic-ai-skills>=v0.4.0
|
|
3
4
|
fastapi>=0.128.0
|
|
4
5
|
fastmcp
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "github-agent"
|
|
7
|
-
version = "0.2.
|
|
7
|
+
version = "0.2.14"
|
|
8
8
|
readme = "README.md"
|
|
9
9
|
description = "GitHub Agent for MCP"
|
|
10
10
|
requires-python = ">=3.10"
|
|
@@ -18,7 +18,8 @@ classifiers = [
|
|
|
18
18
|
"Programming Language :: Python :: 3",
|
|
19
19
|
]
|
|
20
20
|
dependencies = [
|
|
21
|
-
"
|
|
21
|
+
"tree-sitter>=0.23.2",
|
|
22
|
+
"pydantic-ai-slim[fastmcp,openai,groq,anthropic,mistral,google,huggingface,a2a,ag-ui,web]>=1.60.0",
|
|
22
23
|
"pydantic-ai-skills>=v0.4.0",
|
|
23
24
|
"fastapi>=0.128.0",
|
|
24
25
|
"fastmcp",
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|