fix: git 版本管理链路跑通 — role agent clone 时机 + git_clone 工具 + repo_dir 兼容
This commit is contained in:
parent
011dd9cb95
commit
d89ec3c784
@ -167,6 +167,7 @@ AGENT_TOOLS = [
|
||||
{"name":"write_file","description":"写入文件(自动创建父目录)","params":{"path":"相对路径","content":"文件内容"}},
|
||||
{"name":"list_files","description":"列出目录内容","params":{"path":"相对路径(可选,默认工作空间根)"}},
|
||||
{"name":"run_shell","description":"在工作空间中执行shell命令","params":{"command":"命令"}},
|
||||
{"name":"git_clone","description":"克隆git仓库到工作空间repos/下","params":{"repo_url":"仓库URL","repo_name":"仓库目录名(可选,默认从URL推断)","branch":"分支(可选,默认main)"}},
|
||||
{"name":"git_status","description":"查看git仓库状态","params":{"repo_dir":"仓库子目录(可选,默认repos下第一个)"}},
|
||||
{"name":"git_commit_push","description":"git add + commit + push","params":{"message":"提交信息","repo_dir":"仓库子目录(可选)"}},
|
||||
{"name":"ask_question","description":"向用户提问(缺少信息时使用)","params":{"question":"问题"}},
|
||||
@ -473,6 +474,21 @@ def _parse_agent_action(raw):
|
||||
return {"action": "deliver", "result": raw}
|
||||
|
||||
|
||||
def _resolve_repo_target(workspace_dir, repo_dir):
|
||||
"""解析仓库目录:空→repos/下第一个git仓库;'repos/xxx'→直接;'xxx'→repos/xxx。"""
|
||||
repos_dir = os.path.join(workspace_dir, 'repos')
|
||||
if repo_dir:
|
||||
if repo_dir.startswith('repos/') or repo_dir.startswith('repos\\'):
|
||||
return os.path.join(workspace_dir, repo_dir)
|
||||
return os.path.join(repos_dir, repo_dir)
|
||||
if os.path.isdir(repos_dir):
|
||||
dirs = [d for d in os.listdir(repos_dir)
|
||||
if os.path.isdir(os.path.join(repos_dir, d, '.git'))]
|
||||
if dirs:
|
||||
return os.path.join(repos_dir, dirs[0])
|
||||
return workspace_dir
|
||||
|
||||
|
||||
async def _exec_agent_tool(tool, params, workspace_dir):
|
||||
p = params or {}
|
||||
try:
|
||||
@ -512,26 +528,21 @@ async def _exec_agent_tool(tool, params, workspace_dir):
|
||||
if not cmd: return 'FAIL: 需要命令'
|
||||
r = await _run_shell(cmd, workspace_dir, timeout=120)
|
||||
return f"rc={r['rc']}\nSTDOUT:\n{r['stdout'][:2000]}\nSTDERR:\n{r['stderr'][:1000]}"
|
||||
elif tool == 'git_clone':
|
||||
url = p.get('repo_url', '')
|
||||
if not url: return 'FAIL: 需要仓库URL'
|
||||
name = p.get('repo_name', '') or url.rstrip('/').split('/')[-1].replace('.git', '')
|
||||
target = os.path.join(workspace_dir, 'repos', name)
|
||||
r = await _git_clone(url, target, p.get('branch', 'main'))
|
||||
return f"rc={r['rc']} {r['message']}"
|
||||
elif tool == 'git_status':
|
||||
repo = p.get('repo_dir', '')
|
||||
repos_dir = os.path.join(workspace_dir, 'repos')
|
||||
if repo:
|
||||
target = os.path.join(workspace_dir, repo)
|
||||
else:
|
||||
dirs = [d for d in os.listdir(repos_dir) if os.path.isdir(os.path.join(repos_dir, d, '.git'))] if os.path.isdir(repos_dir) else []
|
||||
target = os.path.join(repos_dir, dirs[0]) if dirs else workspace_dir
|
||||
target = _resolve_repo_target(workspace_dir, p.get('repo_dir', ''))
|
||||
r = await _run_shell('git status --short', target, 10)
|
||||
r2 = await _run_shell('git log --oneline -3', target, 10)
|
||||
return f"Status:\n{r['stdout'][:1000] or '(clean)'}\nRecent:\n{r2['stdout'][:500]}"
|
||||
elif tool == 'git_commit_push':
|
||||
msg = p.get('message', '') or 'agent update'
|
||||
repo = p.get('repo_dir', '')
|
||||
repos_dir = os.path.join(workspace_dir, 'repos')
|
||||
if repo:
|
||||
target = os.path.join(workspace_dir, repo)
|
||||
else:
|
||||
dirs = [d for d in os.listdir(repos_dir) if os.path.isdir(os.path.join(repos_dir, d, '.git'))] if os.path.isdir(repos_dir) else []
|
||||
target = os.path.join(repos_dir, dirs[0]) if dirs else workspace_dir
|
||||
target = _resolve_repo_target(workspace_dir, p.get('repo_dir', ''))
|
||||
r = await _git_commit_push(target, msg)
|
||||
return f"rc={r['rc']} {r['message']}"
|
||||
return f'未实现: {tool}'
|
||||
@ -565,6 +576,13 @@ async def role_agent_run(project_id, role, agent_id=None, model_name=None):
|
||||
repos_dir = os.path.join(workspace_dir, 'repos')
|
||||
os.makedirs(repos_dir, exist_ok=True)
|
||||
|
||||
# 确保项目关联仓库已 clone(幂等:已存在则 pull)——否则源码写不进 git 仓库
|
||||
try:
|
||||
clone_results = await _setup_repos(sor, workspace_dir, project_id)
|
||||
logger.info(f"role_agent_run setup_repos: {clone_results}")
|
||||
except Exception as e:
|
||||
logger.warning(f"role_agent_run setup_repos failed: {e}")
|
||||
|
||||
role_specific = ROLE_SPECIFICS.get(role, ROLE_SPECIFICS.get('develop', ''))
|
||||
qna_section = await _build_qna_section(sor, task_id)
|
||||
tools_text = json.dumps(AGENT_TOOLS, ensure_ascii=False)
|
||||
|
||||
@ -563,6 +563,7 @@ class AgentExecutor:
|
||||
"answer_question": self._t_answer_question,
|
||||
"add_repo": self._t_add_repo,
|
||||
"list_repos": self._t_list_repos,
|
||||
"clone_repo": self._t_clone_repo,
|
||||
"run_command": self._t_run_command,
|
||||
"check_progress": self._t_check_progress,
|
||||
"add_bug": self._t_add_bug,
|
||||
@ -919,6 +920,30 @@ class AgentExecutor:
|
||||
lines.append(f"- {getattr(r, 'repo_name', '')}: {getattr(r, 'repo_url', '')}")
|
||||
return "\n".join(lines)
|
||||
|
||||
async def _t_clone_repo(self, sor, p, pid):
|
||||
if not pid:
|
||||
return "请先切换到项目"
|
||||
|
||||
import os
|
||||
from pipeline_service.agent_loop import (
|
||||
_setup_repos, _get_workspace_dir, _git_clone)
|
||||
|
||||
workspace_dir = await _get_workspace_dir(sor, pid)
|
||||
url = (p.get("repo_url", "") or "").strip()
|
||||
|
||||
if url:
|
||||
name = url.rstrip("/").split("/")[-1].replace(".git", "")
|
||||
target = os.path.join(workspace_dir, "repos", name)
|
||||
r = await _git_clone(url, target, p.get("branch", "main"))
|
||||
return f"rc={r['rc']} {r['message']}"
|
||||
|
||||
results = await _setup_repos(sor, workspace_dir, pid)
|
||||
if not results:
|
||||
return "无关联仓库可克隆"
|
||||
return "\n".join(
|
||||
f"- {x['repo']}: rc={x.get('rc', '?')} {x.get('message', '')}"
|
||||
for x in results)
|
||||
|
||||
async def _t_run_command(self, sor, p, pid):
|
||||
cmd = p.get("command", "")
|
||||
if not cmd:
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user