From dedb2a60e1a13d9e5a2d5442198c0e314a5c36cb Mon Sep 17 00:00:00 2001 From: ymq Date: Sun, 9 Aug 2026 19:55:16 +0800 Subject: [PATCH] feat: delete_project tool with cascade cleanup + confirm gate --- wwwroot/api/cockpit_chat.dspy | 34 +++++++++++++++++++++++++++++++++- 1 file changed, 33 insertions(+), 1 deletion(-) diff --git a/wwwroot/api/cockpit_chat.dspy b/wwwroot/api/cockpit_chat.dspy index bb7f871..5c64096 100644 --- a/wwwroot/api/cockpit_chat.dspy +++ b/wwwroot/api/cockpit_chat.dspy @@ -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: