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.
- package/README.md +117 -0
- package/dist/git-repo-manager.d.ts +115 -0
- package/dist/git-repo-manager.d.ts.map +1 -0
- package/dist/git-repo-manager.js +316 -0
- package/dist/git-repo-manager.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +7 -0
- package/dist/index.js.map +1 -0
- package/dist/operations/blame-ops.d.ts +4 -0
- package/dist/operations/blame-ops.d.ts.map +1 -0
- package/dist/operations/blame-ops.js +79 -0
- package/dist/operations/blame-ops.js.map +1 -0
- package/dist/operations/branch-ops.d.ts +4 -0
- package/dist/operations/branch-ops.d.ts.map +1 -0
- package/dist/operations/branch-ops.js +35 -0
- package/dist/operations/branch-ops.js.map +1 -0
- package/dist/operations/diff-ops.d.ts +4 -0
- package/dist/operations/diff-ops.d.ts.map +1 -0
- package/dist/operations/diff-ops.js +43 -0
- package/dist/operations/diff-ops.js.map +1 -0
- package/dist/operations/grep-ops.d.ts +4 -0
- package/dist/operations/grep-ops.d.ts.map +1 -0
- package/dist/operations/grep-ops.js +35 -0
- package/dist/operations/grep-ops.js.map +1 -0
- package/dist/operations/log-ops.d.ts +4 -0
- package/dist/operations/log-ops.d.ts.map +1 -0
- package/dist/operations/log-ops.js +55 -0
- package/dist/operations/log-ops.js.map +1 -0
- package/dist/operations/ls-files-ops.d.ts +4 -0
- package/dist/operations/ls-files-ops.d.ts.map +1 -0
- package/dist/operations/ls-files-ops.js +40 -0
- package/dist/operations/ls-files-ops.js.map +1 -0
- package/dist/operations/registry.d.ts +10 -0
- package/dist/operations/registry.d.ts.map +1 -0
- package/dist/operations/registry.js +40 -0
- package/dist/operations/registry.js.map +1 -0
- package/dist/operations/show-ops.d.ts +4 -0
- package/dist/operations/show-ops.d.ts.map +1 -0
- package/dist/operations/show-ops.js +37 -0
- package/dist/operations/show-ops.js.map +1 -0
- package/dist/operations/tag-ops.d.ts +4 -0
- package/dist/operations/tag-ops.d.ts.map +1 -0
- package/dist/operations/tag-ops.js +37 -0
- package/dist/operations/tag-ops.js.map +1 -0
- package/dist/operations/types.d.ts +15 -0
- package/dist/operations/types.d.ts.map +1 -0
- package/dist/operations/types.js +2 -0
- package/dist/operations/types.js.map +1 -0
- package/dist/server.d.ts +2 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +180 -0
- package/dist/server.js.map +1 -0
- package/dist/services/git-executor.d.ts +12 -0
- package/dist/services/git-executor.d.ts.map +1 -0
- package/dist/services/git-executor.js +35 -0
- package/dist/services/git-executor.js.map +1 -0
- package/dist/services/repository-manager.d.ts +58 -0
- package/dist/services/repository-manager.d.ts.map +1 -0
- package/dist/services/repository-manager.js +297 -0
- package/dist/services/repository-manager.js.map +1 -0
- package/dist/tools/description.d.ts +7 -0
- package/dist/tools/description.d.ts.map +1 -0
- package/dist/tools/description.js +85 -0
- package/dist/tools/description.js.map +1 -0
- package/dist/tools/git/handlers/blame-handler.d.ts +8 -0
- package/dist/tools/git/handlers/blame-handler.d.ts.map +1 -0
- package/dist/tools/git/handlers/blame-handler.js +49 -0
- package/dist/tools/git/handlers/blame-handler.js.map +1 -0
- package/dist/tools/git/handlers/branches-handler.d.ts +8 -0
- package/dist/tools/git/handlers/branches-handler.d.ts.map +1 -0
- package/dist/tools/git/handlers/branches-handler.js +29 -0
- package/dist/tools/git/handlers/branches-handler.js.map +1 -0
- package/dist/tools/git/handlers/cat-file-handler.d.ts +8 -0
- package/dist/tools/git/handlers/cat-file-handler.d.ts.map +1 -0
- package/dist/tools/git/handlers/cat-file-handler.js +44 -0
- package/dist/tools/git/handlers/cat-file-handler.js.map +1 -0
- package/dist/tools/git/handlers/clone-handler.d.ts +8 -0
- package/dist/tools/git/handlers/clone-handler.d.ts.map +1 -0
- package/dist/tools/git/handlers/clone-handler.js +24 -0
- package/dist/tools/git/handlers/clone-handler.js.map +1 -0
- package/dist/tools/git/handlers/diff-handler.d.ts +8 -0
- package/dist/tools/git/handlers/diff-handler.d.ts.map +1 -0
- package/dist/tools/git/handlers/diff-handler.js +44 -0
- package/dist/tools/git/handlers/diff-handler.js.map +1 -0
- package/dist/tools/git/handlers/grep-handler.d.ts +8 -0
- package/dist/tools/git/handlers/grep-handler.d.ts.map +1 -0
- package/dist/tools/git/handlers/grep-handler.js +47 -0
- package/dist/tools/git/handlers/grep-handler.js.map +1 -0
- package/dist/tools/git/handlers/log-handler.d.ts +8 -0
- package/dist/tools/git/handlers/log-handler.d.ts.map +1 -0
- package/dist/tools/git/handlers/log-handler.js +43 -0
- package/dist/tools/git/handlers/log-handler.js.map +1 -0
- package/dist/tools/git/handlers/ls-files-handler.d.ts +8 -0
- package/dist/tools/git/handlers/ls-files-handler.d.ts.map +1 -0
- package/dist/tools/git/handlers/ls-files-handler.js +41 -0
- package/dist/tools/git/handlers/ls-files-handler.js.map +1 -0
- package/dist/tools/git/handlers/remove-handler.d.ts +8 -0
- package/dist/tools/git/handlers/remove-handler.d.ts.map +1 -0
- package/dist/tools/git/handlers/remove-handler.js +40 -0
- package/dist/tools/git/handlers/remove-handler.js.map +1 -0
- package/dist/tools/git/handlers/repos-handler.d.ts +8 -0
- package/dist/tools/git/handlers/repos-handler.d.ts.map +1 -0
- package/dist/tools/git/handlers/repos-handler.js +29 -0
- package/dist/tools/git/handlers/repos-handler.js.map +1 -0
- package/dist/tools/git/handlers/show-handler.d.ts +8 -0
- package/dist/tools/git/handlers/show-handler.d.ts.map +1 -0
- package/dist/tools/git/handlers/show-handler.js +41 -0
- package/dist/tools/git/handlers/show-handler.js.map +1 -0
- package/dist/tools/git/index.d.ts +8 -0
- package/dist/tools/git/index.d.ts.map +1 -0
- package/dist/tools/git/index.js +168 -0
- package/dist/tools/git/index.js.map +1 -0
- package/dist/types/index.d.ts +99 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +2 -0
- package/dist/types/index.js.map +1 -0
- package/dist/utils/response-wrapper.d.ts +9 -0
- package/dist/utils/response-wrapper.d.ts.map +1 -0
- package/dist/utils/response-wrapper.js +32 -0
- package/dist/utils/response-wrapper.js.map +1 -0
- 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":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAC9C,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AACtC,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AACrC,OAAO,IAAI,MAAM,WAAW,CAAC;AAE7B,MAAM,aAAa,GAAG,SAAS,CAAC,QAAQ,CAAC,CAAC;AAE1C,MAAM,QAAQ,GAAG,qBAAqB,CAAC;AAEvC;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,WAAW,CAAC,OAAgB;IAChD,IAAI,OAAO,EAAE,CAAC;QACZ,MAAM,QAAQ,GAAG,MAAM,UAAU,CAAC,OAAO,CAAC,CAAC;QAC3C,MAAM,QAAQ,GAAG,eAAe,CAAC,OAAO,CAAC,CAAC;QAC1C,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAE,CAAC;IAChC,CAAC;IACD,mCAAmC;IACnC,MAAM,EAAE,MAAM,EAAE,GAAG,MAAM,aAAa,CAAC,KAAK,EAAE,CAAC,WAAW,EAAE,iBAAiB,CAAC,EAAE,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC,CAAC;IACpG,MAAM,QAAQ,GAAG,MAAM,CAAC,IAAI,EAAE,CAAC;IAC/B,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;IACzC,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAE,CAAC;AAChC,CAAC;AAwBD;;;GAGG;AACH,MAAM,UAAU,eAAe,CAAC,OAAe;IAC7C,kCAAkC;IAClC,MAAM,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC;IAC9C,uDAAuD;IACvD,MAAM,QAAQ,GAAG,OAAO,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;IACvD,MAAM,IAAI,GAAG,QAAQ,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;IAC3C,IAAI,CAAC,IAAI,EAAE,CAAC;QACV,MAAM,IAAI,KAAK,CAAC,4CAA4C,OAAO,EAAE,CAAC,CAAC;IACzE,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,WAAW,CAAC,OAAe;IACzC,MAAM,QAAQ,GAAG,eAAe,CAAC,OAAO,CAAC,CAAC;IAC1C,OAAO,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC;AACvC,CAAC;AAED;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,UAAU,CAAC,OAAe;IAC9C,MAAM,QAAQ,GAAG,WAAW,CAAC,OAAO,CAAC,CAAC;IAEtC,IAAI,UAAU,CAAC,QAAQ,CAAC,EAAE,CAAC;QACzB,uBAAuB;QACvB,MAAM,aAAa,CAAC,KAAK,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE,SAAS,CAAC,EAAE;YACxD,GAAG,EAAE,QAAQ;YACb,OAAO,EAAE,MAAM;SAChB,CAAC,CAAC;IACL,CAAC;SAAM,CAAC;QACN,qBAAqB;QACrB,MAAM,aAAa,CAAC,KAAK,EAAE,CAAC,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,QAAQ,CAAC,EAAE;YACjE,OAAO,EAAE,OAAO;SACjB,CAAC,CAAC;IACL,CAAC;IAED,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,kBAAkB,CAAC,MAAc,EAAE,GAAW;IAC5D,IAAI,CAAC,MAAM,CAAC,IAAI,EAAE,EAAE,CAAC;QACnB,OAAO,EAAE,CAAC;IACZ,CAAC;IAED,MAAM,KAAK,GAAG,MAAM,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IACxC,MAAM,OAAO,GAAgB,EAAE,CAAC;IAChC,MAAM,SAAS,GAAG,GAAG,GAAG,GAAG,CAAC;IAE5B,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,sCAAsC;QACtC,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,SAAS,CAAC,EAAE,CAAC;YAChC,SAAS;QACX,CAAC;QAED,MAAM,QAAQ,GAAG,IAAI,CAAC,KAAK,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC;QAC9C,mEAAmE;QACnE,MAAM,UAAU,GAAG,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;QACzC,IAAI,UAAU,KAAK,CAAC,CAAC;YAAE,SAAS;QAEhC,MAAM,SAAS,GAAG,QAAQ,CAAC,KAAK,CAAC,UAAU,GAAG,CAAC,CAAC,CAAC;QACjD,MAAM,WAAW,GAAG,SAAS,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;QAC3C,IAAI,WAAW,KAAK,CAAC,CAAC;YAAE,SAAS;QAEjC,MAAM,IAAI,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC,EAAE,UAAU,CAAC,CAAC;QAC3C,MAAM,OAAO,GAAG,MAAM,CAAC,QAAQ,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,EAAE,WAAW,CAAC,EAAE,EAAE,CAAC,CAAC;QACrE,MAAM,OAAO,GAAG,SAAS,CAAC,KAAK,CAAC,WAAW,GAAG,CAAC,CAAC,CAAC;QAEjD,IAAI,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC;YAAE,SAAS;QAEpC,OAAO,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,OAAO,EAAE,OAAO,EAAE,CAAC,CAAC;IACjD,CAAC;IAED,OAAO,OAAO,CAAC;AACjB,CAAC;AAED;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,OAAO,CAC3B,QAAgB,EAChB,OAAe,EACf,UAA0B,EAAE;IAE5B,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,IAAI,MAAM,CAAC;IAClC,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,SAAS,IAAI,GAAG,EAAE,GAAG,CAAC,CAAC;IAEzD,MAAM,IAAI,GAAG,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;IAC5B,IAAI,OAAO,CAAC,WAAW,EAAE,CAAC;QACxB,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAClB,CAAC;IACD,IAAI,CAAC,IAAI,CAAC,eAAe,QAAQ,EAAE,CAAC,CAAC;IACrC,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IACnB,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IAEf,IAAI,OAAO,CAAC,IAAI,EAAE,CAAC;QACjB,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,OAAO,CAAC,IAAI,CAAC,CAAC;IAChC,CAAC;IAED,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;IAEzC,IAAI,CAAC;QACH,MAAM,EAAE,MAAM,EAAE,GAAG,MAAM,aAAa,CAAC,KAAK,EAAE,IAAI,EAAE;YAClD,GAAG,EAAE,QAAQ;YACb,OAAO,EAAE,MAAM;YACf,SAAS,EAAE,EAAE,GAAG,IAAI,GAAG,IAAI,EAAE,OAAO;SACrC,CAAC,CAAC;QAEH,MAAM,OAAO,GAAG,kBAAkB,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;QAChD,MAAM,SAAS,GAAG,OAAO,CAAC,MAAM,IAAI,QAAQ,CAAC;QAE7C,OAAO;YACL,IAAI,EAAE,QAAQ;YACd,GAAG;YACH,OAAO;YACP,OAAO;YACP,aAAa,EAAE,OAAO,CAAC,MAAM;YAC7B,SAAS;SACV,CAAC;IACJ,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,mDAAmD;QACnD,IAAI,KAAK,YAAY,KAAK,IAAI,MAAM,IAAI,KAAK,IAAK,KAA0B,CAAC,IAAI,KAAK,CAAC,EAAE,CAAC;YACxF,OAAO;gBACL,IAAI,EAAE,QAAQ;gBACd,GAAG;gBACH,OAAO;gBACP,OAAO,EAAE,EAAE;gBACX,aAAa,EAAE,CAAC;gBAChB,SAAS,EAAE,KAAK;aACjB,CAAC;QACJ,CAAC;QACD,MAAM,KAAK,CAAC;IACd,CAAC;AACH,CAAC;AA2BD;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,UAAU,CAC9B,QAAgB,EAChB,MAAc,MAAM,EACpB,UAA0B,EAAE;IAE5B,MAAM,IAAI,GAAG,CAAC,SAAS,EAAE,aAAa,EAAE,IAAI,EAAE,GAAG,CAAC,CAAC;IAEnD,IAAI,OAAO,CAAC,IAAI,EAAE,CAAC;QACjB,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,OAAO,CAAC,IAAI,CAAC,CAAC;IAChC,CAAC;IAED,MAAM,EAAE,MAAM,EAAE,GAAG,MAAM,aAAa,CAAC,KAAK,EAAE,IAAI,EAAE;QAClD,GAAG,EAAE,QAAQ;QACb,OAAO,EAAE,MAAM;QACf,SAAS,EAAE,EAAE,GAAG,IAAI,GAAG,IAAI;KAC5B,CAAC,CAAC;IAEH,IAAI,KAAK,GAAG,MAAM,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;IAEtD,IAAI,OAAO,CAAC,OAAO,EAAE,CAAC;QACpB,MAAM,WAAW,GAAG,CAAC,IAAY,EAAE,EAAE;YACnC,MAAM,OAAO,GAAG,IAAI;iBACjB,OAAO,CAAC,mBAAmB,EAAE,MAAM,CAAC;iBACpC,OAAO,CAAC,OAAO,EAAE,gBAAgB,CAAC;iBAClC,OAAO,CAAC,KAAK,EAAE,OAAO,CAAC;iBACvB,OAAO,CAAC,KAAK,EAAE,MAAM,CAAC;iBACtB,OAAO,CAAC,iBAAiB,EAAE,IAAI,CAAC,CAAC;YACpC,OAAO,IAAI,MAAM,CAAC,IAAI,OAAO,GAAG,CAAC,CAAC;QACpC,CAAC,CAAC;QACF,MAAM,KAAK,GAAG,WAAW,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;QAC3C,KAAK,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC;IAC3C,CAAC;IAED,OAAO,KAAK,CAAC;AACf,CAAC;AAED;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,MAAM,CAC1B,QAAgB,EAChB,MAAc,MAAM,EACpB,UAAsB,EAAE;IAExB,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,SAAS,IAAI,EAAE,EAAE,GAAG,CAAC,CAAC;IAExD,MAAM,IAAI,GAAG;QACX,KAAK;QACL,iCAAiC;QACjC,IAAI,QAAQ,EAAE;QACd,GAAG;KACJ,CAAC;IAEF,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC;QACnB,IAAI,CAAC,IAAI,CAAC,YAAY,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;IAC1C,CAAC;IACD,IAAI,OAAO,CAAC,KAAK,EAAE,CAAC;QAClB,IAAI,CAAC,IAAI,CAAC,WAAW,OAAO,CAAC,KAAK,EAAE,CAAC,CAAC;IACxC,CAAC;IACD,IAAI,OAAO,CAAC,KAAK,EAAE,CAAC;QAClB,IAAI,CAAC,IAAI,CAAC,WAAW,OAAO,CAAC,KAAK,EAAE,CAAC,CAAC;IACxC,CAAC;IACD,IAAI,OAAO,CAAC,IAAI,EAAE,CAAC;QACjB,IAAI,CAAC,IAAI,CAAC,UAAU,OAAO,CAAC,IAAI,EAAE,CAAC,CAAC;IACtC,CAAC;IACD,IAAI,OAAO,CAAC,IAAI,EAAE,CAAC;QACjB,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,OAAO,CAAC,IAAI,CAAC,CAAC;IAChC,CAAC;IAED,MAAM,EAAE,MAAM,EAAE,GAAG,MAAM,aAAa,CAAC,KAAK,EAAE,IAAI,EAAE;QAClD,GAAG,EAAE,QAAQ;QACb,OAAO,EAAE,MAAM;QACf,SAAS,EAAE,EAAE,GAAG,IAAI,GAAG,IAAI;KAC5B,CAAC,CAAC;IAEH,OAAO,MAAM,CAAC,IAAI,EAAE,CAAC;AACvB,CAAC;AAED;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,QAAQ,CAC5B,QAAgB,EAChB,MAAc,MAAM,EACpB,QAAgB,EAChB,UAAwB,EAAE;IAE1B,MAAM,IAAI,GAAG,CAAC,OAAO,EAAE,aAAa,CAAC,CAAC;IAEtC,IAAI,OAAO,CAAC,UAAU,IAAI,OAAO,CAAC,QAAQ,EAAE,CAAC;QAC3C,IAAI,CAAC,IAAI,CAAC,KAAK,OAAO,CAAC,UAAU,IAAI,OAAO,CAAC,QAAQ,EAAE,CAAC,CAAC;IAC3D,CAAC;SAAM,IAAI,OAAO,CAAC,UAAU,EAAE,CAAC;QAC9B,IAAI,CAAC,IAAI,CAAC,KAAK,OAAO,CAAC,UAAU,GAAG,CAAC,CAAC;IACxC,CAAC;IAED,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,IAAI,EAAE,QAAQ,CAAC,CAAC;IAE/B,MAAM,EAAE,MAAM,EAAE,GAAG,MAAM,aAAa,CAAC,KAAK,EAAE,IAAI,EAAE;QAClD,GAAG,EAAE,QAAQ;QACb,OAAO,EAAE,MAAM;QACf,SAAS,EAAE,EAAE,GAAG,IAAI,GAAG,IAAI;KAC5B,CAAC,CAAC;IAEH,OAAO,MAAM,CAAC,IAAI,EAAE,CAAC;AACvB,CAAC;AAED;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,OAAO,CAC3B,QAAgB,EAChB,GAAW,EACX,QAAiB;IAEjB,MAAM,MAAM,GAAG,QAAQ,CAAC,CAAC,CAAC,GAAG,GAAG,IAAI,QAAQ,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC;IACrD,MAAM,IAAI,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAE9B,MAAM,EAAE,MAAM,EAAE,GAAG,MAAM,aAAa,CAAC,KAAK,EAAE,IAAI,EAAE;QAClD,GAAG,EAAE,QAAQ;QACb,OAAO,EAAE,MAAM;QACf,SAAS,EAAE,EAAE,GAAG,IAAI,GAAG,IAAI;KAC5B,CAAC,CAAC;IAEH,OAAO,MAAM,CAAC;AAChB,CAAC;AAED;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,OAAO,CAC3B,QAAgB,EAChB,OAAe,EACf,KAAa,EACb,UAAuB,EAAE;IAEzB,MAAM,IAAI,GAAG,CAAC,MAAM,EAAE,OAAO,EAAE,KAAK,CAAC,CAAC;IAEtC,IAAI,OAAO,CAAC,IAAI,EAAE,CAAC;QACjB,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,OAAO,CAAC,IAAI,CAAC,CAAC;IAChC,CAAC;IAED,MAAM,EAAE,MAAM,EAAE,GAAG,MAAM,aAAa,CAAC,KAAK,EAAE,IAAI,EAAE;QAClD,GAAG,EAAE,QAAQ;QACb,OAAO,EAAE,MAAM;QACf,SAAS,EAAE,EAAE,GAAG,IAAI,GAAG,IAAI;KAC5B,CAAC,CAAC;IAEH,OAAO,MAAM,CAAC;AAChB,CAAC;AAMD;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,aAAa,CACjC,QAAgB,EAChB,UAA6B,EAAE;IAE/B,MAAM,MAAM,GAAG,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC,CAAC;IAEnG,MAAM,IAAI,GAAG,MAAM;QACjB,CAAC,CAAC,CAAC,cAAc,EAAE,2BAA2B,EAAE,aAAa,CAAC;QAC9D,CAAC,CAAC,CAAC,QAAQ,EAAE,IAAI,EAAE,2BAA2B,CAAC,CAAC;IAElD,MAAM,EAAE,MAAM,EAAE,GAAG,MAAM,aAAa,CAAC,KAAK,EAAE,IAAI,EAAE;QAClD,GAAG,EAAE,QAAQ;QACb,OAAO,EAAE,MAAM;QACf,SAAS,EAAE,EAAE,GAAG,IAAI,GAAG,IAAI;KAC5B,CAAC,CAAC;IAEH,IAAI,QAAQ,GAAG,MAAM,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;IAEzD,IAAI,OAAO,CAAC,OAAO,EAAE,CAAC;QACpB,MAAM,WAAW,GAAG,CAAC,IAAY,EAAE,EAAE;YACnC,MAAM,OAAO,GAAG,IAAI;iBACjB,OAAO,CAAC,mBAAmB,EAAE,MAAM,CAAC;iBACpC,OAAO,CAAC,OAAO,EAAE,gBAAgB,CAAC;iBAClC,OAAO,CAAC,KAAK,EAAE,OAAO,CAAC;iBACvB,OAAO,CAAC,KAAK,EAAE,MAAM,CAAC;iBACtB,OAAO,CAAC,iBAAiB,EAAE,IAAI,CAAC,CAAC;YACpC,OAAO,IAAI,MAAM,CAAC,IAAI,OAAO,GAAG,CAAC,CAAC;QACpC,CAAC,CAAC;QACF,MAAM,KAAK,GAAG,WAAW,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;QAC3C,QAAQ,GAAG,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC;IACjD,CAAC;IAED,OAAO,QAAQ,CAAC;AAClB,CAAC;AAOD;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,UAAU,CAC9B,QAAgB,EAChB,UAA0B,EAAE;IAE5B,MAAM,IAAI,GAAG,CAAC,KAAK,EAAE,QAAQ,EAAE,qBAAqB,CAAC,CAAC;IAEtD,IAAI,OAAO,CAAC,OAAO,EAAE,CAAC;QACpB,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;IAC7B,CAAC;IAED,MAAM,EAAE,MAAM,EAAE,GAAG,MAAM,aAAa,CAAC,KAAK,EAAE,IAAI,EAAE;QAClD,GAAG,EAAE,QAAQ;QACb,OAAO,EAAE,MAAM;QACf,SAAS,EAAE,EAAE,GAAG,IAAI,GAAG,IAAI;KAC5B,CAAC,CAAC;IAEH,IAAI,IAAI,GAAG,MAAM,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;IAErD,IAAI,OAAO,CAAC,SAAS,EAAE,CAAC;QACtB,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,OAAO,CAAC,SAAS,CAAC,CAAC;IAC1C,CAAC;IAED,OAAO,IAAI,CAAC;AACd,CAAC"}
|
package/dist/index.d.ts
ADDED
|
@@ -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 @@
|
|
|
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 @@
|
|
|
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
|