diff --git a/NodeApp/src/helpers/Dojo/ExerciseHelper.ts b/NodeApp/src/helpers/Dojo/ExerciseHelper.ts
new file mode 100644
index 0000000000000000000000000000000000000000..9edd5a4707e44998317638c0385841816a751680
--- /dev/null
+++ b/NodeApp/src/helpers/Dojo/ExerciseHelper.ts
@@ -0,0 +1,140 @@
+import Exercise           from '../../sharedByClients/models/Exercise';
+import ora                from 'ora';
+import TextStyle          from '../../types/TextStyle';
+import inquirer           from 'inquirer';
+import Config             from '../../config/Config';
+import DojoBackendManager from '../../managers/DojoBackendManager';
+
+
+class ExerciseHelper {
+    /**
+     * Clone the exercise repository
+     * @param exercise
+     * @param providedPath If a string is provided, the repository will be cloned in the specified directory. If true, the repository will be cloned in the current directory. If false, the repository will not be cloned, and if undefined, the user will be prompted for the path.
+     */
+    async clone(exercise: Exercise, providedPath: string | boolean | undefined) {
+        if ( providedPath === false ) {
+            return;
+        }
+
+        let path: string | boolean = './';
+        if ( providedPath === undefined ) {
+            path = (await inquirer.prompt([ {
+                type   : 'input',
+                name   : 'path',
+                message: `Please enter the path (blank, '.' or './' for current directory):`
+            } ])).path;
+        } else {
+            path = providedPath;
+        }
+
+        console.log(TextStyle.BLOCK('Please wait while we are cloning the repository...'));
+
+        await Config.gitlabManager.cloneRepository(path === '' ? true : path, exercise.gitlabCreationInfo.ssh_url_to_repo, `DojoExercise_${ exercise.assignmentName }`, true, 0);
+    }
+
+    async delete(exerciseIdOrUrl: string) {
+        console.log(TextStyle.BLOCK('Please wait while we are deleting the exercise...'));
+
+        await DojoBackendManager.deleteExercise(exerciseIdOrUrl);
+    }
+
+    async actionMenu(exercise: Exercise): Promise<void> {
+        // eslint-disable-next-line no-constant-condition
+        while ( true ) {
+            const action: string = (await inquirer.prompt([ {
+                type   : 'list',
+                name   : 'action',
+                message: 'What action do you want to do on the exercise ?',
+                choices: [ {
+                    name : 'Display details of the exercise',
+                    value: 'info'
+                }, new inquirer.Separator(), {
+                    name : 'Clone (SSH required) in current directory (will create a subdirectory)',
+                    value: 'cloneInCurrentDirectory'
+                }, {
+                    name : 'Clone (SSH required) in the specified directory (will create a subdirectory)',
+                    value: 'clone'
+                }, new inquirer.Separator(), {
+                    name : 'Delete the exercise',
+                    value: 'delete'
+                }, new inquirer.Separator(), {
+                    name : 'Exit',
+                    value: 'exit'
+                }, new inquirer.Separator() ]
+            } ])).action;
+
+            switch ( action ) {
+                case 'info':
+                    await this.displayDetails(exercise, false);
+                    break;
+                case 'cloneInCurrentDirectory':
+                    await this.clone(exercise, true);
+                    break;
+                case 'clone':
+                    await this.clone(exercise, undefined);
+                    break;
+                case 'delete':
+                    await this.delete(exercise.id);
+                    return;
+                case 'exit':
+                    return;
+                default:
+                    ora().fail('Invalid option.');
+                    return;
+            }
+        }
+    }
+
+    async displayDetails(exercise: Exercise, showActionMenu: boolean = false): Promise<void> {
+        ora().info(`Details of the exercise:`);
+
+        const oraInfo = (message: string, indent: number = 4) => {
+            ora({
+                    text  : message,
+                    indent: indent
+                }).start().info();
+        };
+
+        oraInfo(`${ TextStyle.LIST_ITEM_NAME('Id:') } ${ exercise.id }`);
+        oraInfo(`${ TextStyle.LIST_ITEM_NAME('Name:') } ${ exercise.name }`);
+        oraInfo(`${ TextStyle.LIST_ITEM_NAME('Assignment:') } ${ exercise.assignmentName }`);
+
+        // Display exercise teachers
+        if ( exercise.assignment?.staff && exercise.assignment?.staff.length > 0 ) {
+            oraInfo(`${ TextStyle.LIST_ITEM_NAME('Teachers:') }`);
+            exercise.assignment?.staff.forEach(staff => {
+                console.log(`        - ${ staff.gitlabUsername }`);
+            });
+        } else {
+            ora({
+                    text  : `${ TextStyle.LIST_ITEM_NAME('Teachers:') } No teachers found for this exercise.`,
+                    indent: 4
+                }).start().warn();
+        }
+
+        // Display exercise members
+        if ( exercise.members && exercise.members.length > 0 ) {
+            oraInfo(`${ TextStyle.LIST_ITEM_NAME('Members:') }`);
+            exercise.members.forEach(member => {
+                console.log(`        - ${ member.gitlabUsername }`);
+            });
+        } else {
+            ora({
+                    text  : `${ TextStyle.LIST_ITEM_NAME('Members:') } No members found for this exercise.`,
+                    indent: 4
+                }).start().warn();
+        }
+
+        oraInfo(`${ TextStyle.LIST_ITEM_NAME('Gitlab URL:') } ${ exercise.gitlabCreationInfo.web_url }`);
+        oraInfo(`${ TextStyle.LIST_ITEM_NAME('HTTP Repo:') } ${ exercise.gitlabCreationInfo.http_url_to_repo }`);
+        oraInfo(`${ TextStyle.LIST_ITEM_NAME('SSH Repo:') } ${ exercise.gitlabCreationInfo.ssh_url_to_repo }`);
+
+        if ( showActionMenu ) {
+            await this.actionMenu(exercise);
+        }
+    }
+}
+
+
+export default new ExerciseHelper();
\ No newline at end of file