@@ -8,6 +8,8 @@ import { join } from "path";
88import { homedir } from "os" ;
99import { RepoConfig } from "../repos/config.js" ;
1010
11+ export type Logger = ( message : string , level ?: "info" | "debug" | "warning" | "error" ) => void ;
12+
1113/** Base directory for cloned repos */
1214export const REPOS_DIR = join (
1315 process . env . AZTEC_MCP_REPOS_DIR || join ( homedir ( ) , ".aztec-mcp" ) ,
@@ -41,7 +43,8 @@ export function isRepoCloned(repoName: string): boolean {
4143 */
4244export async function cloneRepo (
4345 config : RepoConfig ,
44- force : boolean = false
46+ force : boolean = false ,
47+ log ?: Logger
4548) : Promise < string > {
4649 ensureReposDir ( ) ;
4750 const repoPath = getRepoPath ( config . name ) ;
@@ -51,21 +54,36 @@ export async function cloneRepo(
5154
5255 // Remove existing if force is set or version changed
5356 if ( ( force || versionMismatch ) && existsSync ( repoPath ) ) {
57+ log ?.( `${ config . name } : Removing existing clone (force=${ force } , versionMismatch=${ versionMismatch } )` , "debug" ) ;
5458 rmSync ( repoPath , { recursive : true , force : true } ) ;
5559 }
5660
57- // If already cloned and version matches, just update
61+ // If already cloned and version matches, skip or update
5862 if ( isRepoCloned ( config . name ) ) {
59- return await updateRepo ( config . name ) ;
63+ if ( config . tag || config . commit ) {
64+ log ?.( `${ config . name } : Already cloned at correct ${ config . tag ? "tag" : "commit" } , skipping` , "debug" ) ;
65+ return `${ config . name } already at ${ config . commit || config . tag } ` ;
66+ }
67+ log ?.( `${ config . name } : Already cloned, updating` , "debug" ) ;
68+ return await updateRepo ( config . name , log ) ;
6069 }
6170
62- const git : SimpleGit = simpleGit ( ) ;
63-
6471 // Determine ref to checkout: commit > tag > branch
6572 const ref = config . commit || config . tag || config . branch || "default" ;
6673 const refType = config . commit ? "commit" : config . tag ? "tag" : "branch" ;
74+ const isSparse = config . sparse && config . sparse . length > 0 ;
6775
68- if ( config . sparse && config . sparse . length > 0 ) {
76+ log ?.( `${ config . name } : Cloning @ ${ ref } (${ refType } ${ isSparse ? ", sparse" : "" } )` , "info" ) ;
77+
78+ const progressHandler = log
79+ ? ( data : { method : string ; stage : string ; progress : number } ) => {
80+ log ( `${ config . name } : ${ data . method } ${ data . stage } ${ data . progress } %` , "debug" ) ;
81+ }
82+ : undefined ;
83+
84+ const git : SimpleGit = simpleGit ( { progress : progressHandler } ) ;
85+
86+ if ( isSparse ) {
6987 // Clone with sparse checkout for large repos
7088 if ( config . commit ) {
7189 // For commits, we need full history to fetch the commit
@@ -75,10 +93,13 @@ export async function cloneRepo(
7593 "--no-checkout" ,
7694 ] ) ;
7795
78- const repoGit = simpleGit ( repoPath ) ;
96+ const repoGit = simpleGit ( { baseDir : repoPath , progress : progressHandler } ) ;
7997 await repoGit . raw ( [ "config" , "gc.auto" , "0" ] ) ;
80- await repoGit . raw ( [ "sparse-checkout" , "set" , ...config . sparse ] ) ;
98+ log ?.( `${ config . name } : Setting sparse checkout paths: ${ config . sparse ! . join ( ", " ) } ` , "debug" ) ;
99+ await repoGit . raw ( [ "sparse-checkout" , "set" , ...config . sparse ! ] ) ;
100+ log ?.( `${ config . name } : Fetching commit ${ config . commit . substring ( 0 , 7 ) } ` , "info" ) ;
81101 await repoGit . fetch ( [ "origin" , config . commit ] ) ;
102+ log ?.( `${ config . name } : Checking out commit` , "debug" ) ;
82103 await repoGit . checkout ( config . commit ) ;
83104 } else if ( config . tag ) {
84105 await git . clone ( config . url , repoPath , [
@@ -87,29 +108,14 @@ export async function cloneRepo(
87108 "--no-checkout" ,
88109 ] ) ;
89110
90- const repoGit = simpleGit ( repoPath ) ;
111+ const repoGit = simpleGit ( { baseDir : repoPath , progress : progressHandler } ) ;
91112 await repoGit . raw ( [ "config" , "gc.auto" , "0" ] ) ;
92- await repoGit . raw ( [ "sparse-checkout" , "set" , ...config . sparse ] ) ;
113+ log ?.( `${ config . name } : Setting sparse checkout paths: ${ config . sparse ! . join ( ", " ) } ` , "debug" ) ;
114+ await repoGit . raw ( [ "sparse-checkout" , "set" , ...config . sparse ! ] ) ;
115+ log ?.( `${ config . name } : Fetching tag ${ config . tag } ` , "info" ) ;
93116 await repoGit . fetch ( [ "--depth=1" , "origin" , `refs/tags/${ config . tag } :refs/tags/${ config . tag } ` ] ) ;
117+ log ?.( `${ config . name } : Checking out tag` , "debug" ) ;
94118 await repoGit . checkout ( config . tag ) ;
95-
96- // Apply sparse path overrides from different branches
97- if ( config . sparsePathOverrides ) {
98- for ( const override of config . sparsePathOverrides ) {
99- await repoGit . fetch ( [ "--depth=1" , "origin" , override . branch ] ) ;
100- try {
101- await repoGit . checkout ( [ `origin/${ override . branch } ` , "--" , ...override . paths ] ) ;
102- } catch ( error ) {
103- const repoBase = config . url . replace ( / \. g i t $ / , "" ) ;
104- const parentDirs = [ ...new Set ( override . paths . map ( ( p ) => p . split ( "/" ) . slice ( 0 , - 1 ) . join ( "/" ) ) ) ] ;
105- const browseLinks = parentDirs . map ( ( d ) => `${ repoBase } /tree/${ override . branch } /${ d } ` ) ;
106- throw new Error (
107- `sparsePathOverrides failed for branch "${ override . branch } ": could not checkout paths [${ override . paths . join ( ", " ) } ]. ` +
108- `Check the actual folder names at: ${ browseLinks . join ( " , " ) } ` ,
109- ) ;
110- }
111- }
112- }
113119 } else {
114120 await git . clone ( config . url , repoPath , [
115121 "--filter=blob:none" ,
@@ -118,25 +124,31 @@ export async function cloneRepo(
118124 ...( config . branch ? [ "-b" , config . branch ] : [ ] ) ,
119125 ] ) ;
120126
121- const repoGit = simpleGit ( repoPath ) ;
127+ const repoGit = simpleGit ( { baseDir : repoPath , progress : progressHandler } ) ;
122128 await repoGit . raw ( [ "config" , "gc.auto" , "0" ] ) ;
123- await repoGit . raw ( [ "sparse-checkout" , "set" , ...config . sparse ] ) ;
129+ log ?.( `${ config . name } : Setting sparse checkout paths: ${ config . sparse ! . join ( ", " ) } ` , "debug" ) ;
130+ await repoGit . raw ( [ "sparse-checkout" , "set" , ...config . sparse ! ] ) ;
124131 }
125132
126- return `Cloned ${ config . name } @ ${ ref } (${ refType } , sparse: ${ config . sparse . join ( ", " ) } )` ;
133+ log ?.( `${ config . name } : Clone complete` , "info" ) ;
134+ return `Cloned ${ config . name } @ ${ ref } (${ refType } , sparse: ${ config . sparse ! . join ( ", " ) } )` ;
127135 } else {
128136 // Clone for smaller repos
129137 if ( config . commit ) {
130138 // For commits, clone and checkout specific commit
131139 await git . clone ( config . url , repoPath , [ "--no-checkout" ] ) ;
132- const repoGit = simpleGit ( repoPath ) ;
140+ const repoGit = simpleGit ( { baseDir : repoPath , progress : progressHandler } ) ;
141+ log ?.( `${ config . name } : Fetching commit ${ config . commit . substring ( 0 , 7 ) } ` , "info" ) ;
133142 await repoGit . fetch ( [ "origin" , config . commit ] ) ;
143+ log ?.( `${ config . name } : Checking out commit` , "debug" ) ;
134144 await repoGit . checkout ( config . commit ) ;
135145 } else if ( config . tag ) {
136146 // Clone and checkout tag
137147 await git . clone ( config . url , repoPath , [ "--no-checkout" ] ) ;
138- const repoGit = simpleGit ( repoPath ) ;
148+ const repoGit = simpleGit ( { baseDir : repoPath , progress : progressHandler } ) ;
149+ log ?.( `${ config . name } : Fetching tag ${ config . tag } ` , "info" ) ;
139150 await repoGit . fetch ( [ "--depth=1" , "origin" , `refs/tags/${ config . tag } :refs/tags/${ config . tag } ` ] ) ;
151+ log ?.( `${ config . name } : Checking out tag` , "debug" ) ;
140152 await repoGit . checkout ( config . tag ) ;
141153 } else {
142154 await git . clone ( config . url , repoPath , [
@@ -145,32 +157,38 @@ export async function cloneRepo(
145157 ] ) ;
146158 }
147159
160+ log ?.( `${ config . name } : Clone complete` , "info" ) ;
148161 return `Cloned ${ config . name } @ ${ ref } (${ refType } )` ;
149162 }
150163}
151164
152165/**
153166 * Update an existing repository
154167 */
155- export async function updateRepo ( repoName : string ) : Promise < string > {
168+ export async function updateRepo ( repoName : string , log ?: Logger ) : Promise < string > {
156169 const repoPath = getRepoPath ( repoName ) ;
157170
158171 if ( ! isRepoCloned ( repoName ) ) {
159172 throw new Error ( `Repository ${ repoName } is not cloned` ) ;
160173 }
161174
175+ log ?.( `${ repoName } : Updating` , "info" ) ;
162176 const git = simpleGit ( repoPath ) ;
163177
164178 try {
165179 await git . fetch ( [ "--depth=1" ] ) ;
166180 await git . reset ( [ "--hard" , "origin/HEAD" ] ) ;
181+ log ?.( `${ repoName } : Update complete` , "info" ) ;
167182 return `Updated ${ repoName } ` ;
168183 } catch ( error ) {
184+ log ?.( `${ repoName } : Fetch failed, trying pull` , "warning" ) ;
169185 // If fetch fails, try a simple pull
170186 try {
171187 await git . pull ( ) ;
188+ log ?.( `${ repoName } : Pull complete` , "info" ) ;
172189 return `Updated ${ repoName } ` ;
173190 } catch ( pullError ) {
191+ log ?.( `${ repoName } : Update failed: ${ pullError } ` , "error" ) ;
174192 return `Failed to update ${ repoName } : ${ pullError } ` ;
175193 }
176194 }
0 commit comments