11import * 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 {
2729export 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 ( / ^ g i t @ ( [ ^ : ] + ) : ( .+ ?) ( \. g i t ) ? $ / ) ;
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 ( / ^ h t t p s ? : \/ \/ ( [ ^ \/ ] + ) \/ ( .+ ?) ( \. g i t ) ? $ / ) ;
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