diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml
new file mode 100644
index 0000000000000000000000000000000000000000..03d9549ea8e4ada36fb3ecbc30fef08175b7d728
--- /dev/null
+++ b/.idea/inspectionProfiles/Project_Default.xml
@@ -0,0 +1,6 @@
+<component name="InspectionProjectProfileManager">
+  <profile version="1.0">
+    <option name="myName" value="Project Default" />
+    <inspection_tool class="Eslint" enabled="true" level="WARNING" enabled_by_default="true" />
+  </profile>
+</component>
\ No newline at end of file
diff --git a/NodeApp/.env.vault b/NodeApp/.env.vault
index dfdcf927c6197bf63486a9fb84b97e47db10945c..1b27b44793a575e76c83badf684b00dae574c548 100644
--- a/NodeApp/.env.vault
+++ b/NodeApp/.env.vault
@@ -1,6 +1,6 @@
 #/-------------------.env.vault---------------------/
 #/         cloud-agnostic vaulting standard         /
-#/   [how it works](https://dotenvx.com/env-vault)  /
+#/   [how it works](https://dotenv.org/env-vault)   /
 #/--------------------------------------------------/
 
 # development
diff --git a/NodeApp/.idea/vcs.xml b/NodeApp/.idea/vcs.xml
index 5e5bcd02ce8b098302940ffa22bb6d19b8d8986f..073c3af9249ca68df7259aa068e9081d1d0ddc0d 100644
--- a/NodeApp/.idea/vcs.xml
+++ b/NodeApp/.idea/vcs.xml
@@ -2,7 +2,7 @@
 <project version="4">
   <component name="VcsDirectoryMappings">
     <mapping directory="$PROJECT_DIR$/.." vcs="Git" />
-    <mapping directory="$PROJECT_DIR$/DojoExercise_Technique_de compilation - TP" vcs="Git" />
+    <mapping directory="$PROJECT_DIR$/DojoExercise_c_hello_world" vcs="Git" />
     <mapping directory="$PROJECT_DIR$/src/shared" vcs="Git" />
     <mapping directory="$PROJECT_DIR$/src/sharedByClients" vcs="Git" />
   </component>
diff --git a/NodeApp/package-lock.json b/NodeApp/package-lock.json
index 50f206cf82c6e9959e972830450524d4c9828ec7..2360b3fcf03826add521e726b48c60f8c19f7b23 100644
--- a/NodeApp/package-lock.json
+++ b/NodeApp/package-lock.json
@@ -22,6 +22,7 @@
                 "commander": "^13.1.0",
                 "form-data": "^4.0.2",
                 "fs-extra": "^11.3.0",
+                "fuse.js": "^7.1.0",
                 "http-status-codes": "^2.3.0",
                 "inquirer": "^8.2.6",
                 "json5": "^2.2.3",
@@ -4000,6 +4001,15 @@
                 "url": "https://github.com/sponsors/ljharb"
             }
         },
+        "node_modules/fuse.js": {
+            "version": "7.1.0",
+            "resolved": "https://registry.npmjs.org/fuse.js/-/fuse.js-7.1.0.tgz",
+            "integrity": "sha512-trLf4SzuuUxfusZADLINj+dE8clK1frKdmqiJNb1Es75fmI5oY6X2mxLVUciLLjxqw/xr72Dhy+lER6dGd02FQ==",
+            "license": "Apache-2.0",
+            "engines": {
+                "node": ">=10"
+            }
+        },
         "node_modules/genversion": {
             "version": "3.2.0",
             "resolved": "https://registry.npmjs.org/genversion/-/genversion-3.2.0.tgz",
diff --git a/NodeApp/package.json b/NodeApp/package.json
index 0370c080636c251eefc826fe5bd58941a24a16f3..e1cf3e0fa9bbd4662deb74cb9fc4fcf3134ab4b5 100644
--- a/NodeApp/package.json
+++ b/NodeApp/package.json
@@ -46,6 +46,7 @@
         "commander"                 : "^13.1.0",
         "form-data"                 : "^4.0.2",
         "fs-extra"                  : "^11.3.0",
+        "fuse.js"                   : "^7.1.0",
         "http-status-codes"         : "^2.3.0",
         "inquirer"                  : "^8.2.6",
         "json5"                     : "^2.2.3",
diff --git a/NodeApp/src/commander/exercise/ExerciseCommand.ts b/NodeApp/src/commander/exercise/ExerciseCommand.ts
index 02e40e36a7bd369b056c2cf5a8303e796d8af5f5..882e3e3bd53aa7010e49e0359eb44d1039dcf0eb 100644
--- a/NodeApp/src/commander/exercise/ExerciseCommand.ts
+++ b/NodeApp/src/commander/exercise/ExerciseCommand.ts
@@ -3,6 +3,8 @@ import ExerciseCreateCommand     from './subcommands/ExerciseCreateCommand.js';
 import ExerciseRunCommand        from './subcommands/ExerciseRunCommand.js';
 import ExerciseCorrectionCommand from './subcommands/ExerciseCorrectionCommand.js';
 import ExerciseDeleteCommand     from './subcommands/ExerciseDeleteCommand';
+import ExerciseSearchCommand     from './subcommands/ExerciseSearchCommand';
+import ExerciseInfoCommand       from './subcommands/ExerciseInfoCommand';
 
 
 class ExerciseCommand extends CommanderCommand {
@@ -18,6 +20,10 @@ class ExerciseCommand extends CommanderCommand {
         ExerciseRunCommand.registerOnCommand(this.command);
         ExerciseDeleteCommand.registerOnCommand(this.command);
         ExerciseCorrectionCommand.registerOnCommand(this.command);
+        ExerciseSearchCommand.registerOnCommand(this.command);
+        ExerciseInfoCommand.registerOnCommand(this.command);
+        // ExerciseResultCommand.registerOnCommand(this.command);
+        // ExerciseSummaryCommand.registerOnCommand(this.command);
     }
 
     protected async commandAction(): Promise<void> {
diff --git a/NodeApp/src/commander/exercise/subcommands/ExerciseCreateCommand.ts b/NodeApp/src/commander/exercise/subcommands/ExerciseCreateCommand.ts
index 16b70b79064d215c89e112b89c9b14c5e00ceeed..c354b0100d4dd29e9dc28ad8fcc70d55d488ec12 100644
--- a/NodeApp/src/commander/exercise/subcommands/ExerciseCreateCommand.ts
+++ b/NodeApp/src/commander/exercise/subcommands/ExerciseCreateCommand.ts
@@ -10,6 +10,7 @@ import inquirer            from 'inquirer';
 import Config              from '../../../config/Config';
 import ClientsSharedConfig from '../../../sharedByClients/config/ClientsSharedConfig';
 import { Option }          from 'commander';
+import ExerciseHelper      from '../../../helpers/Dojo/ExerciseHelper';
 
 
 type CommandOptions = { assignment: string, members_id?: Array<number>, members_username?: Array<string>, clone?: string | boolean, force?: boolean };
@@ -161,26 +162,7 @@ class ExerciseCreateCommand extends CommanderCommand {
 
         this.exercise = await DojoBackendManager.createExercise(this.assignment!.name, this.members!);
 
-        const oraInfo = (message: string) => {
-            ora({
-                    text  : message,
-                    indent: 4
-                }).start().info();
-        };
-
-        oraInfo(`${ TextStyle.LIST_ITEM_NAME('Id:') } ${ this.exercise.id }`);
-        oraInfo(`${ TextStyle.LIST_ITEM_NAME('Name:') } ${ this.exercise.name }`);
-        oraInfo(`${ TextStyle.LIST_ITEM_NAME('Web URL:') } ${ this.exercise.gitlabCreationInfo.web_url }`);
-        oraInfo(`${ TextStyle.LIST_ITEM_NAME('HTTP Repo:') } ${ this.exercise.gitlabCreationInfo.http_url_to_repo }`);
-        oraInfo(`${ TextStyle.LIST_ITEM_NAME('SSH Repo:') } ${ this.exercise.gitlabCreationInfo.ssh_url_to_repo }`);
-    }
-
-    private async cloneRepository(options: CommandOptions) {
-        if ( options.clone ) {
-            console.log(TextStyle.BLOCK('Please wait while we are cloning the repository...'));
-
-            await Config.gitlabManager.cloneRepository(options.clone, this.exercise.gitlabCreationInfo.ssh_url_to_repo, `DojoExercise_${ this.exercise.assignmentName }`, true, 0);
-        }
+        await ExerciseHelper.displayDetails(this.exercise);
     }
 
 
@@ -188,7 +170,7 @@ class ExerciseCreateCommand extends CommanderCommand {
         try {
             await this.dataRetrieval(options);
             await this.createExercise();
-            await this.cloneRepository(options);
+            await ExerciseHelper.clone(this.exercise, options.clone ?? false);
         } catch ( e ) { /* Do nothing */ }
     }
 }
diff --git a/NodeApp/src/commander/exercise/subcommands/ExerciseDeleteCommand.ts b/NodeApp/src/commander/exercise/subcommands/ExerciseDeleteCommand.ts
index 2ec20cdfd13b93306de1bf9095adc9c6d40abb5c..f974a0bbdf75bf42a47abf29f8e0d949e94820a8 100644
--- a/NodeApp/src/commander/exercise/subcommands/ExerciseDeleteCommand.ts
+++ b/NodeApp/src/commander/exercise/subcommands/ExerciseDeleteCommand.ts
@@ -1,7 +1,7 @@
-import CommanderCommand   from '../../CommanderCommand';
-import DojoBackendManager from '../../../managers/DojoBackendManager';
-import AccessesHelper     from '../../../helpers/AccessesHelper';
-import TextStyle          from '../../../types/TextStyle';
+import CommanderCommand from '../../CommanderCommand';
+import AccessesHelper   from '../../../helpers/AccessesHelper';
+import TextStyle        from '../../../types/TextStyle';
+import ExerciseHelper   from '../../../helpers/Dojo/ExerciseHelper';
 
 
 class ExerciseDeleteCommand extends CommanderCommand {
@@ -20,16 +20,10 @@ class ExerciseDeleteCommand extends CommanderCommand {
         await AccessesHelper.checkStudent();
     }
 
-    private async deleteExercise(exerciseIdOrUrl: string) {
-        console.log(TextStyle.BLOCK('Please wait while we are deleting the exercise...'));
-
-        await DojoBackendManager.deleteExercise(exerciseIdOrUrl);
-    }
-
     protected async commandAction(exerciseIdOrUrl: string): Promise<void> {
         try {
             await this.dataRetrieval();
-            await this.deleteExercise(exerciseIdOrUrl);
+            await ExerciseHelper.delete(exerciseIdOrUrl);
         } catch ( e ) { /* Do nothing */ }
     }
 }
diff --git a/NodeApp/src/commander/exercise/subcommands/ExerciseInfoCommand.ts b/NodeApp/src/commander/exercise/subcommands/ExerciseInfoCommand.ts
new file mode 100644
index 0000000000000000000000000000000000000000..944033b9eeeba433c9f299b636e03531bca54f7e
--- /dev/null
+++ b/NodeApp/src/commander/exercise/subcommands/ExerciseInfoCommand.ts
@@ -0,0 +1,54 @@
+import CommanderCommand   from '../../CommanderCommand';
+import AccessesHelper     from '../../../helpers/AccessesHelper';
+import TextStyle          from '../../../types/TextStyle';
+import ExerciseHelper     from '../../../helpers/Dojo/ExerciseHelper';
+import Exercise           from '../../../sharedByClients/models/Exercise';
+import DojoBackendManager from '../../../managers/DojoBackendManager';
+import ora                from 'ora';
+import Config             from '../../../config/Config';
+
+
+class ExerciseInfoCommand extends CommanderCommand {
+    protected commandName: string = 'info';
+
+    protected defineCommand(): void {
+        this.command
+            .description('delete an exercise')
+            .argument('id or url', 'id or url of the exercise')
+            .action(this.commandAction.bind(this));
+    }
+
+    private async dataRetrieval(exerciseIdOrUrl: string): Promise<Exercise> {
+        console.log(TextStyle.BLOCK('Please wait while we verify and retrieve data...'));
+
+        await AccessesHelper.checkStudent();
+
+
+        // Fetch exercise
+        const exercisesGetSpinner: ora.Ora = ora({
+                                                     text  : `Checking exercise`,
+                                                     indent: 4
+                                                 }).start();
+
+        const exercise = await DojoBackendManager.getExercise(exerciseIdOrUrl);
+
+        if ( !exercise ) {
+            exercisesGetSpinner.fail(`Exercise not found`);
+            throw new Error();
+        }
+
+        exercisesGetSpinner.succeed(`Exercise fetched successfully`);
+
+        return exercise;
+    }
+
+    protected async commandAction(exerciseIdOrUrl: string): Promise<void> {
+        try {
+            const exercise = await this.dataRetrieval(exerciseIdOrUrl);
+            return ExerciseHelper.displayDetails(exercise, Config.interactiveMode);
+        } catch ( e ) { /* Do nothing */ }
+    }
+}
+
+
+export default new ExerciseInfoCommand();
diff --git a/NodeApp/src/commander/exercise/subcommands/ExerciseResultCommand.ts b/NodeApp/src/commander/exercise/subcommands/ExerciseResultCommand.ts
new file mode 100644
index 0000000000000000000000000000000000000000..687fab7a55458b0a56d9a10459b8e0d0b3f5b4b7
--- /dev/null
+++ b/NodeApp/src/commander/exercise/subcommands/ExerciseResultCommand.ts
@@ -0,0 +1,124 @@
+import CommanderCommand   from '../../CommanderCommand';
+import chalk              from 'chalk';
+import ora                from 'ora';
+import DojoBackendManager from '../../../managers/DojoBackendManager';
+import Result             from '../../../sharedByClients/models/Result';
+import inquirer           from 'inquirer';
+
+
+// THIS COMMAND IS NOT WORKING YET - NEEDS TO BE REWRITTEN
+
+class ExerciseResultCommand extends CommanderCommand {
+    protected commandName: string = 'result';
+
+    protected defineCommand(): void {
+        this.command
+            .description('results of an exercise')
+            .argument('<idOrLink>', 'display results of a specific exercise by Id or Gitlab Link')
+            .action(this.commandAction.bind(this));
+    }
+
+    protected async commandAction(idOrLink: string): Promise<void> {
+        const spinner = ora('Fetching exercise results...').start();
+
+        try {
+            const exerciseId = this.extractExerciseId(idOrLink);
+            // console.log('Exercise ID:', exerciseId);
+            spinner.info(`Fetching results for exercise with ID: ${ exerciseId }`);
+            const results = await DojoBackendManager.getExerciseResults(exerciseId);
+
+            if ( !results ) {
+                spinner.info('No results found for this exercise.');
+                spinner.succeed('Exercise results fetched successfully.');
+                return;
+            }
+
+            if ( results.length === 0 ) {
+                spinner.info('No results found for this exercise.');
+            } else {
+                const answer = await inquirer.prompt([ {
+                    type   : 'list',
+                    name   : 'testType',
+                    message: 'Choisissez le type de tests à afficher:',
+                    choices: [ 'Tests réussis', 'Tests échoués', 'Les deux' ]
+                } ]);
+
+                const { testType } = answer;
+
+                this.displayResults(results, testType as string);
+                spinner.succeed('Exercise results fetched successfully.');
+            }
+        } catch ( error ) {
+            spinner.fail('Error fetching exercise results.');
+            console.error(error);
+        }
+    }
+
+    private extractExerciseId(idOrLink: string): string {
+        if ( idOrLink.length <= 36 ) {
+            return idOrLink;
+        } else {
+            const lastUnderscoreIndex = idOrLink.lastIndexOf('_');
+            // console.log('Last underscore index:', lastUnderscoreIndex);
+            if ( lastUnderscoreIndex !== -1 ) { // -1 = pas de underscore trouvé
+                return idOrLink.substring(lastUnderscoreIndex + 1); // Extrait la sous-chaîne après le dernier underscore dans idOrLink
+            } else {
+                return '';
+            }
+        }
+    }
+
+    private displayResults(results: Result[], testType: string): void {
+        if ( !results || results.length === 0 ) {
+            console.log('No results to display.');
+            return;
+        }
+
+        // Filtrer les résultats en fonction du type de test choisi
+        const filteredResults = results.filter(result => {
+            if ( testType === 'Tests réussis' ) {
+                return result.success;
+            } else if ( testType === 'Tests échoués' ) {
+                return !result.success;
+            }
+            return true; // 'Les deux' ou autre
+        });
+
+        if ( filteredResults.length === 0 ) {
+            console.log('No results for this test type.');
+            return;
+        }
+
+        filteredResults.forEach(result => {
+            console.log(chalk.magenta(`Résultats de l\`exercice : ${ result.exerciseId }`));
+            console.log(`  - Date et heure : ${ result.dateTime }`);
+            console.log(`  - Succès : ${ result.success ? chalk.green('Oui') : chalk.red('Non') }`);
+            console.log('  - Détails des résultats :');
+            console.log(`    - Tests réussis : ${ result.results.successfulTests }`);
+            console.log(`    - Tests échoués : ${ result.results.failedTests }`);
+
+            if ( testType === 'Tests réussis' || testType === 'Les deux' ) {
+                console.log('    - Liste des tests réussis :');
+                if ( Array.isArray(result.results.successfulTestsList) ) {
+                    result.results.successfulTestsList.forEach((test: string) => {
+                        console.log(`      - ${ test } ${ chalk.green('\u2713') }`);
+                    });
+                }
+            }
+
+            if ( testType === 'Tests échoués' || testType === 'Les deux' ) {
+                console.log('    - Liste des tests échoués :');
+                if ( Array.isArray(result.results.failedTestsList) ) {
+                    result.results.failedTestsList.forEach((test: string) => {
+                        console.log(`      - ${ test } ${ chalk.red('\u2717') }`);
+                    });
+                }
+            }
+
+            console.log('-----------------------------------');
+        });
+    }
+}
+
+
+export default new ExerciseResultCommand();
diff --git a/NodeApp/src/commander/exercise/subcommands/ExerciseRunCommand.ts b/NodeApp/src/commander/exercise/subcommands/ExerciseRunCommand.ts
index 061c9cbc65808d819927ab1c5aefad2e11c4f84a..708e8c07293b23338b0dfad54a22f837c464e88a 100644
--- a/NodeApp/src/commander/exercise/subcommands/ExerciseRunCommand.ts
+++ b/NodeApp/src/commander/exercise/subcommands/ExerciseRunCommand.ts
@@ -7,7 +7,7 @@ class ExerciseRunCommand extends CommanderCommand {
     protected commandName: string = 'run';
 
     protected defineCommand() {
-        GlobalHelper.runCommandDefinition(this.command)
+        GlobalHelper.runCommandDefinition(this.command, false)
             .description('locally run an exercise')
             .action(this.commandAction.bind(this));
     }
diff --git a/NodeApp/src/commander/exercise/subcommands/ExerciseSearchCommand.ts b/NodeApp/src/commander/exercise/subcommands/ExerciseSearchCommand.ts
new file mode 100644
index 0000000000000000000000000000000000000000..9c6183cba27bf019c5b7a94c0ca066a1691943e7
--- /dev/null
+++ b/NodeApp/src/commander/exercise/subcommands/ExerciseSearchCommand.ts
@@ -0,0 +1,313 @@
+// ExerciseListCommand.ts
+import CommanderCommand   from '../../CommanderCommand';
+import ora                from 'ora';
+import DojoBackendManager from '../../../managers/DojoBackendManager';
+import AccessesHelper     from '../../../helpers/AccessesHelper';
+import Exercise           from '../../../sharedByClients/models/Exercise';
+import inquirer           from 'inquirer';
+import Table              from 'cli-table3';
+import Fuse               from 'fuse.js';
+import User               from '../../../sharedByClients/models/User';
+import TextStyle          from '../../../types/TextStyle';
+import ExerciseHelper     from '../../../helpers/Dojo/ExerciseHelper';
+import { Option }         from 'commander';
+import Config             from '../../../config/Config';
+
+
+type CommandOptions = { all: boolean, name: string, teacher: string };
+
+
+class ExerciseSearchCommand extends CommanderCommand {
+    protected commandName: string = 'search';
+    protected aliasNames: string[] = [ 'list' ];
+
+    protected teachers: User[] = [];
+
+    protected allExercises: Exercise[] = [];
+    protected filteredExercises: Exercise[] = [];
+
+    protected currentSearchFilter = '';
+
+    protected defineCommand(): void {
+        this.command
+            .description('list your exercises')
+            .addOption(new Option('-a, --all', 'list all exercises').conflicts([ 'name', 'teacher' ]))
+            .addOption(new Option('-n, --name <pattern_to_search>', 'search exercises by name').conflicts([ 'all', 'teacher' ]))
+            .addOption(new Option('-t, --teacher <pattern_to_search>', 'search exercises by teacher').conflicts([ 'all', 'name' ]))
+            .action(this.commandAction.bind(this));
+    }
+
+    private async dataRetrieval(getTeachers: boolean) {
+        console.log(TextStyle.BLOCK('Please wait while we verify and retrieve data...'));
+
+        // Check access
+        await AccessesHelper.checkStudent();
+
+        // Fetch teachers
+        if ( getTeachers ) {
+            const teachersGetSpinner: ora.Ora = ora(`Fetching teachers`).start();
+            try {
+                const teachers: Array<User> | undefined = await DojoBackendManager.getTeachers();
+                if ( teachers ) {
+                    this.teachers = teachers;
+                } else {
+                    throw new Error();
+                }
+            } catch ( error ) {
+                teachersGetSpinner.fail(`Error while fetching teachers.`);
+                throw new Error();
+            }
+            teachersGetSpinner.succeed(`Teachers fetched successfully.`);
+        }
+
+        // Fetch user's exercises
+        const exercisesGetSpinner: ora.Ora = ora(`Checking user's exercises`).start();
+
+        this.allExercises = await DojoBackendManager.getUserExercises() ?? [];
+        this.filteredExercises = this.allExercises;
+
+        if ( this.allExercises.length === 0 ) {
+            exercisesGetSpinner.fail(`You do not have any exercises yet.`);
+            throw new Error();
+        }
+
+        exercisesGetSpinner.succeed(`User's exercises fetched successfully.`);
+    }
+
+    private clear(): void {
+        this.currentSearchFilter = '';
+        this.filteredExercises = this.allExercises;
+    }
+
+    private async displayMenu(): Promise<void> {
+        // eslint-disable-next-line no-constant-condition
+        while ( true ) {
+            console.log('');
+            ora(`${ '='.repeat(25) } Current filter: ${ this.currentSearchFilter == '' ? 'no filter' : this.currentSearchFilter } ${ '='.repeat(25) }`).info();
+
+            const action: string = (await inquirer.prompt([ {
+                type   : 'list',
+                name   : 'action',
+                message: 'What do you want ?',
+                choices: [ {
+                    name : 'Display current filtered exercises list',
+                    value: 'list'
+                }, new inquirer.Separator(), {
+                    name : 'Get details of an exercise',
+                    value: 'details'
+                }, new inquirer.Separator(), {
+                    name : 'Filter by name',
+                    value: 'fuzzy'
+                }, {
+                    name : 'Filter by teacher',
+                    value: 'teacher'
+                }, new inquirer.Separator(), {
+                    name : 'Clear filters',
+                    value: 'clear'
+                }, new inquirer.Separator(), {
+                    name : 'Exit',
+                    value: 'exit'
+                }, new inquirer.Separator() ]
+            } ])).action;
+
+            switch ( action ) {
+                case 'list':
+                    await this.displayExerciseList();
+                    break;
+                case 'details':
+                    await this.selectExerciseForDetails();
+                    return;
+                case 'fuzzy':
+                    await this.filterByExerciseName();
+                    break;
+                case 'teacher':
+                    await this.filterByTeacherInteractive();
+                    break;
+                case 'clear':
+                    this.clear();
+                    break;
+                case 'exit':
+                    throw new Error();
+                default:
+                    ora().info('Invalid filter type.');
+                    return;
+            }
+        }
+    }
+
+    private async selectExerciseForDetails(): Promise<void> {
+        const { selectedExercise } = await inquirer.prompt([ {
+            type   : 'list',
+            name   : 'selectedExercise',
+            message: 'Please select an exercise :',
+            choices: [ ...this.filteredExercises.map(exercise => ({
+                name : exercise.name,
+                value: exercise.id
+            })), new inquirer.Separator(), {
+                name : 'Cancel',
+                value: 'cancel'
+            } ]
+        } ]);
+
+        if ( selectedExercise === 'cancel' ) {
+            return;
+        }
+
+        const selected = this.filteredExercises.find(ex => ex.id === selectedExercise);
+        if ( selected ) {
+            return ExerciseHelper.displayDetails(selected, true);
+        } else {
+            ora().info('Invalid selection. No exercise details to show.');
+        }
+    }
+
+    private async filterByExerciseName(searchQuery: string | undefined = undefined): Promise<void> {
+        if ( searchQuery === undefined ) {
+            searchQuery = (await inquirer.prompt([ {
+                type   : 'input',
+                name   : 'searchQuery',
+                message: 'Please enter the searched string (leave blank if you want all exercises list):'
+            } ])).searchQuery;
+        }
+
+        this.currentSearchFilter = `[Name] ${ searchQuery }`;
+
+        if ( !searchQuery ) {
+            this.filteredExercises = this.allExercises;
+        } else {
+            const fuse = new Fuse(this.allExercises, {
+                keys     : [ 'name' ],
+                threshold: 0.5,
+                distance : 150
+            });
+
+            this.filteredExercises = fuse.search(searchQuery).map(result => result.item);
+        }
+
+        await this.displayExerciseList();
+    }
+
+    private async filterByTeacher(searchQuery: string): Promise<void> {
+        if ( this.teachers.length === 0 ) {
+            ora().info('No teachers found.');
+            return;
+        }
+
+        this.currentSearchFilter = `[Teacher] ${ searchQuery }`;
+
+        const exercises: Array<Exercise & { teachers: string }> = this.allExercises.map(exercise => ({
+            ...exercise,
+            teachers: (exercise.assignment?.staff ?? []).map(staff => staff.gitlabUsername).join(' ')
+        })) as Array<Exercise & { teachers: string }>;
+
+        const fuse = new Fuse(exercises, {
+            keys     : [ 'teachers' ],
+            threshold: 0.5,
+            distance : 150
+        });
+
+        this.filteredExercises = fuse.search(searchQuery).map(result => result.item);
+
+        await this.displayExerciseList();
+    }
+
+    private async filterByTeacherInteractive(): Promise<void> {
+        if ( this.teachers.length === 0 ) {
+            ora().info('No teachers found.');
+            return;
+        }
+
+        const teacherChoices = this.teachers.map(teacher => ({
+            name : `${ teacher.gitlabUsername }`,
+            value: teacher
+        }));
+
+        teacherChoices.sort((a, b) => a.name.split('.')[1].localeCompare(b.name.split('.')[1]));
+
+        const selectedTeacher: User = (await inquirer.prompt([ {
+            type   : 'list',
+            name   : 'selectedTeacher',
+            message: 'Please select a teacher:',
+            choices: teacherChoices
+        } ])).selectedTeacher;
+
+        this.currentSearchFilter = `[Teacher] ${ selectedTeacher.gitlabUsername }`;
+
+        this.filteredExercises = this.allExercises.filter(exercise => (exercise.assignment?.staff ?? []).find(staff => staff.id === selectedTeacher.id) !== undefined);
+
+        await this.displayExerciseList();
+    }
+
+    private async displayExerciseList(): Promise<void> {
+        ora(`Search results for filter: ${ this.currentSearchFilter == '' ? 'no filter' : this.currentSearchFilter }`).info();
+
+        if ( this.filteredExercises.length === 0 ) {
+            ora().info('No exercises found.');
+            return;
+        }
+
+        this.filteredExercises.forEach(exercise => {
+            console.log(TextStyle.LIST_ITEM_NAME(`➡ ${ exercise.name }`));
+            console.log(`    ${ TextStyle.LIST_SUBITEM_NAME('- Id:') } ${ exercise.id }`);
+            console.log(`    ${ TextStyle.LIST_SUBITEM_NAME('- Gitlab URL:') } ${ exercise.gitlabCreationInfo.web_url }`);
+        });
+    }
+
+    private async displayExerciseTable(): Promise<void> {
+        ora(`Search results for filter: ${ this.currentSearchFilter == '' ? 'no filter' : this.currentSearchFilter }`).info();
+
+        if ( this.filteredExercises.length === 0 ) {
+            ora().info('No exercises found.');
+            return;
+        }
+
+        const headers = [ 'Exercise Name', 'GitLab Link' ];
+
+        // Calculate the maximum width for each column
+        const maxWidths = headers.map(header => header.length);
+
+        this.filteredExercises.forEach(exercise => {
+            maxWidths[0] = Math.max(maxWidths[0], exercise.name.length);
+            maxWidths[1] = Math.max(maxWidths[1], exercise.gitlabLink.length);
+        });
+
+        const table = new Table({
+                                    head: headers
+                                });
+
+        this.filteredExercises.forEach((exercise) => {
+            table.push([ exercise.name, exercise.gitlabLink ]);
+        });
+
+        console.log(table.toString());
+    }
+
+    protected async commandAction(options: CommandOptions): Promise<void> {
+        try {
+            if ( !options.all && !options.name && !options.teacher && !Config.interactiveMode ) {
+                ora().fail('At least one filter or interactive mode is required.');
+                this.command.help();
+                return;
+            }
+
+            await this.dataRetrieval(!(options.all || options.name));
+
+            if ( Config.interactiveMode ) {
+                await this.displayMenu();
+            } else {
+                if ( options.all ) {
+                    await this.displayExerciseList();
+                } else if ( options.name ) {
+                    await this.filterByExerciseName(options.name);
+                } else if ( options.teacher ) {
+                    await this.filterByTeacher(options.teacher);
+                }
+
+                ora().info(`${ TextStyle.TIPS('[Tips]') } If you want to see more details about an exercise, use the command ${ TextStyle.CODE('dojo exercise info <id or url>') }.`);
+            }
+        } catch ( e ) { /* Do nothing */ }
+    }
+}
+
+
+export default new ExerciseSearchCommand();
\ No newline at end of file
diff --git a/NodeApp/src/commander/exercise/subcommands/ExerciseSummaryCommand.ts b/NodeApp/src/commander/exercise/subcommands/ExerciseSummaryCommand.ts
new file mode 100644
index 0000000000000000000000000000000000000000..9ca7bda8427da8edabbfffa7befa44328c0cbdd6
--- /dev/null
+++ b/NodeApp/src/commander/exercise/subcommands/ExerciseSummaryCommand.ts
@@ -0,0 +1,106 @@
+import CommanderCommand   from '../../CommanderCommand';
+import ora                from 'ora';
+import DojoBackendManager from '../../../managers/DojoBackendManager';
+import Exercise           from '../../../sharedByClients/models/Exercise';
+import Result             from '../../../sharedByClients/models/Result';
+import Table              from 'cli-table3';
+
+
+// THIS COMMAND IS NOT WORKING - NEEDS TO BE REWRITTEN AND THINK OF HIS INTEREST
+
+class ExerciseSummaryCommand extends CommanderCommand {
+    protected commandName: string = 'summary';
+
+    protected defineCommand(): void {
+        this.command
+            .description('Display top exercises based on successful tests')
+            .action(this.commandAction.bind(this));
+    }
+
+    protected async commandAction(): Promise<void> {
+        const spinner = ora('Classsement... \n').start();
+
+        try {
+            const exercises = await DojoBackendManager.getUserExercises();
+            const exerciseResults = await this.fetchExerciseResults(exercises);
+
+            if ( exerciseResults.length === 0 ) {
+                spinner.info('No exercise results found.');
+                spinner.succeed('Exercise summary fetched successfully.');
+                return;
+            }
+
+            const sortedExercises = this.sortExercisesBySuccessfulTests(exerciseResults);
+
+            this.displayExerciseSummary(sortedExercises);
+
+            spinner.succeed('Exercise summary fetched successfully.');
+        } catch ( error ) {
+            spinner.fail('Error fetching exercise summary.');
+            console.error(error);
+        }
+    }
+
+    private async fetchExerciseResults(exercises: Exercise[] | undefined): Promise<{ exercise: Exercise; successfulTests: number; dateTime: string }[]> {
+        const results: { exercise: Exercise, successfulTests: number, dateTime: string }[] = [];
+
+        for ( const exercise of exercises ?? [] ) {
+            try {
+                const exerciseId = exercise.id;
+                const exerciseResults = await DojoBackendManager.getExerciseResults(exerciseId);
+
+                if ( exerciseResults ) {
+                    const successfulTests = this.countSuccessfulTests(exerciseResults);
+                    results.push({
+                                     exercise,
+                                     successfulTests,
+                                     dateTime: exerciseResults[0]?.dateTime || ''
+                                 });
+                }
+            } catch ( error ) {
+                console.error(`Error fetching results for exercise ${ exercise.id }:`, error);
+            }
+        }
+
+        return results;
+    }
+
+    private countSuccessfulTests(results: Result[]): number {
+        return results.reduce((count, result) => count + (result.success ? (result.results.successfulTestsList ?? []).length : 0), 0);
+    }
+
+    private sortExercisesBySuccessfulTests(exerciseResults: { exercise: Exercise, successfulTests: number, dateTime: string }[]): { exercise: Exercise, successfulTests: number, dateTime: string }[] {
+        return exerciseResults.sort((a, b) => b.successfulTests - a.successfulTests);
+    }
+
+    private displayExerciseSummary(sortedExercises: { exercise: Exercise, successfulTests: number, dateTime: string }[]): void {
+        // Calculate the maximum width for each column
+        const headers = [ '#', 'Exercise', 'Nb de tests réussis', 'Date' ];
+        const maxWidths = headers.map(header => header.length);
+
+        sortedExercises.forEach((exercise, index) => {
+            maxWidths[0] = Math.max(maxWidths[0], (index + 1).toString().length);
+            maxWidths[1] = Math.max(maxWidths[1], exercise.exercise.name?.length);
+            maxWidths[2] = Math.max(maxWidths[2], exercise.successfulTests.toString().length);
+            maxWidths[3] = Math.max(maxWidths[3], exercise.dateTime.length);
+        });
+
+        // Define colWidths based on maxWidths
+        // const colWidths = maxWidths.map(width => ({ width }));
+
+        // Create the table
+        const table = new Table({
+                                    head: headers
+                                });
+
+        // Populate the table with data
+        sortedExercises.forEach((exercise, index) => {
+            table.push([ index + 1, exercise.exercise.name, exercise.successfulTests, exercise.dateTime ]);
+        });
+
+        console.log(table.toString(), '\n');
+    }
+}
+
+
+export default new ExerciseSummaryCommand();
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
diff --git a/NodeApp/src/helpers/GlobalHelper.ts b/NodeApp/src/helpers/GlobalHelper.ts
index dc9d2442afe43d4e9b16d077fa3c0dd17ad3abf5..fcb6418503f91d8719355eb60f159038f95e6eba 100644
--- a/NodeApp/src/helpers/GlobalHelper.ts
+++ b/NodeApp/src/helpers/GlobalHelper.ts
@@ -8,9 +8,9 @@ import Config              from '../config/Config';
 
 
 class GlobalHelper {
-    public runCommandDefinition(command: Command) {
+    public runCommandDefinition(command: Command, isAssignment: boolean = true): Command {
         command
-            .option('-p, --path <value>', 'assignment path', Config.folders.defaultLocalExercise)
+            .option('-p, --path <value>', `${ isAssignment ? 'assignment' : 'exercise' } path`, Config.folders.defaultLocalExercise)
             .option('-v, --verbose', 'verbose mode - display principal container output in live')
             .addOption(new Option('-w, --super-verbose', 'verbose mode - display all docker compose logs (build included) in live').conflicts('verbose'))
             .addOption(new Option('--verbose-ssj2').hideHelp().implies({ superVerbose: true }));
diff --git a/NodeApp/src/managers/DojoBackendManager.ts b/NodeApp/src/managers/DojoBackendManager.ts
index 50c2c0e01201345dd26e8f7d5375e24b20d0c881..f885205140af602eb2c3207a6d9d5b5b34060fee 100644
--- a/NodeApp/src/managers/DojoBackendManager.ts
+++ b/NodeApp/src/managers/DojoBackendManager.ts
@@ -17,6 +17,8 @@ import ClientsSharedConfig   from '../sharedByClients/config/ClientsSharedConfig
 import inquirer              from 'inquirer';
 import SharedConfig          from '../shared/config/SharedConfig';
 import ConfigFiles           from '../config/ConfigFiles';
+import SessionManager        from './SessionManager';
+import UserRole              from '../sharedByClients/models/UserRole';
 
 
 class DojoBackendManager {
@@ -478,7 +480,7 @@ class DojoBackendManager {
 
     public async getUserExercises(): Promise<Array<Exercise> | undefined> {
         try {
-            const response = await axios.get<DojoBackendResponse<Array<Exercise>>>(DojoBackendHelper.getApiUrl(ApiRoute.EXERCISE_LIST));
+            const response = await axios.get<DojoBackendResponse<Array<Exercise>>>(DojoBackendHelper.getApiUrl(ApiRoute.USER_EXERCISES_LIST, { userId: SessionManager.profile?.id }));
             return response.data.data;
         } catch ( error ) {
             console.error('Error fetching user exercises:', error);
@@ -486,13 +488,13 @@ class DojoBackendManager {
         }
     }
 
-    public async getExerciseDetails(exerciseIdOrUrl: string): Promise<Exercise | undefined> {
+    public async getExercise(exerciseIdOrUrl: string): Promise<Exercise | undefined> {
         try {
 
-            const response = await axios.get<Exercise>(DojoBackendHelper.getApiUrl(ApiRoute.EXERCISE_DETAILS_GET, {
+            const response = await axios.get<DojoBackendResponse<Exercise>>(DojoBackendHelper.getApiUrl(ApiRoute.EXERCISE_GET_DELETE, {
                 exerciseIdOrUrl: exerciseIdOrUrl
             }));
-            return response.data;
+            return response.data.data;
         } catch ( error ) {
             console.error('Error fetching exercise details:', error);
             return undefined;
@@ -540,19 +542,19 @@ class DojoBackendManager {
         }
     }
 
-    public async getUsers(roleFilter?: string): Promise<Array<User> | undefined> {
+    public async getUsers(role?: string): Promise<Array<User> | undefined> {
         try {
-            const response = await axios.get<DojoBackendResponse<Array<User>>>(DojoBackendHelper.getApiUrl(ApiRoute.USER_LIST), { params: roleFilter ? { roleFilter: roleFilter } : {} });
+            const response = await axios.get<DojoBackendResponse<Array<User>>>(DojoBackendHelper.getApiUrl(ApiRoute.USER_LIST), { params: role ? { role: role } : {} });
 
             return response.data.data;
         } catch ( error ) {
-            console.error('Error fetching professors:', error);
+            console.error('Error fetching users:', error);
             return undefined;
         }
     }
 
     public async getTeachers(): Promise<Array<User> | undefined> {
-        return this.getUsers('teacher');
+        return this.getUsers(UserRole.TEACHING_STAFF);
     }
 }
 
diff --git a/NodeApp/src/sharedByClients b/NodeApp/src/sharedByClients
index 530fe7459023f7fa11b14a9edf7a99629304cf8b..81c1c69cdc9ed0b381c60fe4fd2e4668abe00625 160000
--- a/NodeApp/src/sharedByClients
+++ b/NodeApp/src/sharedByClients
@@ -1 +1 @@
-Subproject commit 530fe7459023f7fa11b14a9edf7a99629304cf8b
+Subproject commit 81c1c69cdc9ed0b381c60fe4fd2e4668abe00625
diff --git a/NodeApp/src/types/TextStyle.ts b/NodeApp/src/types/TextStyle.ts
index 266f1e3129c6936bb1f9ac05dbd977e555976413..ddf01d50d76437568c940848ef4f5ebbca55685d 100644
--- a/NodeApp/src/types/TextStyle.ts
+++ b/NodeApp/src/types/TextStyle.ts
@@ -5,7 +5,9 @@ class TextStyle {
     public readonly BLOCK = chalk.cyan;
     public readonly CODE = chalk.bgHex('F7F7F7').grey.italic;
     public readonly LIST_ITEM_NAME = chalk.magenta;
+    public readonly LIST_SUBITEM_NAME = chalk.green;
     public readonly QUESTION = chalk.greenBright;
+    public readonly TIPS = chalk.blueBright;
     public readonly URL = chalk.blue.underline;
     public readonly WARNING = chalk.red;
 }