@@ -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,32 @@ 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
5761 // If already cloned and version matches, just update
5862 if ( isRepoCloned ( config . name ) ) {
59- return await updateRepo ( config . name ) ;
63+ log ?.( `${ config . name } : Already cloned, updating` , "debug" ) ;
64+ return await updateRepo ( config . name , log ) ;
6065 }
6166
62- const git : SimpleGit = simpleGit ( ) ;
63-
6467 // Determine ref to checkout: commit > tag > branch
6568 const ref = config . commit || config . tag || config . branch || "default" ;
6669 const refType = config . commit ? "commit" : config . tag ? "tag" : "branch" ;
70+ const isSparse = config . sparse && config . sparse . length > 0 ;
71+
72+ log ?.( `${ config . name } : Cloning @ ${ ref } (${ refType } ${ isSparse ? ", sparse" : "" } )` , "info" ) ;
73+
74+ const progressHandler = log
75+ ? ( data : { method : string ; stage : string ; progress : number } ) => {
76+ log ( `${ config . name } : ${ data . method } ${ data . stage } ${ data . progress } %` , "debug" ) ;
77+ }
78+ : undefined ;
79+
80+ const git : SimpleGit = simpleGit ( { progress : progressHandler } ) ;
6781
68- if ( config . sparse && config . sparse . length > 0 ) {
82+ if ( isSparse ) {
6983 // Clone with sparse checkout for large repos
7084 if ( config . commit ) {
7185 // For commits, we need full history to fetch the commit
@@ -75,9 +89,13 @@ export async function cloneRepo(
7589 "--no-checkout" ,
7690 ] ) ;
7791
78- const repoGit = simpleGit ( repoPath ) ;
79- await repoGit . raw ( [ "sparse-checkout" , "set" , ...config . sparse ] ) ;
92+ const repoGit = simpleGit ( { baseDir : repoPath , progress : progressHandler } ) ;
93+ await repoGit . raw ( [ "config" , "gc.auto" , "0" ] ) ;
94+ log ?.( `${ config . name } : Setting sparse checkout paths: ${ config . sparse ! . join ( ", " ) } ` , "debug" ) ;
95+ await repoGit . raw ( [ "sparse-checkout" , "set" , ...config . sparse ! ] ) ;
96+ log ?.( `${ config . name } : Fetching commit ${ config . commit . substring ( 0 , 7 ) } ` , "info" ) ;
8097 await repoGit . fetch ( [ "origin" , config . commit ] ) ;
98+ log ?.( `${ config . name } : Checking out commit` , "debug" ) ;
8199 await repoGit . checkout ( config . commit ) ;
82100 } else if ( config . tag ) {
83101 await git . clone ( config . url , repoPath , [
@@ -86,21 +104,28 @@ export async function cloneRepo(
86104 "--no-checkout" ,
87105 ] ) ;
88106
89- const repoGit = simpleGit ( repoPath ) ;
90- await repoGit . raw ( [ "sparse-checkout" , "set" , ...config . sparse ] ) ;
107+ const repoGit = simpleGit ( { baseDir : repoPath , progress : progressHandler } ) ;
108+ await repoGit . raw ( [ "config" , "gc.auto" , "0" ] ) ;
109+ log ?.( `${ config . name } : Setting sparse checkout paths: ${ config . sparse ! . join ( ", " ) } ` , "debug" ) ;
110+ await repoGit . raw ( [ "sparse-checkout" , "set" , ...config . sparse ! ] ) ;
111+ log ?.( `${ config . name } : Fetching tag ${ config . tag } ` , "info" ) ;
91112 await repoGit . fetch ( [ "--depth=1" , "origin" , `refs/tags/${ config . tag } :refs/tags/${ config . tag } ` ] ) ;
113+ log ?.( `${ config . name } : Checking out tag` , "debug" ) ;
92114 await repoGit . checkout ( config . tag ) ;
93115
94116 // Apply sparse path overrides from different branches
95117 if ( config . sparsePathOverrides ) {
96118 for ( const override of config . sparsePathOverrides ) {
119+ log ?.( `${ config . name } : Fetching override branch ${ override . branch } ` , "debug" ) ;
97120 await repoGit . fetch ( [ "--depth=1" , "origin" , override . branch ] ) ;
98121 try {
122+ log ?.( `${ config . name } : Checking out override paths from ${ override . branch } : ${ override . paths . join ( ", " ) } ` , "debug" ) ;
99123 await repoGit . checkout ( [ `origin/${ override . branch } ` , "--" , ...override . paths ] ) ;
100124 } catch ( error ) {
101125 const repoBase = config . url . replace ( / \. g i t $ / , "" ) ;
102126 const parentDirs = [ ...new Set ( override . paths . map ( ( p ) => p . split ( "/" ) . slice ( 0 , - 1 ) . join ( "/" ) ) ) ] ;
103127 const browseLinks = parentDirs . map ( ( d ) => `${ repoBase } /tree/${ override . branch } /${ d } ` ) ;
128+ log ?.( `${ config . name } : sparsePathOverrides failed for branch "${ override . branch } "` , "error" ) ;
104129 throw new Error (
105130 `sparsePathOverrides failed for branch "${ override . branch } ": could not checkout paths [${ override . paths . join ( ", " ) } ]. ` +
106131 `Check the actual folder names at: ${ browseLinks . join ( " , " ) } ` ,
@@ -116,24 +141,31 @@ export async function cloneRepo(
116141 ...( config . branch ? [ "-b" , config . branch ] : [ ] ) ,
117142 ] ) ;
118143
119- const repoGit = simpleGit ( repoPath ) ;
120- await repoGit . raw ( [ "sparse-checkout" , "set" , ...config . sparse ] ) ;
144+ const repoGit = simpleGit ( { baseDir : repoPath , progress : progressHandler } ) ;
145+ await repoGit . raw ( [ "config" , "gc.auto" , "0" ] ) ;
146+ log ?.( `${ config . name } : Setting sparse checkout paths: ${ config . sparse ! . join ( ", " ) } ` , "debug" ) ;
147+ await repoGit . raw ( [ "sparse-checkout" , "set" , ...config . sparse ! ] ) ;
121148 }
122149
123- return `Cloned ${ config . name } @ ${ ref } (${ refType } , sparse: ${ config . sparse . join ( ", " ) } )` ;
150+ log ?.( `${ config . name } : Clone complete` , "info" ) ;
151+ return `Cloned ${ config . name } @ ${ ref } (${ refType } , sparse: ${ config . sparse ! . join ( ", " ) } )` ;
124152 } else {
125153 // Clone for smaller repos
126154 if ( config . commit ) {
127155 // For commits, clone and checkout specific commit
128156 await git . clone ( config . url , repoPath , [ "--no-checkout" ] ) ;
129- const repoGit = simpleGit ( repoPath ) ;
157+ const repoGit = simpleGit ( { baseDir : repoPath , progress : progressHandler } ) ;
158+ log ?.( `${ config . name } : Fetching commit ${ config . commit . substring ( 0 , 7 ) } ` , "info" ) ;
130159 await repoGit . fetch ( [ "origin" , config . commit ] ) ;
160+ log ?.( `${ config . name } : Checking out commit` , "debug" ) ;
131161 await repoGit . checkout ( config . commit ) ;
132162 } else if ( config . tag ) {
133163 // Clone and checkout tag
134164 await git . clone ( config . url , repoPath , [ "--no-checkout" ] ) ;
135- const repoGit = simpleGit ( repoPath ) ;
165+ const repoGit = simpleGit ( { baseDir : repoPath , progress : progressHandler } ) ;
166+ log ?.( `${ config . name } : Fetching tag ${ config . tag } ` , "info" ) ;
136167 await repoGit . fetch ( [ "--depth=1" , "origin" , `refs/tags/${ config . tag } :refs/tags/${ config . tag } ` ] ) ;
168+ log ?.( `${ config . name } : Checking out tag` , "debug" ) ;
137169 await repoGit . checkout ( config . tag ) ;
138170 } else {
139171 await git . clone ( config . url , repoPath , [
@@ -142,32 +174,38 @@ export async function cloneRepo(
142174 ] ) ;
143175 }
144176
177+ log ?.( `${ config . name } : Clone complete` , "info" ) ;
145178 return `Cloned ${ config . name } @ ${ ref } (${ refType } )` ;
146179 }
147180}
148181
149182/**
150183 * Update an existing repository
151184 */
152- export async function updateRepo ( repoName : string ) : Promise < string > {
185+ export async function updateRepo ( repoName : string , log ?: Logger ) : Promise < string > {
153186 const repoPath = getRepoPath ( repoName ) ;
154187
155188 if ( ! isRepoCloned ( repoName ) ) {
156189 throw new Error ( `Repository ${ repoName } is not cloned` ) ;
157190 }
158191
192+ log ?.( `${ repoName } : Updating` , "info" ) ;
159193 const git = simpleGit ( repoPath ) ;
160194
161195 try {
162196 await git . fetch ( [ "--depth=1" ] ) ;
163197 await git . reset ( [ "--hard" , "origin/HEAD" ] ) ;
198+ log ?.( `${ repoName } : Update complete` , "info" ) ;
164199 return `Updated ${ repoName } ` ;
165200 } catch ( error ) {
201+ log ?.( `${ repoName } : Fetch failed, trying pull` , "warning" ) ;
166202 // If fetch fails, try a simple pull
167203 try {
168204 await git . pull ( ) ;
205+ log ?.( `${ repoName } : Pull complete` , "info" ) ;
169206 return `Updated ${ repoName } ` ;
170207 } catch ( pullError ) {
208+ log ?.( `${ repoName } : Update failed: ${ pullError } ` , "error" ) ;
171209 return `Failed to update ${ repoName } : ${ pullError } ` ;
172210 }
173211 }
0 commit comments