169 lines
7.0 KiB
JavaScript
169 lines
7.0 KiB
JavaScript
import { exec } from "child_process";
|
||
import { promisify } from "util";
|
||
const execAsync = promisify(exec);
|
||
export const gitOpsTool = {
|
||
name: "git_ops",
|
||
description: "Git 全套操作。支持 status、diff、add、commit、push、pull、log、branch、checkout、stash、reset 等所有常用 Git 命令。自动解析输出为结构化报告。",
|
||
inputSchema: {
|
||
type: "object",
|
||
properties: {
|
||
project_path: {
|
||
type: "string",
|
||
description: "项目根目录(绝对路径)",
|
||
},
|
||
action: {
|
||
type: "string",
|
||
description: "Git 操作",
|
||
enum: [
|
||
"status", "diff", "diff_staged", "add", "add_all", "commit",
|
||
"push", "pull", "log", "branch", "branch_create", "checkout",
|
||
"stash", "stash_pop", "reset_soft", "reset_hard", "remote", "init",
|
||
],
|
||
},
|
||
message: {
|
||
type: "string",
|
||
description: "commit 消息(commit 时必填)",
|
||
},
|
||
target: {
|
||
type: "string",
|
||
description: "目标参数(文件路径/分支名/commit hash 等)",
|
||
},
|
||
count: {
|
||
type: "number",
|
||
description: "log 显示条数(默认 10)",
|
||
},
|
||
},
|
||
required: ["project_path", "action"],
|
||
},
|
||
};
|
||
async function git(args, cwd) {
|
||
try {
|
||
const { stdout, stderr } = await execAsync(`git ${args}`, {
|
||
cwd,
|
||
timeout: 30000,
|
||
maxBuffer: 1024 * 1024 * 5,
|
||
});
|
||
return { stdout, stderr, code: 0 };
|
||
}
|
||
catch (error) {
|
||
return { stdout: error.stdout || "", stderr: error.stderr || "", code: error.code ?? 1 };
|
||
}
|
||
}
|
||
export async function executeGitOps(args) {
|
||
const { project_path, action, message, target, count = 10 } = args;
|
||
let result;
|
||
let title = "";
|
||
switch (action) {
|
||
case "status":
|
||
title = "Git Status";
|
||
result = await git("status --short --branch", project_path);
|
||
if (result.code === 0) {
|
||
const lines = result.stdout.trim().split("\n");
|
||
const branch = lines[0] || "";
|
||
const changes = lines.slice(1);
|
||
const staged = changes.filter((l) => /^[MADRC]/.test(l));
|
||
const unstaged = changes.filter((l) => /^.[MADRC]/.test(l));
|
||
const untracked = changes.filter((l) => l.startsWith("??"));
|
||
return [
|
||
`# ${title}`, `📂 ${project_path}`, `🌿 ${branch}`, ``,
|
||
`| 状态 | 数量 |`, `|------|------|`,
|
||
`| ✅ 已暂存 | ${staged.length} |`,
|
||
`| 📝 已修改 | ${unstaged.length} |`,
|
||
`| ❓ 未跟踪 | ${untracked.length} |`, ``,
|
||
changes.length > 0 ? "```\n" + result.stdout.trim() + "\n```" : "✨ 工作区干净",
|
||
].join("\n");
|
||
}
|
||
break;
|
||
case "diff":
|
||
title = "Git Diff (工作区)";
|
||
result = await git(`diff ${target ? `-- "${target}"` : ""} --stat`, project_path);
|
||
if (result.stdout.trim()) {
|
||
const detail = await git(`diff ${target ? `-- "${target}"` : ""} --no-color`, project_path);
|
||
return `# ${title}\n\n## 摘要\n\`\`\`\n${result.stdout.trim()}\n\`\`\`\n\n## 详细\n\`\`\`diff\n${detail.stdout.slice(0, 8000)}\n\`\`\``;
|
||
}
|
||
return `# ${title}\n\n✨ 无差异`;
|
||
case "diff_staged":
|
||
title = "Git Diff (暂存区)";
|
||
result = await git("diff --cached --stat", project_path);
|
||
if (result.stdout.trim()) {
|
||
const detail = await git("diff --cached --no-color", project_path);
|
||
return `# ${title}\n\n## 摘要\n\`\`\`\n${result.stdout.trim()}\n\`\`\`\n\n## 详细\n\`\`\`diff\n${detail.stdout.slice(0, 8000)}\n\`\`\``;
|
||
}
|
||
return `# ${title}\n\n✨ 暂存区无内容`;
|
||
case "add":
|
||
title = "Git Add";
|
||
result = await git(`add "${target || "."}"`, project_path);
|
||
break;
|
||
case "add_all":
|
||
title = "Git Add All";
|
||
result = await git("add -A", project_path);
|
||
break;
|
||
case "commit":
|
||
if (!message)
|
||
return "❌ commit 需要提供 message 参数";
|
||
title = "Git Commit";
|
||
result = await git(`commit -m "${message.replace(/"/g, '\\"')}"`, project_path);
|
||
break;
|
||
case "push":
|
||
title = "Git Push";
|
||
result = await git(`push ${target || ""}`, project_path);
|
||
break;
|
||
case "pull":
|
||
title = "Git Pull";
|
||
result = await git(`pull ${target || ""}`, project_path);
|
||
break;
|
||
case "log":
|
||
title = "Git Log";
|
||
result = await git(`log --oneline --graph --decorate -n ${count}`, project_path);
|
||
if (result.code === 0) {
|
||
return `# ${title} (最近 ${count} 条)\n\n\`\`\`\n${result.stdout.trim()}\n\`\`\``;
|
||
}
|
||
break;
|
||
case "branch":
|
||
title = "Git Branch";
|
||
result = await git("branch -a -v", project_path);
|
||
break;
|
||
case "branch_create":
|
||
if (!target)
|
||
return "❌ 创建分支需要提供 target(分支名)";
|
||
title = `Git Branch Create: ${target}`;
|
||
result = await git(`checkout -b "${target}"`, project_path);
|
||
break;
|
||
case "checkout":
|
||
if (!target)
|
||
return "❌ checkout 需要提供 target(分支名或文件)";
|
||
title = `Git Checkout: ${target}`;
|
||
result = await git(`checkout "${target}"`, project_path);
|
||
break;
|
||
case "stash":
|
||
title = "Git Stash";
|
||
result = await git(`stash${message ? ` push -m "${message}"` : ""}`, project_path);
|
||
break;
|
||
case "stash_pop":
|
||
title = "Git Stash Pop";
|
||
result = await git("stash pop", project_path);
|
||
break;
|
||
case "reset_soft":
|
||
title = "Git Reset (soft)";
|
||
result = await git(`reset --soft ${target || "HEAD~1"}`, project_path);
|
||
break;
|
||
case "reset_hard":
|
||
title = "Git Reset (hard) ⚠️";
|
||
result = await git(`reset --hard ${target || "HEAD"}`, project_path);
|
||
break;
|
||
case "remote":
|
||
title = "Git Remote";
|
||
result = await git("remote -v", project_path);
|
||
break;
|
||
case "init":
|
||
title = "Git Init";
|
||
result = await git("init", project_path);
|
||
break;
|
||
default:
|
||
return `❌ 未知 Git 操作: ${action}`;
|
||
}
|
||
const output = [result.stdout, result.stderr].filter(Boolean).join("\n").trim();
|
||
const icon = result.code === 0 ? "✅" : "❌";
|
||
return `# ${icon} ${title}\n\n📂 ${project_path}\n\n\`\`\`\n${output || "(无输出)"}\n\`\`\``;
|
||
}
|
||
//# sourceMappingURL=gitOps.js.map
|