diff --git a/NodeApp/src/commander/CommanderApp.ts b/NodeApp/src/commander/CommanderApp.ts
index 37dbc06cd7783fa8de53d828417c22e981bd9769..8fad32ae891aaca66f2bf1471671110bf9e7078a 100644
--- a/NodeApp/src/commander/CommanderApp.ts
+++ b/NodeApp/src/commander/CommanderApp.ts
@@ -1,7 +1,8 @@
-import { Command }    from 'commander';
-import Config         from '../config/Config';
-import EnonceCommand  from './enonce/EnonceCommand';
-import SessionCommand from './session/SessionCommand';
+import { Command }     from 'commander';
+import Config          from '../config/Config';
+import EnonceCommand   from './enonce/EnonceCommand';
+import SessionCommand  from './session/SessionCommand';
+import ExerciceCommand from './exercice/ExerciceCommand';
 
 
 class CommanderApp {
@@ -32,6 +33,7 @@ class CommanderApp {
     private registerCommands() {
         SessionCommand.registerOnCommand(this.program);
         EnonceCommand.registerOnCommand(this.program);
+        ExerciceCommand.registerOnCommand(this.program);
     }
 }
 
diff --git a/NodeApp/src/commander/exercice/ExerciceCommand.ts b/NodeApp/src/commander/exercice/ExerciceCommand.ts
new file mode 100644
index 0000000000000000000000000000000000000000..7833c5719a10af723351c59511095535a4f5a8a9
--- /dev/null
+++ b/NodeApp/src/commander/exercice/ExerciceCommand.ts
@@ -0,0 +1,33 @@
+import CommanderCommand      from '../CommanderCommand';
+import ExerciceCreateCommand from './ExerciceCreateCommand';
+
+
+class ExerciceCommand extends CommanderCommand {
+    protected commandName: string = 'exercice';
+
+    private static _instance: ExerciceCommand;
+
+    private constructor() { super(); }
+
+    public static get instance(): ExerciceCommand {
+        if ( !ExerciceCommand._instance ) {
+            ExerciceCommand._instance = new ExerciceCommand();
+        }
+
+        return ExerciceCommand._instance;
+    }
+
+    protected defineCommand() {
+        this.command
+        .description('manage an exercice');
+    }
+
+    protected defineSubCommands() {
+        ExerciceCreateCommand.registerOnCommand(this.command);
+    }
+
+    protected async commandAction(options: any): Promise<void> { }
+}
+
+
+export default ExerciceCommand.instance;
\ No newline at end of file
diff --git a/NodeApp/src/commander/exercice/ExerciceCreateCommand.ts b/NodeApp/src/commander/exercice/ExerciceCreateCommand.ts
new file mode 100644
index 0000000000000000000000000000000000000000..a3c0fdc9d6893856a087a7e2d78634a111c6c435
--- /dev/null
+++ b/NodeApp/src/commander/exercice/ExerciceCreateCommand.ts
@@ -0,0 +1,114 @@
+import CommanderCommand   from '../CommanderCommand';
+import chalk              from 'chalk';
+import GitlabManager      from '../../managers/GitlabManager';
+import SessionManager     from '../../managers/SessionManager';
+import GitlabUser         from '../../shared/types/Gitlab/GitlabUser';
+import Enonce             from '../../types/Enonce';
+import ora                from 'ora';
+import DojoBackendManager from '../../managers/DojoBackendManager';
+import Exercice           from '../../types/Exercice';
+
+
+class ExerciceCreateCommand extends CommanderCommand {
+    protected commandName: string = 'create';
+
+    private static _instance: ExerciceCreateCommand;
+
+    private constructor() { super(); }
+
+    public static get instance(): ExerciceCreateCommand {
+        if ( !ExerciceCreateCommand._instance ) {
+            ExerciceCreateCommand._instance = new ExerciceCreateCommand();
+        }
+
+        return ExerciceCreateCommand._instance;
+    }
+
+    protected defineCommand() {
+        this.command
+        .description('create a new exercice from an enonce')
+        .requiredOption('-e, --enonce <value>', 'enonce source (Dojo enonce ID, Dojo enonce name or Gitlab enonce URL)')
+        .option('-i, --members_id <ids...>', 'list of gitlab members ids (group\'s student) to add to the repository')
+        .option('-u, --members_username <usernames...>', 'list of gitlab members username (group\'s student) to add to the repository')
+        .action(this.commandAction.bind(this));
+    }
+
+    private async checkAccesses(): Promise<boolean> {
+        let sessionResult = await SessionManager.testSession(true, [ 'student' ]);
+
+        if ( !sessionResult ) {
+            return false;
+        }
+
+        return (await GitlabManager.testToken(true)).every(result => result);
+    }
+
+    protected async commandAction(options: any): Promise<void> {
+        let members!: Array<GitlabUser> | false;
+        let enonce!: Enonce | undefined;
+
+        // Check access and retrieve data
+        {
+            console.log(chalk.cyan('Please wait while we verify and retrieve data...'));
+
+            if ( !await this.checkAccesses() ) {
+                return;
+            }
+
+            members = await GitlabManager.fetchMembers(options);
+            if ( !members ) {
+                return;
+            }
+
+            ora('Checking enonce:').start().info();
+            const enonceGetSpinner: ora.Ora = ora({
+                                                      text  : 'Checking if enonce exists',
+                                                      indent: 4
+                                                  }).start();
+            enonce = await DojoBackendManager.getEnonce(options.enonce);
+            if ( !enonce ) {
+                enonceGetSpinner.fail(`Enonce "${ options.enonce }" doesn't exists`);
+                return;
+            }
+            enonceGetSpinner.succeed(`Enonce "${ options.enonce }" exists`);
+
+            const enoncePublishedSpinner: ora.Ora = ora({
+                                                            text  : 'Checking if enonce is published',
+                                                            indent: 4
+                                                        }).start();
+            //TODO : Check if the enonce is published
+            //if ( false ) {
+            //enoncePublishedSpinner.fail(`Enonce "${ enonce.name }" isn't published`);
+            //return;
+            //}
+            enoncePublishedSpinner.succeed(`Enonce "${ enonce.name }" is published`);
+        }
+
+        //Create the exercice
+        {
+            console.log(chalk.cyan('Please wait while we are creating the exercice...'));
+
+            try {
+                const exercice: Exercice = await DojoBackendManager.createExercice((enonce as Enonce).name, members);
+
+                const oraInfo = (message: string) => {
+                    ora({
+                            text  : message,
+                            indent: 4
+                        }).start().info();
+                };
+
+                oraInfo(`${ chalk.magenta('Id:') } ${ exercice.id }`);
+                oraInfo(`${ chalk.magenta('Name:') } ${ exercice.name }`);
+                oraInfo(`${ chalk.magenta('Web URL:') } ${ exercice.gitlabCreationInfo.web_url }`);
+                oraInfo(`${ chalk.magenta('HTTP Repo:') } ${ exercice.gitlabCreationInfo.http_url_to_repo }`);
+                oraInfo(`${ chalk.magenta('SSH Repo:') } ${ exercice.gitlabCreationInfo.ssh_url_to_repo }`);
+            } catch ( error ) {
+                return;
+            }
+        }
+    }
+}
+
+
+export default ExerciceCreateCommand.instance;
\ No newline at end of file
diff --git a/NodeApp/src/managers/DojoBackendManager.ts b/NodeApp/src/managers/DojoBackendManager.ts
index 1f36638118b1b6f8f8eee6fced3712936f19974c..151168612b588897cdece6b6c4be949825c80498 100644
--- a/NodeApp/src/managers/DojoBackendManager.ts
+++ b/NodeApp/src/managers/DojoBackendManager.ts
@@ -6,6 +6,7 @@ import { StatusCodes }       from 'http-status-codes';
 import Enonce                from '../types/Enonce';
 import GitlabUser            from '../shared/types/Gitlab/GitlabUser';
 import DojoResponse          from '../types/DojoResponse';
+import Exercice              from '../types/Exercice';
 
 
 class DojoBackendManager {
@@ -106,6 +107,40 @@ class DojoBackendManager {
             throw error;
         }
     }
+
+    public async createExercice(enonceName: string, members: Array<GitlabUser>, verbose: boolean = true): Promise<Exercice> {
+        const spinner: ora.Ora = ora('Creating exercice...');
+
+        if ( verbose ) {
+            spinner.start();
+        }
+
+        try {
+            const response = await axios.post<DojoResponse<Exercice>>(this.getApiUrl(ApiRoutes.EXERCICE_CREATE).replace('{{nameOrUrl}}', String(enonceName)), { members: JSON.stringify(members) });
+
+            if ( verbose ) {
+                spinner.succeed(`Exercice successfully created`);
+            }
+
+            return response.data.data;
+        } catch ( error ) {
+            if ( verbose ) {
+                if ( error instanceof AxiosError ) {
+                    if ( error.response ) {
+                        if ( error.response.status === StatusCodes.CONFLICT ) {
+                            spinner.fail(`You've reached the max number of exercice of this enonce.`);
+                        } else {
+                            spinner.fail(`Exercice creation error: ${ error.response.statusText }`);
+                        }
+                    }
+                } else {
+                    spinner.fail(`Exercice creation error: unknown error`);
+                }
+            }
+
+            throw error;
+        }
+    }
 }
 
 
diff --git a/NodeApp/src/types/ApiRoutes.ts b/NodeApp/src/types/ApiRoutes.ts
index cc62f3f7fe264915d2a6ac92fe5ac5f0b2895791..63f492a3a0620f32b4d1c498051704416306ef4c 100644
--- a/NodeApp/src/types/ApiRoutes.ts
+++ b/NodeApp/src/types/ApiRoutes.ts
@@ -4,6 +4,7 @@ enum ApiRoutes {
     GITLAB_CHECK_TEMPLATE_ACCESS = '/gitlab/project/{{id}}/checkTemplateAccess',
     ENONCE_GET                   = '/enonces/{{nameOrUrl}}',
     ENONCE_CREATE                = '/enonces',
+    EXERCICE_CREATE              = '/enonces/{{nameOrUrl}}/exercices',
 }
 
 
diff --git a/NodeApp/src/types/Exercice.ts b/NodeApp/src/types/Exercice.ts
new file mode 100644
index 0000000000000000000000000000000000000000..12bb8da658e209f25f535b08554b73864fea3b07
--- /dev/null
+++ b/NodeApp/src/types/Exercice.ts
@@ -0,0 +1,16 @@
+import GitlabRepository from '../shared/types/Gitlab/GitlabRepository';
+
+
+interface Exercice {
+    id: string;
+    enonceName: string;
+    name: string;
+    gitlabId: number;
+    gitlabLink: string;
+    gitlabCreationInfo: GitlabRepository;
+    gitlabLastInfo: GitlabRepository;
+    gitlabLastInfoTs: number;
+}
+
+
+export default Exercice;
\ No newline at end of file