修复: aichat P0 三项(AR-2 新建对话守卫 + AR-3 审批卡片可读化 + AR-4 create_project 双审)
- AR-2 ai_conversation_create 加 generating 守卫(clear 前,防审批态新建清空 session 致 generating 永不复位卡死) - AR-3 audit.rs build_approval_reason 按 9 工具特化拼对象名(id/field/path/title/name)+ ToolCard resultSummary 补 restore/purge case + ARG_LABEL_MAP id 加友好标签 + i18n 双语补 4 key - AR-4 create_project schema 加 path/stack + handler 有 path 时合并绑定(spawn_blocking 探测栈,复用 bind_directory 逻辑,消除双审双 API) - cargo check / vue-tsc 0 error - 审查 boundary 全误判(并行同工作区 git diff 污染),主代理独立核查三任务全正确
This commit is contained in:
@@ -144,8 +144,11 @@ pub fn build_ai_tool_registry(db: &Arc<Database>) -> AiToolRegistry {
|
||||
})},
|
||||
);
|
||||
registry.register(
|
||||
"create_project", "创建新项目",
|
||||
df_ai::ai_tools::object_schema(vec![("name", "string", true), ("description", "string", false)]),
|
||||
"create_project", "创建新项目,可选传 path/stack 一步完成创建+绑定目录(无需再调 bind_directory)",
|
||||
df_ai::ai_tools::object_schema(vec![
|
||||
("name", "string", true), ("description", "string", false),
|
||||
("path", "string", false), ("stack", "string", false),
|
||||
]),
|
||||
RiskLevel::Medium,
|
||||
{ let db = db.clone(); Box::new(move |args: serde_json::Value| {
|
||||
let db = db.clone();
|
||||
@@ -161,7 +164,42 @@ pub fn build_ai_tool_registry(db: &Arc<Database>) -> AiToolRegistry {
|
||||
};
|
||||
let id = record.id.clone();
|
||||
repo.insert(record).await?;
|
||||
Ok(serde_json::json!({ "id": id, "name": name, "status": "planning" }))
|
||||
// path 缺省:保持原行为(仅创建,不绑定)
|
||||
let path_opt = args.get("path").and_then(|v| v.as_str()).filter(|s| !s.is_empty());
|
||||
if let Some(path) = path_opt {
|
||||
// 绑定逻辑复用自 bind_directory handler(下文):目录校验 + 防重复绑定 + spawn_blocking 探测技术栈
|
||||
// TODO: 抽公共绑定闭包/函数供 create_project 与 bind_directory 共用(当前内联以收敛改动范围)
|
||||
let dir = std::path::Path::new(path);
|
||||
if !dir.is_dir() {
|
||||
anyhow::bail!("目录不存在: {path}");
|
||||
}
|
||||
let target = df_project::scan::normalize_path(path);
|
||||
let projects = repo.list_active().await?;
|
||||
for proj in &projects {
|
||||
if proj.id != id {
|
||||
if let Some(pp) = &proj.path {
|
||||
if df_project::scan::normalize_path(pp) == target {
|
||||
anyhow::bail!("目录已被项目「{}」绑定", proj.name);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// stack:AI 提供则用,否则探测(detect_stack 内含多次同步 fs IO,必须 spawn_blocking)
|
||||
let stack = if let Some(s) = args.get("stack").and_then(|v| v.as_str()).filter(|s| !s.is_empty()) {
|
||||
s.split(',').map(|t| t.trim().to_string()).filter(|t| !t.is_empty()).collect::<Vec<String>>()
|
||||
} else {
|
||||
let dir_buf = std::path::PathBuf::from(path);
|
||||
tokio::task::spawn_blocking(move || df_project::scan::detect_stack(&dir_buf))
|
||||
.await
|
||||
.map_err(|e| anyhow::anyhow!("技术栈探测任务失败: {e}"))??
|
||||
};
|
||||
let stack_json = serde_json::to_string(&stack)?;
|
||||
repo.update_field(&id, "path", path).await?;
|
||||
repo.update_field(&id, "stack", &stack_json).await?;
|
||||
Ok(serde_json::json!({ "id": id, "name": name, "status": "planning", "path": path, "stack": stack, "bound": true }))
|
||||
} else {
|
||||
Ok(serde_json::json!({ "id": id, "name": name, "status": "planning" }))
|
||||
}
|
||||
})
|
||||
})},
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user