feat(flywheel): 阶段三执行质量预测层(trailer 死链修复 + 风险画像) #2
@ -47,19 +47,120 @@ fn liangjing_path(project_id: &str) -> Result<PathBuf, String> {
|
||||
Ok(project_root(project_id)?.join(".claude").join("liangjing.json"))
|
||||
}
|
||||
|
||||
/// 写炼境上下文文件,供子项目 Claude 会话直接读取 project_id(不通过遍历 list_group_projects 匹配)
|
||||
fn write_liangjing_context(project_id: &str, port: u16) -> Result<(), String> {
|
||||
/// 写炼境上下文文件,供子项目 Claude 会话直接读取 project_id / group_id
|
||||
fn write_liangjing_context(project_id: &str, group_id: &str, port: u16) -> Result<(), String> {
|
||||
let path = liangjing_path(project_id)?;
|
||||
if let Some(parent) = path.parent() {
|
||||
std::fs::create_dir_all(parent)
|
||||
.map_err(|e| format!("创建 .claude 目录失败: {}", e))?;
|
||||
}
|
||||
let ctx = json!({ "project_id": project_id, "mcp_port": port });
|
||||
let ctx = json!({
|
||||
"project_id": project_id,
|
||||
"group_id": group_id,
|
||||
"mcp_port": port
|
||||
});
|
||||
let content = serde_json::to_string_pretty(&ctx).map_err(|e| e.to_string())?;
|
||||
std::fs::write(&path, content)
|
||||
.map_err(|e| format!("写入 liangjing.json 失败: {}", e))
|
||||
}
|
||||
|
||||
/// 查找 claude CLI 路径(优先环境变量 CLAUDE_BIN,然后常用安装目录,最后 where 命令)
|
||||
fn find_claude_bin() -> Option<String> {
|
||||
if let Ok(path) = std::env::var("CLAUDE_BIN") {
|
||||
if std::path::Path::new(&path).exists() {
|
||||
return Some(path);
|
||||
}
|
||||
}
|
||||
|
||||
let home = std::env::var("USERPROFILE")
|
||||
.or_else(|_| std::env::var("HOME"))
|
||||
.unwrap_or_default();
|
||||
|
||||
let candidates = [
|
||||
format!(r"{}\.local\bin\claude.exe", home),
|
||||
format!(r"{}\AppData\Local\AnthropicClaude\claude.exe", home),
|
||||
r"C:\Program Files\AnthropicClaude\claude.exe".to_string(),
|
||||
];
|
||||
|
||||
for c in &candidates {
|
||||
if std::path::Path::new(c).exists() {
|
||||
return Some(c.clone());
|
||||
}
|
||||
}
|
||||
|
||||
// 最后尝试 where 命令(GUI 进程 PATH 可能不含用户 bin,but worth trying)
|
||||
if let Ok(out) = std::process::Command::new("where").arg("claude.exe").output() {
|
||||
if out.status.success() {
|
||||
if let Ok(stdout) = String::from_utf8(out.stdout) {
|
||||
if let Some(line) = stdout.lines().next() {
|
||||
return Some(line.trim().to_string());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
/// 将产品组 MCP 注册到 Claude 全局配置(~/.claude.json),best-effort,失败仅记录日志
|
||||
fn try_register_globally(group_id: &str, port: u16) {
|
||||
let server_name = format!("lian-jing-{}", group_id);
|
||||
let server_url = format!("http://127.0.0.1:{}/mcp/group/{}", port, group_id);
|
||||
|
||||
let Some(claude_bin) = find_claude_bin() else {
|
||||
db::log_event(
|
||||
"mcp_inject",
|
||||
"warn",
|
||||
"claude CLI 未找到,跳过全局 Mcp 注册",
|
||||
None,
|
||||
);
|
||||
return;
|
||||
};
|
||||
|
||||
let git_bash = std::env::var("CLAUDE_CODE_GIT_BASH_PATH").unwrap_or_else(|_| {
|
||||
r"C:\Program Files\Git\bin\bash.exe".to_string()
|
||||
});
|
||||
|
||||
match std::process::Command::new(&claude_bin)
|
||||
.args([
|
||||
"mcp",
|
||||
"add",
|
||||
"--transport",
|
||||
"sse",
|
||||
"-s",
|
||||
"user",
|
||||
&server_name,
|
||||
&server_url,
|
||||
])
|
||||
.env("CLAUDE_CODE_GIT_BASH_PATH", &git_bash)
|
||||
.output()
|
||||
{
|
||||
Ok(out) if out.status.success() => {
|
||||
db::log_event(
|
||||
"mcp_inject",
|
||||
"info",
|
||||
"全局 MCP 注册成功",
|
||||
Some(&server_name),
|
||||
);
|
||||
}
|
||||
Ok(out) => {
|
||||
let msg = format!(
|
||||
"claude mcp add 非零退出: {}",
|
||||
String::from_utf8_lossy(&out.stderr)
|
||||
);
|
||||
db::log_event("mcp_inject", "warn", &msg, Some(&server_name));
|
||||
}
|
||||
Err(e) => {
|
||||
db::log_event(
|
||||
"mcp_inject",
|
||||
"warn",
|
||||
&format!("执行 claude mcp add 失败: {}", e),
|
||||
Some(&server_name),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ── JSON 读写 ─────────────────────────────────────────────────────────────────
|
||||
|
||||
fn read_json(path: &PathBuf) -> Result<Value, String> {
|
||||
@ -118,8 +219,11 @@ pub fn inject_project_mcp(project_id: &str, group_id: &str) -> Result<(), String
|
||||
log_event("mcp_inject", "info", "MCP 配置注入成功", Some(&ctx));
|
||||
})?;
|
||||
|
||||
// 写炼境上下文,让子项目 Claude 会话无需遍历即可知道自己的 project_id
|
||||
let _ = write_liangjing_context(project_id, port);
|
||||
// 写炼境上下文(含 group_id),让子项目 Claude 会话无需遍历即可知道 project_id + group_id
|
||||
let _ = write_liangjing_context(project_id, group_id, port);
|
||||
|
||||
// 同步注册到 ~/.claude.json,让 Claude Code 能加载 MCP 工具(best-effort)
|
||||
try_register_globally(group_id, port);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user