@cyanheads/git-mcp-server 2.1.4 → 2.1.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -2
- package/dist/mcp-server/tools/gitAdd/logic.js +16 -45
- package/dist/mcp-server/tools/gitBranch/logic.js +39 -34
- package/dist/mcp-server/tools/gitCheckout/logic.js +17 -12
- package/dist/mcp-server/tools/gitCherryPick/logic.js +22 -15
- package/dist/mcp-server/tools/gitClean/logic.js +11 -8
- package/dist/mcp-server/tools/gitClone/logic.js +15 -11
- package/dist/mcp-server/tools/gitCommit/logic.js +29 -65
- package/dist/mcp-server/tools/gitDiff/logic.js +29 -14
- package/dist/mcp-server/tools/gitFetch/logic.js +13 -12
- package/dist/mcp-server/tools/gitInit/logic.js +12 -9
- package/dist/mcp-server/tools/gitLog/logic.js +17 -30
- package/dist/mcp-server/tools/gitMerge/logic.js +17 -12
- package/dist/mcp-server/tools/gitPull/logic.js +13 -14
- package/dist/mcp-server/tools/gitPush/logic.js +19 -21
- package/dist/mcp-server/tools/gitRebase/logic.js +29 -20
- package/dist/mcp-server/tools/gitRemote/logic.js +15 -15
- package/dist/mcp-server/tools/gitReset/logic.js +11 -10
- package/dist/mcp-server/tools/gitSetWorkingDir/logic.js +6 -4
- package/dist/mcp-server/tools/gitShow/logic.js +10 -7
- package/dist/mcp-server/tools/gitStash/logic.js +16 -17
- package/dist/mcp-server/tools/gitStatus/logic.js +16 -8
- package/dist/mcp-server/tools/gitTag/logic.js +15 -15
- package/dist/mcp-server/tools/gitWorktree/logic.js +54 -38
- package/package.json +9 -20
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { execFile } from "child_process";
|
|
2
2
|
import { promisify } from "util";
|
|
3
3
|
import { z } from "zod";
|
|
4
4
|
// Import utils from barrel (logger from ../utils/internal/logger.js)
|
|
@@ -7,7 +7,7 @@ import { logger } from "../../../utils/index.js";
|
|
|
7
7
|
import { BaseErrorCode, McpError } from "../../../types-global/errors.js"; // Keep direct import for types-global
|
|
8
8
|
// Import utils from barrel (sanitization from ../utils/security/sanitization.js)
|
|
9
9
|
import { sanitization } from "../../../utils/index.js";
|
|
10
|
-
const
|
|
10
|
+
const execFileAsync = promisify(execFile);
|
|
11
11
|
// Define the base input schema without refinement
|
|
12
12
|
const GitDiffInputBaseSchema = z.object({
|
|
13
13
|
path: z
|
|
@@ -98,30 +98,32 @@ export async function diffGitChanges(input, context) {
|
|
|
98
98
|
let untrackedFilesCount = 0;
|
|
99
99
|
try {
|
|
100
100
|
// Construct the standard git diff command
|
|
101
|
-
|
|
101
|
+
const standardDiffArgs = ["-C", targetPath, "diff"];
|
|
102
102
|
if (input.staged) {
|
|
103
|
-
|
|
103
|
+
standardDiffArgs.push("--staged"); // Or --cached
|
|
104
104
|
}
|
|
105
105
|
else {
|
|
106
106
|
// Add commit references if not doing staged diff
|
|
107
107
|
if (safeCommit1) {
|
|
108
|
-
|
|
108
|
+
standardDiffArgs.push(safeCommit1);
|
|
109
109
|
}
|
|
110
110
|
if (safeCommit2) {
|
|
111
|
-
|
|
111
|
+
standardDiffArgs.push(safeCommit2);
|
|
112
112
|
}
|
|
113
113
|
}
|
|
114
114
|
// Add file path limiter if provided for standard diff
|
|
115
115
|
// Note: `input.file` will not apply to the untracked files part unless we explicitly filter them.
|
|
116
116
|
// For simplicity, `includeUntracked` will show all untracked files if `input.file` is also set.
|
|
117
117
|
if (safeFile) {
|
|
118
|
-
|
|
118
|
+
standardDiffArgs.push("--", safeFile); // Use '--' to separate paths from revisions
|
|
119
119
|
}
|
|
120
|
-
logger.debug(`Executing standard diff command: ${
|
|
120
|
+
logger.debug(`Executing standard diff command: git ${standardDiffArgs.join(" ")}`, {
|
|
121
121
|
...context,
|
|
122
122
|
operation,
|
|
123
123
|
});
|
|
124
|
-
const { stdout: standardStdout, stderr: standardStderr } = await
|
|
124
|
+
const { stdout: standardStdout, stderr: standardStderr } = await execFileAsync("git", standardDiffArgs, {
|
|
125
|
+
maxBuffer: 1024 * 1024 * 20,
|
|
126
|
+
});
|
|
125
127
|
if (standardStderr) {
|
|
126
128
|
logger.warning(`Git diff (standard) stderr: ${standardStderr}`, {
|
|
127
129
|
...context,
|
|
@@ -132,9 +134,15 @@ export async function diffGitChanges(input, context) {
|
|
|
132
134
|
// Handle untracked files if requested
|
|
133
135
|
if (input.includeUntracked) {
|
|
134
136
|
logger.debug("Including untracked files.", { ...context, operation });
|
|
135
|
-
const
|
|
137
|
+
const listUntrackedArgs = [
|
|
138
|
+
"-C",
|
|
139
|
+
targetPath,
|
|
140
|
+
"ls-files",
|
|
141
|
+
"--others",
|
|
142
|
+
"--exclude-standard",
|
|
143
|
+
];
|
|
136
144
|
try {
|
|
137
|
-
const { stdout: untrackedFilesStdOut } = await
|
|
145
|
+
const { stdout: untrackedFilesStdOut } = await execFileAsync("git", listUntrackedArgs);
|
|
138
146
|
const untrackedFiles = untrackedFilesStdOut
|
|
139
147
|
.trim()
|
|
140
148
|
.split("\n")
|
|
@@ -152,10 +160,17 @@ export async function diffGitChanges(input, context) {
|
|
|
152
160
|
// Skip if file path becomes empty after sanitization (unlikely but safe)
|
|
153
161
|
if (!safeUntrackedFile)
|
|
154
162
|
continue;
|
|
155
|
-
const
|
|
156
|
-
|
|
163
|
+
const untrackedDiffArgs = [
|
|
164
|
+
"-C",
|
|
165
|
+
targetPath,
|
|
166
|
+
"diff",
|
|
167
|
+
"--no-index",
|
|
168
|
+
"/dev/null",
|
|
169
|
+
safeUntrackedFile,
|
|
170
|
+
];
|
|
171
|
+
logger.debug(`Executing diff for untracked file: git ${untrackedDiffArgs.join(" ")}`, { ...context, operation, file: safeUntrackedFile });
|
|
157
172
|
try {
|
|
158
|
-
const { stdout: untrackedFileDiffOut } = await
|
|
173
|
+
const { stdout: untrackedFileDiffOut } = await execFileAsync("git", untrackedDiffArgs);
|
|
159
174
|
individualUntrackedDiffs += untrackedFileDiffOut;
|
|
160
175
|
untrackedFilesCount++;
|
|
161
176
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { execFile } from "child_process";
|
|
2
2
|
import { promisify } from "util";
|
|
3
3
|
import { z } from "zod";
|
|
4
4
|
// Import utils from barrel (logger from ../utils/internal/logger.js)
|
|
@@ -7,7 +7,7 @@ import { logger } from "../../../utils/index.js";
|
|
|
7
7
|
import { BaseErrorCode, McpError } from "../../../types-global/errors.js"; // Keep direct import for types-global
|
|
8
8
|
// Import utils from barrel (sanitization from ../utils/security/sanitization.js)
|
|
9
9
|
import { sanitization } from "../../../utils/index.js";
|
|
10
|
-
const
|
|
10
|
+
const execFileAsync = promisify(execFile);
|
|
11
11
|
// Define the input schema for the git_fetch tool using Zod
|
|
12
12
|
export const GitFetchInputSchema = z.object({
|
|
13
13
|
path: z
|
|
@@ -76,27 +76,28 @@ export async function fetchGitRemote(input, context) {
|
|
|
76
76
|
throw error;
|
|
77
77
|
throw new McpError(BaseErrorCode.VALIDATION_ERROR, `Invalid path: ${error instanceof Error ? error.message : String(error)}`, { context, operation, originalError: error });
|
|
78
78
|
}
|
|
79
|
-
// Basic sanitization for remote name
|
|
80
|
-
const safeRemote = input.remote?.replace(/[^a-zA-Z0-9_.\-/]/g, "");
|
|
81
79
|
try {
|
|
82
80
|
// Construct the git fetch command
|
|
83
|
-
|
|
81
|
+
const args = ["-C", targetPath, "fetch"];
|
|
84
82
|
if (input.prune) {
|
|
85
|
-
|
|
83
|
+
args.push("--prune");
|
|
86
84
|
}
|
|
87
85
|
if (input.tags) {
|
|
88
|
-
|
|
86
|
+
args.push("--tags");
|
|
89
87
|
}
|
|
90
88
|
if (input.all) {
|
|
91
|
-
|
|
89
|
+
args.push("--all");
|
|
92
90
|
}
|
|
93
|
-
else if (
|
|
94
|
-
|
|
91
|
+
else if (input.remote) {
|
|
92
|
+
args.push(input.remote); // Fetch specific remote if 'all' is not used
|
|
95
93
|
}
|
|
96
94
|
// If neither 'all' nor 'remote' is specified, git fetch defaults to 'origin' or configured upstream.
|
|
97
|
-
logger.debug(`Executing command: ${
|
|
95
|
+
logger.debug(`Executing command: git ${args.join(" ")}`, {
|
|
96
|
+
...context,
|
|
97
|
+
operation,
|
|
98
|
+
});
|
|
98
99
|
// Execute command. Fetch output is primarily on stderr.
|
|
99
|
-
const { stdout, stderr } = await
|
|
100
|
+
const { stdout, stderr } = await execFileAsync("git", args);
|
|
100
101
|
logger.debug(`Git fetch stdout: ${stdout}`, { ...context, operation }); // stdout is usually empty
|
|
101
102
|
if (stderr) {
|
|
102
103
|
logger.debug(`Git fetch stderr: ${stderr}`, { ...context, operation }); // stderr contains fetch details
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { execFile } from "child_process";
|
|
2
2
|
import fs from "fs/promises";
|
|
3
3
|
import path from "path";
|
|
4
4
|
import { promisify } from "util";
|
|
@@ -9,7 +9,7 @@ import { logger } from "../../../utils/index.js";
|
|
|
9
9
|
import { BaseErrorCode, McpError } from "../../../types-global/errors.js"; // Keep direct import for types-global
|
|
10
10
|
// Import utils from barrel (sanitization from ../utils/security/sanitization.js)
|
|
11
11
|
import { sanitization } from "../../../utils/index.js";
|
|
12
|
-
const
|
|
12
|
+
const execFileAsync = promisify(execFile);
|
|
13
13
|
// Define the input schema for the git_init tool using Zod
|
|
14
14
|
export const GitInitInputSchema = z.object({
|
|
15
15
|
path: z
|
|
@@ -83,20 +83,23 @@ export async function gitInitLogic(input, context) {
|
|
|
83
83
|
}
|
|
84
84
|
try {
|
|
85
85
|
// Construct the git init command
|
|
86
|
-
|
|
86
|
+
const args = ["init"];
|
|
87
87
|
if (input.quiet) {
|
|
88
|
-
|
|
88
|
+
args.push("--quiet");
|
|
89
89
|
}
|
|
90
90
|
if (input.bare) {
|
|
91
|
-
|
|
91
|
+
args.push("--bare");
|
|
92
92
|
}
|
|
93
93
|
// Determine the initial branch name, defaulting to 'main' if not provided
|
|
94
94
|
const branchNameToUse = input.initialBranch || "main";
|
|
95
|
-
|
|
95
|
+
args.push("-b", branchNameToUse);
|
|
96
96
|
// Add the target directory path at the end
|
|
97
|
-
|
|
98
|
-
logger.debug(`Executing command: ${
|
|
99
|
-
|
|
97
|
+
args.push(targetPath);
|
|
98
|
+
logger.debug(`Executing command: git ${args.join(" ")}`, {
|
|
99
|
+
...context,
|
|
100
|
+
operation,
|
|
101
|
+
});
|
|
102
|
+
const { stdout, stderr } = await execFileAsync("git", args);
|
|
100
103
|
if (stderr && !input.quiet) {
|
|
101
104
|
// Log stderr as warning but proceed, as init might still succeed (e.g., reinitializing)
|
|
102
105
|
logger.warning(`Git init command produced stderr`, {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { execFile } from "child_process";
|
|
2
2
|
import { promisify } from "util";
|
|
3
3
|
import { z } from "zod";
|
|
4
4
|
// Import utils from barrel (logger from ../utils/internal/logger.js)
|
|
@@ -7,7 +7,7 @@ import { logger } from "../../../utils/index.js";
|
|
|
7
7
|
import { BaseErrorCode, McpError } from "../../../types-global/errors.js"; // Keep direct import for types-global
|
|
8
8
|
// Import utils from barrel (sanitization from ../utils/security/sanitization.js)
|
|
9
9
|
import { sanitization } from "../../../utils/index.js";
|
|
10
|
-
const
|
|
10
|
+
const execFileAsync = promisify(execFile);
|
|
11
11
|
// Define the structure for a single commit entry
|
|
12
12
|
export const CommitEntrySchema = z.object({
|
|
13
13
|
hash: z.string().describe("Full commit hash"),
|
|
@@ -107,53 +107,40 @@ export async function logGitHistory(input, context) {
|
|
|
107
107
|
throw new McpError(BaseErrorCode.VALIDATION_ERROR, `Invalid path: ${error instanceof Error ? error.message : String(error)}`, { context, operation, originalError: error });
|
|
108
108
|
}
|
|
109
109
|
try {
|
|
110
|
-
|
|
110
|
+
const args = ["-C", targetPath, "log"];
|
|
111
111
|
let isRawOutput = false; // Flag to indicate if we should parse or return raw
|
|
112
112
|
if (input.showSignature) {
|
|
113
113
|
isRawOutput = true;
|
|
114
|
-
|
|
114
|
+
args.push("--show-signature");
|
|
115
115
|
logger.info("Show signature requested, returning raw output.", {
|
|
116
116
|
...context,
|
|
117
117
|
operation,
|
|
118
118
|
});
|
|
119
|
-
// Append other filters if provided
|
|
120
|
-
if (input.maxCount)
|
|
121
|
-
command += ` -n ${input.maxCount}`;
|
|
122
|
-
if (input.author)
|
|
123
|
-
command += ` --author="${input.author.replace(/[`"$&;*()|<>]/g, "")}"`;
|
|
124
|
-
if (input.since)
|
|
125
|
-
command += ` --since="${input.since.replace(/[`"$&;*()|<>]/g, "")}"`;
|
|
126
|
-
if (input.until)
|
|
127
|
-
command += ` --until="${input.until.replace(/[`"$&;*()|<>]/g, "")}"`;
|
|
128
|
-
if (input.branchOrFile)
|
|
129
|
-
command += ` ${input.branchOrFile.replace(/[`"$&;*()|<>]/g, "")}`;
|
|
130
119
|
}
|
|
131
120
|
else {
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
121
|
+
args.push(GIT_LOG_FORMAT);
|
|
122
|
+
}
|
|
123
|
+
if (input.maxCount) {
|
|
124
|
+
args.push(`-n${input.maxCount}`);
|
|
136
125
|
}
|
|
137
126
|
if (input.author) {
|
|
138
|
-
|
|
139
|
-
const safeAuthor = input.author.replace(/[`"$&;*()|<>]/g, "");
|
|
140
|
-
command += ` --author="${safeAuthor}"`;
|
|
127
|
+
args.push(`--author=${input.author}`);
|
|
141
128
|
}
|
|
142
129
|
if (input.since) {
|
|
143
|
-
|
|
144
|
-
command += ` --since="${safeSince}"`;
|
|
130
|
+
args.push(`--since=${input.since}`);
|
|
145
131
|
}
|
|
146
132
|
if (input.until) {
|
|
147
|
-
|
|
148
|
-
command += ` --until="${safeUntil}"`;
|
|
133
|
+
args.push(`--until=${input.until}`);
|
|
149
134
|
}
|
|
150
135
|
if (input.branchOrFile) {
|
|
151
|
-
|
|
152
|
-
command += ` ${safeBranchOrFile}`; // Add branch or file path at the end
|
|
136
|
+
args.push(input.branchOrFile);
|
|
153
137
|
}
|
|
154
|
-
logger.debug(`Executing command: ${
|
|
138
|
+
logger.debug(`Executing command: git ${args.join(" ")}`, {
|
|
139
|
+
...context,
|
|
140
|
+
operation,
|
|
141
|
+
});
|
|
155
142
|
// Increase maxBuffer if logs can be large
|
|
156
|
-
const { stdout, stderr } = await
|
|
143
|
+
const { stdout, stderr } = await execFileAsync("git", args, {
|
|
157
144
|
maxBuffer: 1024 * 1024 * 10,
|
|
158
145
|
}); // 10MB buffer
|
|
159
146
|
if (stderr) {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { execFile } from "child_process";
|
|
2
2
|
import { promisify } from "util";
|
|
3
3
|
import { z } from "zod";
|
|
4
4
|
// Import utils from barrel (logger from ../utils/internal/logger.js)
|
|
@@ -8,7 +8,7 @@ import { BaseErrorCode, McpError } from "../../../types-global/errors.js"; // Ke
|
|
|
8
8
|
// Import utils from barrel (sanitization from ../utils/security/sanitization.js)
|
|
9
9
|
import path from "path"; // Import path module
|
|
10
10
|
import { sanitization } from "../../../utils/index.js";
|
|
11
|
-
const
|
|
11
|
+
const execFileAsync = promisify(execFile);
|
|
12
12
|
// Define the input schema for the git_merge tool
|
|
13
13
|
export const GitMergeInputSchema = z.object({
|
|
14
14
|
path: z
|
|
@@ -107,29 +107,34 @@ export async function gitMergeLogic(input, context) {
|
|
|
107
107
|
throw new McpError(BaseErrorCode.VALIDATION_ERROR, `Invalid path: ${error instanceof Error ? error.message : String(error)}`, { context, operation, originalError: error });
|
|
108
108
|
}
|
|
109
109
|
// --- Construct the git merge command ---
|
|
110
|
-
|
|
110
|
+
const args = ["-C", targetPath, "merge"];
|
|
111
111
|
if (input.abort) {
|
|
112
|
-
|
|
112
|
+
args.push("--abort");
|
|
113
113
|
}
|
|
114
114
|
else {
|
|
115
115
|
// Standard merge options
|
|
116
|
-
if (input.noFf)
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
116
|
+
if (input.noFf) {
|
|
117
|
+
args.push("--no-ff");
|
|
118
|
+
}
|
|
119
|
+
if (input.squash) {
|
|
120
|
+
args.push("--squash");
|
|
121
|
+
}
|
|
120
122
|
if (input.commitMessage && !input.squash) {
|
|
121
123
|
// Commit message only relevant if not squashing (squash requires separate commit)
|
|
122
|
-
|
|
124
|
+
args.push("-m", input.commitMessage);
|
|
123
125
|
}
|
|
124
126
|
else if (input.squash && input.commitMessage) {
|
|
125
127
|
logger.warning("Commit message provided with --squash, but it will be ignored. Squash requires a separate commit.", { ...context, operation });
|
|
126
128
|
}
|
|
127
|
-
|
|
129
|
+
args.push(input.branch); // Add branch to merge
|
|
128
130
|
}
|
|
129
|
-
logger.debug(`Executing command: ${
|
|
131
|
+
logger.debug(`Executing command: git ${args.join(" ")}`, {
|
|
132
|
+
...context,
|
|
133
|
+
operation,
|
|
134
|
+
});
|
|
130
135
|
// --- Execute and Parse ---
|
|
131
136
|
try {
|
|
132
|
-
const { stdout, stderr } = await
|
|
137
|
+
const { stdout, stderr } = await execFileAsync("git", args);
|
|
133
138
|
logger.debug(`Command stdout: ${stdout}`, { ...context, operation });
|
|
134
139
|
if (stderr)
|
|
135
140
|
logger.debug(`Command stderr: ${stderr}`, { ...context, operation }); // Log stderr even on success
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { execFile } from "child_process";
|
|
2
2
|
import { promisify } from "util";
|
|
3
3
|
import { z } from "zod";
|
|
4
4
|
// Import utils from barrel (logger from ../utils/internal/logger.js)
|
|
@@ -7,7 +7,7 @@ import { logger } from "../../../utils/index.js";
|
|
|
7
7
|
import { BaseErrorCode, McpError } from "../../../types-global/errors.js"; // Keep direct import for types-global
|
|
8
8
|
// Import utils from barrel (sanitization from ../utils/security/sanitization.js)
|
|
9
9
|
import { sanitization } from "../../../utils/index.js";
|
|
10
|
-
const
|
|
10
|
+
const execFileAsync = promisify(execFile);
|
|
11
11
|
// Define the input schema for the git_pull tool using Zod
|
|
12
12
|
export const GitPullInputSchema = z.object({
|
|
13
13
|
path: z
|
|
@@ -91,30 +91,29 @@ export async function pullGitChanges(input, context) {
|
|
|
91
91
|
}
|
|
92
92
|
try {
|
|
93
93
|
// Construct the git pull command
|
|
94
|
-
|
|
94
|
+
const args = ["-C", targetPath, "pull"];
|
|
95
95
|
if (input.rebase) {
|
|
96
|
-
|
|
96
|
+
args.push("--rebase");
|
|
97
97
|
}
|
|
98
98
|
if (input.ffOnly) {
|
|
99
|
-
|
|
99
|
+
args.push("--ff-only");
|
|
100
100
|
}
|
|
101
101
|
if (input.remote) {
|
|
102
|
-
|
|
103
|
-
const safeRemote = input.remote.replace(/[^a-zA-Z0-9_.\-/]/g, "");
|
|
104
|
-
command += ` ${safeRemote}`;
|
|
102
|
+
args.push(input.remote);
|
|
105
103
|
if (input.branch) {
|
|
106
|
-
|
|
107
|
-
command += ` ${safeBranch}`;
|
|
104
|
+
args.push(input.branch);
|
|
108
105
|
}
|
|
109
106
|
}
|
|
110
107
|
else if (input.branch) {
|
|
111
108
|
// If only branch is specified, assume 'origin' or tracked remote
|
|
112
|
-
|
|
113
|
-
command += ` origin ${safeBranch}`; // Defaulting to origin if remote not specified but branch is
|
|
109
|
+
args.push("origin", input.branch); // Defaulting to origin if remote not specified but branch is
|
|
114
110
|
logger.warning(`Remote not specified, defaulting to 'origin' for branch pull`, { ...context, operation });
|
|
115
111
|
}
|
|
116
|
-
logger.debug(`Executing command: ${
|
|
117
|
-
|
|
112
|
+
logger.debug(`Executing command: git ${args.join(" ")}`, {
|
|
113
|
+
...context,
|
|
114
|
+
operation,
|
|
115
|
+
});
|
|
116
|
+
const { stdout, stderr } = await execFileAsync("git", args);
|
|
118
117
|
logger.debug(`Git pull stdout: ${stdout}`, { ...context, operation });
|
|
119
118
|
if (stderr) {
|
|
120
119
|
logger.debug(`Git pull stderr: ${stderr}`, { ...context, operation });
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { execFile } from "child_process";
|
|
2
2
|
import { promisify } from "util";
|
|
3
3
|
import { z } from "zod";
|
|
4
4
|
// Import utils from barrel (logger from ../utils/internal/logger.js)
|
|
@@ -7,7 +7,7 @@ import { logger } from "../../../utils/index.js";
|
|
|
7
7
|
import { BaseErrorCode, McpError } from "../../../types-global/errors.js"; // Keep direct import for types-global
|
|
8
8
|
// Import utils from barrel (sanitization from ../utils/security/sanitization.js)
|
|
9
9
|
import { sanitization } from "../../../utils/index.js";
|
|
10
|
-
const
|
|
10
|
+
const execFileAsync = promisify(execFile);
|
|
11
11
|
// Define the input schema for the git_push tool using Zod
|
|
12
12
|
export const GitPushInputSchema = z.object({
|
|
13
13
|
path: z
|
|
@@ -111,45 +111,43 @@ export async function pushGitChanges(input, context) {
|
|
|
111
111
|
}
|
|
112
112
|
try {
|
|
113
113
|
// Construct the git push command
|
|
114
|
-
|
|
114
|
+
const args = ["-C", targetPath, "push"];
|
|
115
115
|
if (input.force) {
|
|
116
|
-
|
|
116
|
+
args.push("--force");
|
|
117
117
|
}
|
|
118
118
|
else if (input.forceWithLease) {
|
|
119
|
-
|
|
119
|
+
args.push("--force-with-lease");
|
|
120
120
|
}
|
|
121
121
|
if (input.setUpstream) {
|
|
122
|
-
|
|
122
|
+
args.push("--set-upstream");
|
|
123
123
|
}
|
|
124
124
|
if (input.tags) {
|
|
125
|
-
|
|
125
|
+
args.push("--tags");
|
|
126
126
|
}
|
|
127
127
|
if (input.delete) {
|
|
128
|
-
|
|
128
|
+
args.push("--delete");
|
|
129
129
|
}
|
|
130
130
|
// Add remote and branch specification
|
|
131
|
-
const remote = input.remote
|
|
132
|
-
|
|
133
|
-
: "origin"; // Default to origin
|
|
134
|
-
command += ` ${remote}`;
|
|
131
|
+
const remote = input.remote || "origin"; // Default to origin
|
|
132
|
+
args.push(remote);
|
|
135
133
|
if (input.branch) {
|
|
136
|
-
const localBranch = input.branch.replace(/[^a-zA-Z0-9_.\-/]/g, "");
|
|
137
|
-
command += ` ${localBranch}`;
|
|
138
134
|
if (input.remoteBranch && !input.delete) {
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
135
|
+
args.push(`${input.branch}:${input.remoteBranch}`);
|
|
136
|
+
}
|
|
137
|
+
else {
|
|
138
|
+
args.push(input.branch);
|
|
142
139
|
}
|
|
143
140
|
}
|
|
144
141
|
else if (!input.tags && !input.delete) {
|
|
145
142
|
// If no branch, tags, or delete specified, push the current branch by default
|
|
146
|
-
// Git might handle this automatically, but being explicit can be clearer
|
|
147
|
-
// command += ' HEAD'; // Or let git figure out the default push behavior
|
|
148
143
|
logger.debug("No specific branch, tags, or delete specified. Relying on default git push behavior for current branch.", { ...context, operation });
|
|
149
144
|
}
|
|
150
|
-
logger.debug(`Executing command: ${
|
|
145
|
+
logger.debug(`Executing command: git ${args.join(" ")}`, {
|
|
146
|
+
...context,
|
|
147
|
+
operation,
|
|
148
|
+
});
|
|
151
149
|
// Execute command. Note: Git push often uses stderr for progress and success messages.
|
|
152
|
-
const { stdout, stderr } = await
|
|
150
|
+
const { stdout, stderr } = await execFileAsync("git", args);
|
|
153
151
|
logger.debug(`Git push stdout: ${stdout}`, { ...context, operation });
|
|
154
152
|
if (stderr) {
|
|
155
153
|
logger.debug(`Git push stderr: ${stderr}`, { ...context, operation });
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { execFile } from "child_process";
|
|
2
2
|
import { promisify } from "util";
|
|
3
3
|
import { z } from "zod";
|
|
4
4
|
// Import utils from barrel (logger from ../utils/internal/logger.js)
|
|
@@ -7,7 +7,7 @@ import { logger } from "../../../utils/index.js";
|
|
|
7
7
|
import { BaseErrorCode, McpError } from "../../../types-global/errors.js"; // Keep direct import for types-global
|
|
8
8
|
// Import utils from barrel (sanitization from ../utils/security/sanitization.js)
|
|
9
9
|
import { sanitization } from "../../../utils/index.js";
|
|
10
|
-
const
|
|
10
|
+
const execFileAsync = promisify(execFile);
|
|
11
11
|
// Define the BASE input schema for the git_rebase tool using Zod
|
|
12
12
|
export const GitRebaseBaseSchema = z.object({
|
|
13
13
|
path: z
|
|
@@ -117,39 +117,48 @@ export async function gitRebaseLogic(input, context) {
|
|
|
117
117
|
throw new McpError(BaseErrorCode.VALIDATION_ERROR, `Invalid path: ${error instanceof Error ? error.message : String(error)}`, { context, operation, originalError: error });
|
|
118
118
|
}
|
|
119
119
|
try {
|
|
120
|
-
|
|
120
|
+
const args = ["-C", targetPath, "rebase"];
|
|
121
121
|
switch (input.mode) {
|
|
122
122
|
case "start":
|
|
123
|
-
if (input.interactive)
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
if (input.
|
|
130
|
-
|
|
123
|
+
if (input.interactive) {
|
|
124
|
+
args.push("-i");
|
|
125
|
+
}
|
|
126
|
+
if (input.strategy) {
|
|
127
|
+
args.push(`--strategy=${input.strategy}`);
|
|
128
|
+
}
|
|
129
|
+
if (input.strategyOption) {
|
|
130
|
+
args.push(`-X${input.strategyOption}`);
|
|
131
|
+
} // Note: -X for strategy options
|
|
132
|
+
if (input.onto) {
|
|
133
|
+
args.push("--onto", input.onto);
|
|
134
|
+
}
|
|
131
135
|
// Upstream is required by refine unless interactive
|
|
132
|
-
if (input.upstream)
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
+
if (input.upstream) {
|
|
137
|
+
args.push(input.upstream);
|
|
138
|
+
}
|
|
139
|
+
if (input.branch) {
|
|
140
|
+
args.push(input.branch);
|
|
141
|
+
}
|
|
136
142
|
break;
|
|
137
143
|
case "continue":
|
|
138
|
-
|
|
144
|
+
args.push("--continue");
|
|
139
145
|
break;
|
|
140
146
|
case "abort":
|
|
141
|
-
|
|
147
|
+
args.push("--abort");
|
|
142
148
|
break;
|
|
143
149
|
case "skip":
|
|
144
|
-
|
|
150
|
+
args.push("--skip");
|
|
145
151
|
break;
|
|
146
152
|
default:
|
|
147
153
|
// Should not happen due to Zod validation
|
|
148
154
|
throw new McpError(BaseErrorCode.VALIDATION_ERROR, `Invalid mode: ${input.mode}`, { context, operation });
|
|
149
155
|
}
|
|
150
|
-
logger.debug(`Executing command: ${
|
|
156
|
+
logger.debug(`Executing command: git ${args.join(" ")}`, {
|
|
157
|
+
...context,
|
|
158
|
+
operation,
|
|
159
|
+
});
|
|
151
160
|
try {
|
|
152
|
-
const { stdout, stderr } = await
|
|
161
|
+
const { stdout, stderr } = await execFileAsync("git", args);
|
|
153
162
|
const output = stdout + stderr;
|
|
154
163
|
const message = `Rebase ${input.mode} executed successfully. Output: ${output.trim()}`;
|
|
155
164
|
logger.info(message, { ...context, operation, path: targetPath });
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { execFile } from "child_process";
|
|
2
2
|
import { promisify } from "util";
|
|
3
3
|
import { z } from "zod";
|
|
4
4
|
import { BaseErrorCode, McpError } from "../../../types-global/errors.js"; // Direct import for types-global
|
|
5
5
|
import { logger, sanitization } from "../../../utils/index.js"; // Logger (./utils/internal/logger.js) & RequestContext (./utils/internal/requestContext.js) & sanitization (./utils/security/sanitization.js)
|
|
6
|
-
const
|
|
6
|
+
const execFileAsync = promisify(execFile);
|
|
7
7
|
// Define the input schema for the git_remote tool using Zod
|
|
8
8
|
export const GitRemoteInputSchema = z.object({
|
|
9
9
|
path: z
|
|
@@ -79,16 +79,16 @@ export async function gitRemoteLogic(input, context) {
|
|
|
79
79
|
throw new McpError(BaseErrorCode.VALIDATION_ERROR, `Invalid path: ${error instanceof Error ? error.message : String(error)}`, { context, operation, originalError: error });
|
|
80
80
|
}
|
|
81
81
|
try {
|
|
82
|
-
let
|
|
82
|
+
let args;
|
|
83
83
|
let result;
|
|
84
84
|
switch (input.mode) {
|
|
85
85
|
case "list":
|
|
86
|
-
|
|
87
|
-
logger.debug(`Executing command: ${
|
|
86
|
+
args = ["-C", targetPath, "remote", "-v"];
|
|
87
|
+
logger.debug(`Executing command: git ${args.join(" ")}`, {
|
|
88
88
|
...context,
|
|
89
89
|
operation,
|
|
90
90
|
});
|
|
91
|
-
const { stdout: listStdout } = await
|
|
91
|
+
const { stdout: listStdout } = await execFileAsync("git", args);
|
|
92
92
|
const remotes = [];
|
|
93
93
|
const lines = listStdout.trim().split("\n");
|
|
94
94
|
const remoteMap = new Map();
|
|
@@ -127,12 +127,12 @@ export async function gitRemoteLogic(input, context) {
|
|
|
127
127
|
if (!/^[a-zA-Z0-9_.-]+$/.test(input.name)) {
|
|
128
128
|
throw new McpError(BaseErrorCode.VALIDATION_ERROR, `Invalid remote name: ${input.name}`, { context, operation });
|
|
129
129
|
}
|
|
130
|
-
|
|
131
|
-
logger.debug(`Executing command: ${
|
|
130
|
+
args = ["-C", targetPath, "remote", "add", input.name, input.url];
|
|
131
|
+
logger.debug(`Executing command: git ${args.join(" ")}`, {
|
|
132
132
|
...context,
|
|
133
133
|
operation,
|
|
134
134
|
});
|
|
135
|
-
await
|
|
135
|
+
await execFileAsync("git", args);
|
|
136
136
|
result = {
|
|
137
137
|
success: true,
|
|
138
138
|
mode: "add",
|
|
@@ -146,12 +146,12 @@ export async function gitRemoteLogic(input, context) {
|
|
|
146
146
|
if (!/^[a-zA-Z0-9_.-]+$/.test(input.name)) {
|
|
147
147
|
throw new McpError(BaseErrorCode.VALIDATION_ERROR, `Invalid remote name: ${input.name}`, { context, operation });
|
|
148
148
|
}
|
|
149
|
-
|
|
150
|
-
logger.debug(`Executing command: ${
|
|
149
|
+
args = ["-C", targetPath, "remote", "remove", input.name];
|
|
150
|
+
logger.debug(`Executing command: git ${args.join(" ")}`, {
|
|
151
151
|
...context,
|
|
152
152
|
operation,
|
|
153
153
|
});
|
|
154
|
-
await
|
|
154
|
+
await execFileAsync("git", args);
|
|
155
155
|
result = {
|
|
156
156
|
success: true,
|
|
157
157
|
mode: "remove",
|
|
@@ -165,12 +165,12 @@ export async function gitRemoteLogic(input, context) {
|
|
|
165
165
|
if (!/^[a-zA-Z0-9_.-]+$/.test(input.name)) {
|
|
166
166
|
throw new McpError(BaseErrorCode.VALIDATION_ERROR, `Invalid remote name: ${input.name}`, { context, operation });
|
|
167
167
|
}
|
|
168
|
-
|
|
169
|
-
logger.debug(`Executing command: ${
|
|
168
|
+
args = ["-C", targetPath, "remote", "show", input.name];
|
|
169
|
+
logger.debug(`Executing command: git ${args.join(" ")}`, {
|
|
170
170
|
...context,
|
|
171
171
|
operation,
|
|
172
172
|
});
|
|
173
|
-
const { stdout: showStdout } = await
|
|
173
|
+
const { stdout: showStdout } = await execFileAsync("git", args);
|
|
174
174
|
result = { success: true, mode: "show", details: showStdout.trim() };
|
|
175
175
|
break;
|
|
176
176
|
default:
|