git-repo-explorer-mcp 0.1.0

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.
Files changed (122) hide show
  1. package/README.md +117 -0
  2. package/dist/git-repo-manager.d.ts +115 -0
  3. package/dist/git-repo-manager.d.ts.map +1 -0
  4. package/dist/git-repo-manager.js +316 -0
  5. package/dist/git-repo-manager.js.map +1 -0
  6. package/dist/index.d.ts +3 -0
  7. package/dist/index.d.ts.map +1 -0
  8. package/dist/index.js +7 -0
  9. package/dist/index.js.map +1 -0
  10. package/dist/operations/blame-ops.d.ts +4 -0
  11. package/dist/operations/blame-ops.d.ts.map +1 -0
  12. package/dist/operations/blame-ops.js +79 -0
  13. package/dist/operations/blame-ops.js.map +1 -0
  14. package/dist/operations/branch-ops.d.ts +4 -0
  15. package/dist/operations/branch-ops.d.ts.map +1 -0
  16. package/dist/operations/branch-ops.js +35 -0
  17. package/dist/operations/branch-ops.js.map +1 -0
  18. package/dist/operations/diff-ops.d.ts +4 -0
  19. package/dist/operations/diff-ops.d.ts.map +1 -0
  20. package/dist/operations/diff-ops.js +43 -0
  21. package/dist/operations/diff-ops.js.map +1 -0
  22. package/dist/operations/grep-ops.d.ts +4 -0
  23. package/dist/operations/grep-ops.d.ts.map +1 -0
  24. package/dist/operations/grep-ops.js +35 -0
  25. package/dist/operations/grep-ops.js.map +1 -0
  26. package/dist/operations/log-ops.d.ts +4 -0
  27. package/dist/operations/log-ops.d.ts.map +1 -0
  28. package/dist/operations/log-ops.js +55 -0
  29. package/dist/operations/log-ops.js.map +1 -0
  30. package/dist/operations/ls-files-ops.d.ts +4 -0
  31. package/dist/operations/ls-files-ops.d.ts.map +1 -0
  32. package/dist/operations/ls-files-ops.js +40 -0
  33. package/dist/operations/ls-files-ops.js.map +1 -0
  34. package/dist/operations/registry.d.ts +10 -0
  35. package/dist/operations/registry.d.ts.map +1 -0
  36. package/dist/operations/registry.js +40 -0
  37. package/dist/operations/registry.js.map +1 -0
  38. package/dist/operations/show-ops.d.ts +4 -0
  39. package/dist/operations/show-ops.d.ts.map +1 -0
  40. package/dist/operations/show-ops.js +37 -0
  41. package/dist/operations/show-ops.js.map +1 -0
  42. package/dist/operations/tag-ops.d.ts +4 -0
  43. package/dist/operations/tag-ops.d.ts.map +1 -0
  44. package/dist/operations/tag-ops.js +37 -0
  45. package/dist/operations/tag-ops.js.map +1 -0
  46. package/dist/operations/types.d.ts +15 -0
  47. package/dist/operations/types.d.ts.map +1 -0
  48. package/dist/operations/types.js +2 -0
  49. package/dist/operations/types.js.map +1 -0
  50. package/dist/server.d.ts +2 -0
  51. package/dist/server.d.ts.map +1 -0
  52. package/dist/server.js +180 -0
  53. package/dist/server.js.map +1 -0
  54. package/dist/services/git-executor.d.ts +12 -0
  55. package/dist/services/git-executor.d.ts.map +1 -0
  56. package/dist/services/git-executor.js +35 -0
  57. package/dist/services/git-executor.js.map +1 -0
  58. package/dist/services/repository-manager.d.ts +58 -0
  59. package/dist/services/repository-manager.d.ts.map +1 -0
  60. package/dist/services/repository-manager.js +297 -0
  61. package/dist/services/repository-manager.js.map +1 -0
  62. package/dist/tools/description.d.ts +7 -0
  63. package/dist/tools/description.d.ts.map +1 -0
  64. package/dist/tools/description.js +85 -0
  65. package/dist/tools/description.js.map +1 -0
  66. package/dist/tools/git/handlers/blame-handler.d.ts +8 -0
  67. package/dist/tools/git/handlers/blame-handler.d.ts.map +1 -0
  68. package/dist/tools/git/handlers/blame-handler.js +49 -0
  69. package/dist/tools/git/handlers/blame-handler.js.map +1 -0
  70. package/dist/tools/git/handlers/branches-handler.d.ts +8 -0
  71. package/dist/tools/git/handlers/branches-handler.d.ts.map +1 -0
  72. package/dist/tools/git/handlers/branches-handler.js +29 -0
  73. package/dist/tools/git/handlers/branches-handler.js.map +1 -0
  74. package/dist/tools/git/handlers/cat-file-handler.d.ts +8 -0
  75. package/dist/tools/git/handlers/cat-file-handler.d.ts.map +1 -0
  76. package/dist/tools/git/handlers/cat-file-handler.js +44 -0
  77. package/dist/tools/git/handlers/cat-file-handler.js.map +1 -0
  78. package/dist/tools/git/handlers/clone-handler.d.ts +8 -0
  79. package/dist/tools/git/handlers/clone-handler.d.ts.map +1 -0
  80. package/dist/tools/git/handlers/clone-handler.js +24 -0
  81. package/dist/tools/git/handlers/clone-handler.js.map +1 -0
  82. package/dist/tools/git/handlers/diff-handler.d.ts +8 -0
  83. package/dist/tools/git/handlers/diff-handler.d.ts.map +1 -0
  84. package/dist/tools/git/handlers/diff-handler.js +44 -0
  85. package/dist/tools/git/handlers/diff-handler.js.map +1 -0
  86. package/dist/tools/git/handlers/grep-handler.d.ts +8 -0
  87. package/dist/tools/git/handlers/grep-handler.d.ts.map +1 -0
  88. package/dist/tools/git/handlers/grep-handler.js +47 -0
  89. package/dist/tools/git/handlers/grep-handler.js.map +1 -0
  90. package/dist/tools/git/handlers/log-handler.d.ts +8 -0
  91. package/dist/tools/git/handlers/log-handler.d.ts.map +1 -0
  92. package/dist/tools/git/handlers/log-handler.js +43 -0
  93. package/dist/tools/git/handlers/log-handler.js.map +1 -0
  94. package/dist/tools/git/handlers/ls-files-handler.d.ts +8 -0
  95. package/dist/tools/git/handlers/ls-files-handler.d.ts.map +1 -0
  96. package/dist/tools/git/handlers/ls-files-handler.js +41 -0
  97. package/dist/tools/git/handlers/ls-files-handler.js.map +1 -0
  98. package/dist/tools/git/handlers/remove-handler.d.ts +8 -0
  99. package/dist/tools/git/handlers/remove-handler.d.ts.map +1 -0
  100. package/dist/tools/git/handlers/remove-handler.js +40 -0
  101. package/dist/tools/git/handlers/remove-handler.js.map +1 -0
  102. package/dist/tools/git/handlers/repos-handler.d.ts +8 -0
  103. package/dist/tools/git/handlers/repos-handler.d.ts.map +1 -0
  104. package/dist/tools/git/handlers/repos-handler.js +29 -0
  105. package/dist/tools/git/handlers/repos-handler.js.map +1 -0
  106. package/dist/tools/git/handlers/show-handler.d.ts +8 -0
  107. package/dist/tools/git/handlers/show-handler.d.ts.map +1 -0
  108. package/dist/tools/git/handlers/show-handler.js +41 -0
  109. package/dist/tools/git/handlers/show-handler.js.map +1 -0
  110. package/dist/tools/git/index.d.ts +8 -0
  111. package/dist/tools/git/index.d.ts.map +1 -0
  112. package/dist/tools/git/index.js +168 -0
  113. package/dist/tools/git/index.js.map +1 -0
  114. package/dist/types/index.d.ts +99 -0
  115. package/dist/types/index.d.ts.map +1 -0
  116. package/dist/types/index.js +2 -0
  117. package/dist/types/index.js.map +1 -0
  118. package/dist/utils/response-wrapper.d.ts +9 -0
  119. package/dist/utils/response-wrapper.d.ts.map +1 -0
  120. package/dist/utils/response-wrapper.js +32 -0
  121. package/dist/utils/response-wrapper.js.map +1 -0
  122. package/package.json +42 -0
package/README.md ADDED
@@ -0,0 +1,117 @@
1
+ # mcp-git-repo-explorer
2
+
3
+ MCP server for exploring git repositories with worktree support.
4
+
5
+ ## Features
6
+
7
+ - Clone repositories as bare repos for efficient storage
8
+ - Worktree support for working with multiple branches simultaneously
9
+ - Read-only operations: ls-files, grep, log, blame, show, diff, branches
10
+ - File content access via cat-file
11
+
12
+ ## Installation
13
+
14
+ ```bash
15
+ npm install mcp-git-repo-explorer
16
+ ```
17
+
18
+ ## Usage
19
+
20
+ ### As MCP Server
21
+
22
+ Add to your MCP configuration:
23
+
24
+ ```json
25
+ {
26
+ "mcpServers": {
27
+ "mcp-git-repo-explorer": {
28
+ "command": "npx",
29
+ "args": ["mcp-git-repo-explorer", "/tmp/git-repos", "--remind-mcp"]
30
+ }
31
+ }
32
+ }
33
+ ```
34
+
35
+ ### CLI Options
36
+
37
+ - `--base-dir <path>`: Base directory for storing repositories (default: `/tmp/mcp-git-<uuid>`)
38
+ - `--remind-mcp`: Add MCP usage reminder to responses
39
+ - `--remind-org <text>`: Add organization reminder
40
+ - `--remind-task <text>`: Add task reminder
41
+
42
+ ## Actions
43
+
44
+ ### Repository Management
45
+
46
+ | Action | Description | Parameters |
47
+ |--------|-------------|------------|
48
+ | `clone` | Clone a repository | `repository` (URL), `path` (optional name) |
49
+ | `repos` | List all repositories | - |
50
+ | `remove` | Remove repo or worktree | `repository`, `branch` (optional) |
51
+
52
+ ### File Operations
53
+
54
+ | Action | Description | Parameters |
55
+ |--------|-------------|------------|
56
+ | `ls-files` | List tracked files | `repository`, `branch`, `pattern` |
57
+ | `cat-file` | Show file content | `repository`, `file`, `branch`, `ref` |
58
+
59
+ ### Search
60
+
61
+ | Action | Description | Parameters |
62
+ |--------|-------------|------------|
63
+ | `grep` | Search in files | `repository`, `pattern`, `branch`, `path` |
64
+
65
+ ### History
66
+
67
+ | Action | Description | Parameters |
68
+ |--------|-------------|------------|
69
+ | `log` | Commit history | `repository`, `branch`, `file`, `limit`, `format` |
70
+ | `blame` | Line authorship | `repository`, `file`, `branch`, `line` |
71
+ | `show` | Commit details | `repository`, `ref`, `branch`, `file` |
72
+
73
+ ### Comparison
74
+
75
+ | Action | Description | Parameters |
76
+ |--------|-------------|------------|
77
+ | `diff` | Show differences | `repository`, `ref`, `branch`, `file` |
78
+ | `branches` | List branches | `repository` |
79
+
80
+ ## Examples
81
+
82
+ ```typescript
83
+ // Clone a repository
84
+ { action: "clone", repository: "https://github.com/user/repo.git" }
85
+
86
+ // List files on develop branch
87
+ { action: "ls-files", repository: "repo", branch: "develop" }
88
+
89
+ // Search for TODO comments
90
+ { action: "grep", repository: "repo", pattern: "TODO", branch: "main" }
91
+
92
+ // Show recent commits
93
+ { action: "log", repository: "repo", limit: 10 }
94
+
95
+ // Blame a specific file
96
+ { action: "blame", repository: "repo", file: "src/index.ts", line: 42 }
97
+ ```
98
+
99
+ ## Architecture
100
+
101
+ Repositories are stored as bare clones in the base directory. When a branch is
102
+ accessed, a worktree is automatically created under `.worktrees/<branch-name>/`.
103
+
104
+ ```
105
+ /tmp/git-repos/
106
+ ├── repo-name/ # Bare repository
107
+ │ ├── .worktrees/
108
+ │ │ ├── main/ # Worktree for main branch
109
+ │ │ └── develop/ # Worktree for develop branch
110
+ │ ├── objects/
111
+ │ ├── refs/
112
+ │ └── ...
113
+ ```
114
+
115
+ ## License
116
+
117
+ MIT
@@ -0,0 +1,115 @@
1
+ /**
2
+ * Resolve repository path and name.
3
+ * - repo_url specified → ensureRepo (bare clone/fetch) then return remote path
4
+ * - repo_url omitted → use local working directory (current git repo)
5
+ */
6
+ export declare function resolveRepo(repoUrl?: string): Promise<{
7
+ repoPath: string;
8
+ repoName: string;
9
+ }>;
10
+ export interface GrepMatch {
11
+ file: string;
12
+ line: number;
13
+ content: string;
14
+ }
15
+ export interface GrepResult {
16
+ repo: string;
17
+ ref: string;
18
+ pattern: string;
19
+ matches: GrepMatch[];
20
+ total_matches: number;
21
+ truncated: boolean;
22
+ }
23
+ export interface GitGrepOptions {
24
+ ref?: string | undefined;
25
+ path?: string | undefined;
26
+ ignore_case?: boolean | undefined;
27
+ max_count?: number | undefined;
28
+ }
29
+ /**
30
+ * Extract repository name from URL.
31
+ * Handles both SSH (git@github.com:org/repo.git) and HTTPS (https://github.com/org/repo.git) formats.
32
+ */
33
+ export declare function extractRepoName(repoUrl: string): string;
34
+ /**
35
+ * Get the local bare repo path for a given repo URL.
36
+ */
37
+ export declare function getRepoPath(repoUrl: string): string;
38
+ /**
39
+ * Ensure the repository is available locally.
40
+ * Clones as bare repo if not present, fetches if already present.
41
+ * Returns the path to the bare repository.
42
+ */
43
+ export declare function ensureRepo(repoUrl: string): Promise<string>;
44
+ /**
45
+ * Parse git grep output into structured matches.
46
+ * Expected format: "<ref>:<file>:<line>:<content>"
47
+ */
48
+ export declare function parseGitGrepOutput(output: string, ref: string): GrepMatch[];
49
+ /**
50
+ * Execute git grep on a bare repository.
51
+ */
52
+ export declare function gitGrep(repoPath: string, pattern: string, options?: GitGrepOptions): Promise<GrepResult>;
53
+ export interface LsFilesOptions {
54
+ path?: string;
55
+ pattern?: string;
56
+ }
57
+ export interface LogOptions {
58
+ path?: string;
59
+ max_count?: number;
60
+ author?: string;
61
+ since?: string;
62
+ until?: string;
63
+ grep?: string;
64
+ }
65
+ export interface BlameOptions {
66
+ line_start?: number;
67
+ line_end?: number;
68
+ }
69
+ export interface DiffOptions {
70
+ path?: string;
71
+ }
72
+ /**
73
+ * List files in a repository at a given ref.
74
+ * Uses `git ls-tree --name-only -r <ref> [-- <path>]`
75
+ */
76
+ export declare function gitLsFiles(repoPath: string, ref?: string, options?: LsFilesOptions): Promise<string[]>;
77
+ /**
78
+ * Get commit log from a repository.
79
+ * Uses `git log --format=...`
80
+ */
81
+ export declare function gitLog(repoPath: string, ref?: string, options?: LogOptions): Promise<string>;
82
+ /**
83
+ * Get blame information for a file.
84
+ * Uses `git blame <ref> -- <path>`
85
+ */
86
+ export declare function gitBlame(repoPath: string, ref: string | undefined, filePath: string, options?: BlameOptions): Promise<string>;
87
+ /**
88
+ * Show commit or file content.
89
+ * `git show <ref>` for commit detail, `git show <ref>:<path>` for file content.
90
+ */
91
+ export declare function gitShow(repoPath: string, ref: string, filePath?: string): Promise<string>;
92
+ /**
93
+ * Show diff between two refs.
94
+ * Uses `git diff <refFrom> <refTo> [-- <path>]`
95
+ */
96
+ export declare function gitDiff(repoPath: string, refFrom: string, refTo: string, options?: DiffOptions): Promise<string>;
97
+ export interface BranchListOptions {
98
+ pattern?: string;
99
+ }
100
+ /**
101
+ * List branches in a repository.
102
+ * For bare repos: `git for-each-ref --format='%(refname:short)' refs/heads/`
103
+ * For local repos: `git branch -a --format='%(refname:short)'`
104
+ */
105
+ export declare function gitBranchList(repoPath: string, options?: BranchListOptions): Promise<string[]>;
106
+ export interface TagListOptions {
107
+ pattern?: string;
108
+ max_count?: number;
109
+ }
110
+ /**
111
+ * List tags in a repository, sorted by newest first.
112
+ * Uses `git tag --list --sort=-creatordate`
113
+ */
114
+ export declare function gitTagList(repoPath: string, options?: TagListOptions): Promise<string[]>;
115
+ //# sourceMappingURL=git-repo-manager.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"git-repo-manager.d.ts","sourceRoot":"","sources":["../src/git-repo-manager.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,316 @@
1
+ import { execFile } from "node:child_process";
2
+ import { promisify } from "node:util";
3
+ import { existsSync } from "node:fs";
4
+ import path from "node:path";
5
+ const execFileAsync = promisify(execFile);
6
+ const BASE_DIR = "/tmp/git-grep-repos";
7
+ /**
8
+ * Resolve repository path and name.
9
+ * - repo_url specified → ensureRepo (bare clone/fetch) then return remote path
10
+ * - repo_url omitted → use local working directory (current git repo)
11
+ */
12
+ export async function resolveRepo(repoUrl) {
13
+ if (repoUrl) {
14
+ const repoPath = await ensureRepo(repoUrl);
15
+ const repoName = extractRepoName(repoUrl);
16
+ return { repoPath, repoName };
17
+ }
18
+ // Local mode: find git root of cwd
19
+ const { stdout } = await execFileAsync("git", ["rev-parse", "--show-toplevel"], { timeout: 5_000 });
20
+ const repoPath = stdout.trim();
21
+ const repoName = path.basename(repoPath);
22
+ return { repoPath, repoName };
23
+ }
24
+ /**
25
+ * Extract repository name from URL.
26
+ * Handles both SSH (git@github.com:org/repo.git) and HTTPS (https://github.com/org/repo.git) formats.
27
+ */
28
+ export function extractRepoName(repoUrl) {
29
+ // Remove trailing .git if present
30
+ const cleaned = repoUrl.replace(/\.git$/, "");
31
+ // Get last path segment (works for both SSH and HTTPS)
32
+ const segments = cleaned.split(/[/:]/).filter(Boolean);
33
+ const name = segments[segments.length - 1];
34
+ if (!name) {
35
+ throw new Error(`Cannot extract repository name from URL: ${repoUrl}`);
36
+ }
37
+ return name;
38
+ }
39
+ /**
40
+ * Get the local bare repo path for a given repo URL.
41
+ */
42
+ export function getRepoPath(repoUrl) {
43
+ const repoName = extractRepoName(repoUrl);
44
+ return path.join(BASE_DIR, repoName);
45
+ }
46
+ /**
47
+ * Ensure the repository is available locally.
48
+ * Clones as bare repo if not present, fetches if already present.
49
+ * Returns the path to the bare repository.
50
+ */
51
+ export async function ensureRepo(repoUrl) {
52
+ const repoPath = getRepoPath(repoUrl);
53
+ if (existsSync(repoPath)) {
54
+ // Fetch latest changes
55
+ await execFileAsync("git", ["fetch", "--all", "--prune"], {
56
+ cwd: repoPath,
57
+ timeout: 60_000,
58
+ });
59
+ }
60
+ else {
61
+ // Clone as bare repo
62
+ await execFileAsync("git", ["clone", "--bare", repoUrl, repoPath], {
63
+ timeout: 120_000,
64
+ });
65
+ }
66
+ return repoPath;
67
+ }
68
+ /**
69
+ * Parse git grep output into structured matches.
70
+ * Expected format: "<ref>:<file>:<line>:<content>"
71
+ */
72
+ export function parseGitGrepOutput(output, ref) {
73
+ if (!output.trim()) {
74
+ return [];
75
+ }
76
+ const lines = output.trim().split("\n");
77
+ const matches = [];
78
+ const refPrefix = `${ref}:`;
79
+ for (const line of lines) {
80
+ // Format: ref:file:lineNumber:content
81
+ if (!line.startsWith(refPrefix)) {
82
+ continue;
83
+ }
84
+ const afterRef = line.slice(refPrefix.length);
85
+ // Find the first colon-separated number to split file:line:content
86
+ const firstColon = afterRef.indexOf(":");
87
+ if (firstColon === -1)
88
+ continue;
89
+ const remaining = afterRef.slice(firstColon + 1);
90
+ const secondColon = remaining.indexOf(":");
91
+ if (secondColon === -1)
92
+ continue;
93
+ const file = afterRef.slice(0, firstColon);
94
+ const lineNum = Number.parseInt(remaining.slice(0, secondColon), 10);
95
+ const content = remaining.slice(secondColon + 1);
96
+ if (Number.isNaN(lineNum))
97
+ continue;
98
+ matches.push({ file, line: lineNum, content });
99
+ }
100
+ return matches;
101
+ }
102
+ /**
103
+ * Execute git grep on a bare repository.
104
+ */
105
+ export async function gitGrep(repoPath, pattern, options = {}) {
106
+ const ref = options.ref ?? "HEAD";
107
+ const maxCount = Math.min(options.max_count ?? 100, 500);
108
+ const args = ["grep", "-n"];
109
+ if (options.ignore_case) {
110
+ args.push("-i");
111
+ }
112
+ args.push(`--max-count=${maxCount}`);
113
+ args.push(pattern);
114
+ args.push(ref);
115
+ if (options.path) {
116
+ args.push("--", options.path);
117
+ }
118
+ const repoName = path.basename(repoPath);
119
+ try {
120
+ const { stdout } = await execFileAsync("git", args, {
121
+ cwd: repoPath,
122
+ timeout: 30_000,
123
+ maxBuffer: 10 * 1024 * 1024, // 10MB
124
+ });
125
+ const matches = parseGitGrepOutput(stdout, ref);
126
+ const truncated = matches.length >= maxCount;
127
+ return {
128
+ repo: repoName,
129
+ ref,
130
+ pattern,
131
+ matches,
132
+ total_matches: matches.length,
133
+ truncated,
134
+ };
135
+ }
136
+ catch (error) {
137
+ // git grep exits with code 1 when no matches found
138
+ if (error instanceof Error && "code" in error && error.code === 1) {
139
+ return {
140
+ repo: repoName,
141
+ ref,
142
+ pattern,
143
+ matches: [],
144
+ total_matches: 0,
145
+ truncated: false,
146
+ };
147
+ }
148
+ throw error;
149
+ }
150
+ }
151
+ /**
152
+ * List files in a repository at a given ref.
153
+ * Uses `git ls-tree --name-only -r <ref> [-- <path>]`
154
+ */
155
+ export async function gitLsFiles(repoPath, ref = "HEAD", options = {}) {
156
+ const args = ["ls-tree", "--name-only", "-r", ref];
157
+ if (options.path) {
158
+ args.push("--", options.path);
159
+ }
160
+ const { stdout } = await execFileAsync("git", args, {
161
+ cwd: repoPath,
162
+ timeout: 30_000,
163
+ maxBuffer: 10 * 1024 * 1024,
164
+ });
165
+ let files = stdout.trim().split("\n").filter(Boolean);
166
+ if (options.pattern) {
167
+ const globToRegex = (glob) => {
168
+ const escaped = glob
169
+ .replace(/[.+^${}()|[\]\\]/g, "\\$&")
170
+ .replace(/\*\*/g, "___GLOBSTAR___")
171
+ .replace(/\*/g, "[^/]*")
172
+ .replace(/\?/g, "[^/]")
173
+ .replace(/___GLOBSTAR___/g, ".*");
174
+ return new RegExp(`^${escaped}$`);
175
+ };
176
+ const regex = globToRegex(options.pattern);
177
+ files = files.filter(f => regex.test(f));
178
+ }
179
+ return files;
180
+ }
181
+ /**
182
+ * Get commit log from a repository.
183
+ * Uses `git log --format=...`
184
+ */
185
+ export async function gitLog(repoPath, ref = "HEAD", options = {}) {
186
+ const maxCount = Math.min(options.max_count ?? 20, 100);
187
+ const args = [
188
+ "log",
189
+ `--format=%H%x09%ai%x09%an%x09%s`,
190
+ `-${maxCount}`,
191
+ ref,
192
+ ];
193
+ if (options.author) {
194
+ args.push(`--author=${options.author}`);
195
+ }
196
+ if (options.since) {
197
+ args.push(`--since=${options.since}`);
198
+ }
199
+ if (options.until) {
200
+ args.push(`--until=${options.until}`);
201
+ }
202
+ if (options.grep) {
203
+ args.push(`--grep=${options.grep}`);
204
+ }
205
+ if (options.path) {
206
+ args.push("--", options.path);
207
+ }
208
+ const { stdout } = await execFileAsync("git", args, {
209
+ cwd: repoPath,
210
+ timeout: 30_000,
211
+ maxBuffer: 10 * 1024 * 1024,
212
+ });
213
+ return stdout.trim();
214
+ }
215
+ /**
216
+ * Get blame information for a file.
217
+ * Uses `git blame <ref> -- <path>`
218
+ */
219
+ export async function gitBlame(repoPath, ref = "HEAD", filePath, options = {}) {
220
+ const args = ["blame", "--porcelain"];
221
+ if (options.line_start && options.line_end) {
222
+ args.push(`-L${options.line_start},${options.line_end}`);
223
+ }
224
+ else if (options.line_start) {
225
+ args.push(`-L${options.line_start},`);
226
+ }
227
+ args.push(ref, "--", filePath);
228
+ const { stdout } = await execFileAsync("git", args, {
229
+ cwd: repoPath,
230
+ timeout: 30_000,
231
+ maxBuffer: 10 * 1024 * 1024,
232
+ });
233
+ return stdout.trim();
234
+ }
235
+ /**
236
+ * Show commit or file content.
237
+ * `git show <ref>` for commit detail, `git show <ref>:<path>` for file content.
238
+ */
239
+ export async function gitShow(repoPath, ref, filePath) {
240
+ const target = filePath ? `${ref}:${filePath}` : ref;
241
+ const args = ["show", target];
242
+ const { stdout } = await execFileAsync("git", args, {
243
+ cwd: repoPath,
244
+ timeout: 30_000,
245
+ maxBuffer: 10 * 1024 * 1024,
246
+ });
247
+ return stdout;
248
+ }
249
+ /**
250
+ * Show diff between two refs.
251
+ * Uses `git diff <refFrom> <refTo> [-- <path>]`
252
+ */
253
+ export async function gitDiff(repoPath, refFrom, refTo, options = {}) {
254
+ const args = ["diff", refFrom, refTo];
255
+ if (options.path) {
256
+ args.push("--", options.path);
257
+ }
258
+ const { stdout } = await execFileAsync("git", args, {
259
+ cwd: repoPath,
260
+ timeout: 30_000,
261
+ maxBuffer: 10 * 1024 * 1024,
262
+ });
263
+ return stdout;
264
+ }
265
+ /**
266
+ * List branches in a repository.
267
+ * For bare repos: `git for-each-ref --format='%(refname:short)' refs/heads/`
268
+ * For local repos: `git branch -a --format='%(refname:short)'`
269
+ */
270
+ export async function gitBranchList(repoPath, options = {}) {
271
+ const isBare = existsSync(path.join(repoPath, "HEAD")) && !existsSync(path.join(repoPath, ".git"));
272
+ const args = isBare
273
+ ? ["for-each-ref", "--format=%(refname:short)", "refs/heads/"]
274
+ : ["branch", "-a", "--format=%(refname:short)"];
275
+ const { stdout } = await execFileAsync("git", args, {
276
+ cwd: repoPath,
277
+ timeout: 30_000,
278
+ maxBuffer: 10 * 1024 * 1024,
279
+ });
280
+ let branches = stdout.trim().split("\n").filter(Boolean);
281
+ if (options.pattern) {
282
+ const globToRegex = (glob) => {
283
+ const escaped = glob
284
+ .replace(/[.+^${}()|[\]\\]/g, "\\$&")
285
+ .replace(/\*\*/g, "___GLOBSTAR___")
286
+ .replace(/\*/g, "[^/]*")
287
+ .replace(/\?/g, "[^/]")
288
+ .replace(/___GLOBSTAR___/g, ".*");
289
+ return new RegExp(`^${escaped}$`);
290
+ };
291
+ const regex = globToRegex(options.pattern);
292
+ branches = branches.filter(b => regex.test(b));
293
+ }
294
+ return branches;
295
+ }
296
+ /**
297
+ * List tags in a repository, sorted by newest first.
298
+ * Uses `git tag --list --sort=-creatordate`
299
+ */
300
+ export async function gitTagList(repoPath, options = {}) {
301
+ const args = ["tag", "--list", "--sort=-creatordate"];
302
+ if (options.pattern) {
303
+ args.push(options.pattern);
304
+ }
305
+ const { stdout } = await execFileAsync("git", args, {
306
+ cwd: repoPath,
307
+ timeout: 30_000,
308
+ maxBuffer: 10 * 1024 * 1024,
309
+ });
310
+ let tags = stdout.trim().split("\n").filter(Boolean);
311
+ if (options.max_count) {
312
+ tags = tags.slice(0, options.max_count);
313
+ }
314
+ return tags;
315
+ }
316
+ //# sourceMappingURL=git-repo-manager.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"git-repo-manager.js","sourceRoot":"","sources":["../src/git-repo-manager.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+ export {};
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":""}
package/dist/index.js ADDED
@@ -0,0 +1,7 @@
1
+ #!/usr/bin/env node
2
+ import { startServer } from "./server.js";
3
+ startServer().catch((error) => {
4
+ console.error("Failed to start server:", error);
5
+ process.exit(1);
6
+ });
7
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAE1C,WAAW,EAAE,CAAC,KAAK,CAAC,CAAC,KAAK,EAAE,EAAE;IAC5B,OAAO,CAAC,KAAK,CAAC,yBAAyB,EAAE,KAAK,CAAC,CAAC;IAChD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC,CAAC,CAAC"}
@@ -0,0 +1,4 @@
1
+ import type { GitOperation } from "./types.js";
2
+ export declare const blameOp: GitOperation;
3
+ export declare const blameOperations: GitOperation[];
4
+ //# sourceMappingURL=blame-ops.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"blame-ops.d.ts","sourceRoot":"","sources":["../../src/operations/blame-ops.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAkD/C,eAAO,MAAM,OAAO,EAAE,YAuCrB,CAAC;AAEF,eAAO,MAAM,eAAe,EAAE,YAAY,EAAc,CAAC"}
@@ -0,0 +1,79 @@
1
+ import { z } from "zod";
2
+ import { gitBlame } from "../git-repo-manager.js";
3
+ function parseBlameOutput(output) {
4
+ if (!output.trim())
5
+ return [];
6
+ const lines = output.split("\n");
7
+ const result = [];
8
+ let currentCommit = "";
9
+ let currentAuthor = "";
10
+ let currentDate = "";
11
+ let currentLineNum = 0;
12
+ for (const line of lines) {
13
+ // Commit header line: <sha> <orig-line> <final-line> [<num-lines>]
14
+ const commitMatch = line.match(/^([0-9a-f]{40}) \d+ (\d+)/);
15
+ if (commitMatch) {
16
+ currentCommit = commitMatch[1];
17
+ currentLineNum = Number.parseInt(commitMatch[2], 10);
18
+ continue;
19
+ }
20
+ if (line.startsWith("author ")) {
21
+ currentAuthor = line.slice("author ".length);
22
+ }
23
+ else if (line.startsWith("author-time ")) {
24
+ const timestamp = Number.parseInt(line.slice("author-time ".length), 10);
25
+ currentDate = new Date(timestamp * 1000).toISOString().slice(0, 10);
26
+ }
27
+ else if (line.startsWith("\t")) {
28
+ // Content line (starts with tab)
29
+ result.push({
30
+ commit: currentCommit.slice(0, 8),
31
+ author: currentAuthor,
32
+ date: currentDate,
33
+ line_number: currentLineNum,
34
+ content: line.slice(1), // Remove leading tab
35
+ });
36
+ }
37
+ }
38
+ return result;
39
+ }
40
+ export const blameOp = {
41
+ id: "blame",
42
+ summary: "Show line-by-line author and commit info",
43
+ detail: `Show git blame information for each line in a file. Optionally specify line range.
44
+
45
+ Examples:
46
+ operation: "blame"
47
+ params: { path: "src/lib/mcp/index.ts" }
48
+ params: { repo_url: "git@github.com:org/repo.git", ref: "main", path: "packages/api/src/handler.ts", line_start: 10, line_end: 30 }`,
49
+ category: "History",
50
+ argsSchema: z.object({
51
+ repo_url: z.string().optional().describe("Repository URL (omit for current working directory)"),
52
+ ref: z.string().optional().describe('Branch name or commit hash (default: "HEAD")'),
53
+ path: z.string().describe("Target file path (required)"),
54
+ line_start: z.number().int().min(1).optional().describe("Start line number"),
55
+ line_end: z.number().int().min(1).optional().describe("End line number"),
56
+ }),
57
+ execute: async (args, ctx) => {
58
+ const ref = args.ref ?? "HEAD";
59
+ const output = await gitBlame(ctx.repoPath, ref, args.path, {
60
+ line_start: args.line_start,
61
+ line_end: args.line_end,
62
+ });
63
+ const blameLines = parseBlameOutput(output);
64
+ return {
65
+ content: [{
66
+ type: "text",
67
+ text: JSON.stringify({
68
+ repo: ctx.repoName,
69
+ ref,
70
+ path: args.path,
71
+ total_lines: blameLines.length,
72
+ lines: blameLines,
73
+ }, null, 2),
74
+ }],
75
+ };
76
+ },
77
+ };
78
+ export const blameOperations = [blameOp];
79
+ //# sourceMappingURL=blame-ops.js.map