Inside your getFiles method, you can add some extra logic to see if the file that you are currently parsing is a directory, case in which you can loop over the files in that directory and push them to a result set array (filesArray). My example below only takes into account level 2 folders, such as in the /views/test/prototype.html scenario that you provided:
 module.exports = function (router) {
     var fs = require('fs');
     router.get('/file-list', function (req, res) {
         var markup = function (files, callback) {
             var items = [];
             for (i = 0; i < files.length; i++) {
                 var q = files[i];
                 q = q.slice(0, -5);
                 var markup = '<li>' + '<a href="' + q + '">' + q + '</a>' + '</li>';
                 items.push(markup);
             };
             callback(items);
         };
         var getFiles = function (callback) {
             var rootDir = __dirname + '/views';
             fs.readdir(rootDir, function (err, files) { // '/' denotes the root folder
                 if (err) throw err;
                 // Subdirectory parsing logic START
                 var filesArray = [];
                 for (var i = 0; i < files.length; i++) {
                     var subdirPath = rootDir + '/' + files[i];
                     fs.stat(subdirPath, function (err, stats) {
                         if (err) {
                             console.log(err);
                             throw err;
                         }
                         if (stats.isDirectory()) {
                             fs.readdir(subdirPath, function (err, subdirFiles) {
                                 if (err) throw err;
                                 Array.prototype.forEach.call(subdirFiles, function (file) {
                                     filesArray.push(file);
                                 });
                             });
                         } else {
                             filesArray.push(files[i]);
                         }
                     });
                 }
                 // Subdirectory parsing logic END
                 markup(filesArray, callback)
             });
         };
         getFiles(function (items) {
             // render markup for items
             res.render('file-list', {
                 'files': items
             });
         });
     });
 }
The above example is a crude one, as it would be recommended to use a mechanism such as Promises that allows you to better control the asynchronous looping going around and make sure that you have parsed all the individual files before calling markup().
UPDATE #1
I've been trying various approaches for trying to crawl all directories and subdirectories using promises, with no success, but I did manage to come across a working solution for your scenario in this Stack Overflow accepted answer - to be more precise, it's the parallel search mechanism provided in the aforementioned answer.
Below is an adaptation of the parallel search mechanism blended in with your existing code base - I've tested it and it works as intended:
var fs = require('fs');
var path = require('path');
module.exports = function (router) {
    router.get('/file-list', function (req, res) {
        var getFiles = function (callback) {
            var rootDir = path.join(__dirname, 'views');
            walk(rootDir, function (err, results) {
                if (err) {
                    console.log(err);
                    return;
                }
                markup(results, callback);
            });
        };
        var walk = function (dir, done) {
            var results = [];
            fs.readdir(dir, function (err, list) {
                if (err) {
                    return done(err);
                }
                var pending = list.length;
                if (!pending) {
                    return done(null, results);
                }
                list.forEach(function (file) {
                    file = path.resolve(dir, file);
                    fs.stat(file, function (err, stat) {
                        if (stat && stat.isDirectory()) {
                            walk(file, function (err, res) {
                                results = results.concat(res);
                                if (!--pending) {
                                    done(null, results);
                                }
                            });
                        } else {
                            results.push(file);
                            if (!--pending) {
                                done(null, results);
                            }
                        }
                    });
                });
            });
        };
        var markup = function (files, callback) {
            var items = [];
            for (i = 0; i < files.length; i++) {
                var q = files[i];
                q = q.slice(0, -5);
                var markup = '<li>' + '<a href="' + q + '">' + q + '</a>' + '</li>';
                items.push(markup);
            };
            callback(items);
        };
        getFiles(function (items) {
            // render markup for items
            res.render('file-list', {
                'files': items
            });
        });
    });
};