Skip to content

Commit cc6686a

Browse files
committed
feat(context): add git repository identifier support for consistent collection naming
1 parent cbaf635 commit cc6686a

File tree

1 file changed

+101
-0
lines changed

1 file changed

+101
-0
lines changed

packages/mcp/src/utils.ts

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
import * as path from "path";
2+
import { execSync } from "child_process";
3+
import * as fs from "fs";
24

35
/**
46
* Truncate content to specified length
@@ -27,4 +29,103 @@ export function ensureAbsolutePath(inputPath: string): string {
2729
export function trackCodebasePath(codebasePath: string): void {
2830
const absolutePath = ensureAbsolutePath(codebasePath);
2931
console.log(`[TRACKING] Tracked codebase path: ${absolutePath} (not marked as indexed)`);
32+
}
33+
34+
/**
35+
* Check if a directory is a git repository
36+
*/
37+
export function isGitRepository(dirPath: string): boolean {
38+
try {
39+
const gitDir = path.join(dirPath, '.git');
40+
return fs.existsSync(gitDir);
41+
} catch {
42+
return false;
43+
}
44+
}
45+
46+
/**
47+
* Extract git remote URL from a repository path
48+
* @param repoPath Path to the git repository
49+
* @returns Git remote URL or null if not a git repo or no remote
50+
*/
51+
export function extractGitRemoteUrl(repoPath: string): string | null {
52+
try {
53+
if (!isGitRepository(repoPath)) {
54+
return null;
55+
}
56+
57+
// Try to get the origin remote URL
58+
const result = execSync('git remote get-url origin', {
59+
cwd: repoPath,
60+
encoding: 'utf8',
61+
stdio: ['ignore', 'pipe', 'ignore'] // Ignore stderr to suppress git errors
62+
}).trim();
63+
64+
return result || null;
65+
} catch {
66+
// If no origin remote or git command fails, return null
67+
return null;
68+
}
69+
}
70+
71+
/**
72+
* Parse and normalize a git URL to a standard identifier
73+
* Handles various formats:
74+
* - https://github.com/org/repo.git
75+
* - [email protected]:org/repo.git
76+
* - https://gitlab.com/org/repo
77+
*
78+
* @param gitUrl The git remote URL
79+
* @returns Normalized identifier like "github.com/org/repo"
80+
*/
81+
export function parseGitUrl(gitUrl: string): string | null {
82+
try {
83+
// Remove trailing whitespace
84+
gitUrl = gitUrl.trim();
85+
86+
// Handle SSH format ([email protected]:org/repo.git)
87+
const sshMatch = gitUrl.match(/^git@([^:]+):(.+?)(\.git)?$/);
88+
if (sshMatch) {
89+
const host = sshMatch[1];
90+
const path = sshMatch[2];
91+
return `${host}/${path}`;
92+
}
93+
94+
// Handle HTTPS format (https://github.com/org/repo.git)
95+
const httpsMatch = gitUrl.match(/^https?:\/\/([^\/]+)\/(.+?)(\.git)?$/);
96+
if (httpsMatch) {
97+
const host = httpsMatch[1];
98+
const path = httpsMatch[2];
99+
return `${host}/${path}`;
100+
}
101+
102+
// If no match, return null
103+
return null;
104+
} catch {
105+
return null;
106+
}
107+
}
108+
109+
/**
110+
* Get a repository identifier from a path
111+
* First tries to use git remote URL, falls back to path-based identifier
112+
*
113+
* @param dirPath Directory path
114+
* @returns Repository identifier or null
115+
*/
116+
export function getRepositoryIdentifier(dirPath: string): string | null {
117+
// Try to get git remote URL
118+
const gitUrl = extractGitRemoteUrl(dirPath);
119+
120+
if (gitUrl) {
121+
const identifier = parseGitUrl(gitUrl);
122+
if (identifier) {
123+
console.log(`[GIT-UTILS] Repository identified via git remote: ${identifier}`);
124+
return identifier;
125+
}
126+
}
127+
128+
// If not a git repo or parsing fails, return null
129+
// The caller will handle the fallback to path-based identification
130+
return null;
30131
}

0 commit comments

Comments
 (0)