feat: delete_project tool with cascade cleanup + confirm gate

This commit is contained in:
ymq 2026-08-09 19:55:16 +08:00
parent 258afb1e7e
commit dedb2a60e1

View File

@ -19,6 +19,7 @@ AGENT_PROMPT = """你是开发产线驾驶舱 Agent。用工具获取数据
工作流:
- 切换项目:先 list_projects 看有哪些项目,再 switch_project
- 创建项目:不存在则 create_project
- 删除项目delete_project需输入项目名二次确认
- 查看进度list_tasks / task_detail / list_deliverables
- 提交任务:先确保在正确项目,再 create_task
- 启动Agent任务提交后调 start_agents 让角色Agent执行
@ -37,6 +38,7 @@ TOOLS = [
{"name":"list_projects","description":"列出所有可用项目","params":{}},
{"name":"switch_project","description":"切换到指定项目需先list_projects确认项目名","params":{"project":"项目名称"}},
{"name":"create_project","description":"创建新项目","params":{"name":"项目名称","description":"项目描述(可选)"}},
{"name":"delete_project","description":"删除项目及其所有关联数据(需二次确认)","params":{"project":"项目名称","confirm":"输入项目名称确认删除"}},
{"name":"list_tasks","description":"列出当前项目任务","params":{"state":"状态(可选)"}},
{"name":"task_detail","description":"查看任务详情(含交付件和问答)","params":{"task_id":"任务ID"}},
{"name":"list_deliverables","description":"列出当前项目交付件","params":{"task_id":"任务ID(可选)"}},
@ -131,7 +133,7 @@ def _w_card(title, body, kind="success"):
]}
def _w_progress(text): return {"widgettype":"Text","options":{"text":text,"style":{"color":"#f59e0b","fontSize":"12px","padding":"2px 8px"},"halign":"left"}}
_tool_labels = {'list_projects':'列出项目','switch_project':'切换项目','create_project':'创建项目','list_tasks':'查询任务','task_detail':'任务详情','list_deliverables':'查看交付件','view_deliverable':'交付件内容','list_questions':'查看问题','answer_question':'回答问题','create_task':'创建任务','start_agents':'启动角色Agent','check_progress':'检查进度','add_repo':'关联仓库','list_repos':'查看仓库','clone_repo':'克隆仓库','add_bug':'报告Bug','run_command':'执行命令'}
_tool_labels = {'list_projects':'列出项目','switch_project':'切换项目','create_project':'创建项目','delete_project':'删除项目','list_tasks':'查询任务','task_detail':'任务详情','list_deliverables':'查看交付件','view_deliverable':'交付件内容','list_questions':'查看问题','answer_question':'回答问题','create_task':'创建任务','start_agents':'启动角色Agent','check_progress':'检查进度','add_repo':'关联仓库','list_repos':'查看仓库','clone_repo':'克隆仓库','add_bug':'报告Bug','run_command':'执行命令'}
async def _exec_tool(sor, tool, params, ctx, uid, org_id):
p = params or {}
@ -157,6 +159,36 @@ async def _exec_tool(sor, tool, params, ctx, uid, org_id):
await _save_ctx(sor, uid, pid)
ctx['pid'] = pid; ctx['pname'] = name; ctx['ws'] = ws
return f'OK: 已创建并切换到「{name}」'
elif tool == 'delete_project':
name = (p.get('project','') or '').strip()
confirm = (p.get('confirm','') or '').strip()
if not name: return 'FAIL: 需要项目名称'
if confirm != name: return f'FAIL: 确认不匹配(需输入"{name}"确认)'
proj = await _find_project(sor, name)
if not proj: return f'FAIL: 项目「{name}」不存在'
pid = proj.id
# 级联删除关联数据
tables = [
('pipeline_deliverables', 'project_id'),
('pipeline_tasks', 'tenant_id'),
('pipeline_questions', 'project_id'),
('pipeline_project_agents', 'project_id'),
('sd_project_repos', 'project_id'),
('sd_iterations', 'project_id'),
('sd_bugs', 'iteration_id IN (SELECT id FROM sd_iterations WHERE project_id=${p}$)'),
('pipeline_conversations', '1=0'), # conversations not directly project-scoped
]
count = 0
for tbl, cond in tables:
if cond == '1=0': continue
sql = f"DELETE FROM {tbl} WHERE {cond}=${{p}}$"
await sor.sqlExe(sql, {"p": pid})
count += 1
await sor.sqlExe("DELETE FROM pipeline_agent_settings WHERE current_project_id=${p}$", {"p": pid})
await sor.D('sd_projects', {'id': pid})
if ctx['pid'] == pid:
ctx['pid'] = ''; ctx['pname'] = ''; ctx['ws'] = ''; ctx['repos'] = []
return f'OK: 已删除「{name}」及其所有关联数据'
elif tool == 'switch_project':
proj = await _find_project(sor, p.get('project',''))
if proj: