diff --git a/NodeApp/src/commander/CommanderApp.ts b/NodeApp/src/commander/CommanderApp.ts
index e70ce933f692f7cf8b4cac50fdd0b571cb5e4d11..b729c95d25223b799140f889bd489fb1c719e078 100644
--- a/NodeApp/src/commander/CommanderApp.ts
+++ b/NodeApp/src/commander/CommanderApp.ts
@@ -9,7 +9,7 @@ import { stateConfigFile } from '../config/ConfigFiles';
 import semver              from 'semver/preload';
 import { version }         from '../config/Version';
 import Config              from '../config/Config';
-import { writeBashCompletion, writeFishCompletion } from '../helpers/AutoCompletionHelper';
+import CompletionCommand from './completion/CompletionCommand';
 
 
 class CommanderApp {
@@ -44,8 +44,7 @@ class CommanderApp {
 
         this.registerCommands();
 
-        writeBashCompletion(this.program, "bash_completion.sh")
-        writeFishCompletion(this.program, "dojo.fish")
+        this.program.on
 
         this.program.parse();
     }
@@ -92,6 +91,7 @@ https://gitedu.hesge.ch/dojo_project/projects/ui/dojocli/-/releases/Latest`, {
         SessionCommand.registerOnCommand(this.program);
         AssignmentCommand.registerOnCommand(this.program);
         ExerciseCommand.registerOnCommand(this.program);
+        CompletionCommand.registerOnCommand(this.program);
     }
 }
 
diff --git a/NodeApp/src/commander/completion/CompletionCommand.ts b/NodeApp/src/commander/completion/CompletionCommand.ts
new file mode 100644
index 0000000000000000000000000000000000000000..11df30fce08acce6a70ecd5d43946ac385b1aceb
--- /dev/null
+++ b/NodeApp/src/commander/completion/CompletionCommand.ts
@@ -0,0 +1,25 @@
+import CommanderCommand from '../CommanderCommand';
+import CompletionBashCommand from './subcommands/CompletionBashCommand';
+import CompletionFishCommand from './subcommands/CompletionFishCommand';
+import CompletionZshCommand from './subcommands/CompletionZshCommand';
+
+
+class CompletionCommand extends CommanderCommand {
+    protected commandName: string = 'completion';
+
+    protected defineCommand() {
+        this.command
+            .description('Generate completions for bash, fish, or zsh');
+    }
+
+    protected defineSubCommands() {
+        CompletionBashCommand.registerOnCommand(this.command);
+        CompletionFishCommand.registerOnCommand(this.command);
+        CompletionZshCommand.registerOnCommand(this.command);
+    }
+
+    protected async commandAction(): Promise<void> { }
+}
+
+
+export default new CompletionCommand();
\ No newline at end of file
diff --git a/NodeApp/src/commander/completion/subcommands/CompletionBashCommand.ts b/NodeApp/src/commander/completion/subcommands/CompletionBashCommand.ts
new file mode 100644
index 0000000000000000000000000000000000000000..5dfde487fa71a56ad02ab12a5886f93c006ae3ae
--- /dev/null
+++ b/NodeApp/src/commander/completion/subcommands/CompletionBashCommand.ts
@@ -0,0 +1,61 @@
+import { existsSync, writeFileSync } from 'fs';
+import inquirer from 'inquirer';
+import CommanderCommand from '../../CommanderCommand';
+import { generateBashCompletion, getRoot } from '../../../helpers/AutoCompletionHelper';
+
+class CompletionBashCommand extends CommanderCommand {
+    protected commandName: string = 'bash';
+
+    protected default_filename: string = './dojo_bash_completion.sh'
+
+    writeFile(filename: string) {
+        const installInstructions: string = `
+The easiest way to install the completion is to append the content of the
+generated file to the end of the '~/.bash_completion' file or to
+overwrite it, if it only contains the 'dojo' completion.
+
+This can be performed by either
+
+cat ${filename} > ~/.bash_completion  # overwrites .bash_completion
+cat ${filename} >> ~/.bash_completion # appends to .bash_completion
+
+For more details: <https://github.com/scop/bash-completion/blob/master/README.md>
+`
+        try {
+            writeFileSync(filename, generateBashCompletion(getRoot(this.command)))
+            console.log(`Bash completion successfully written.`)
+            console.log(`${installInstructions}`)
+        } catch (error) {
+            console.log(`Error: ${error}, failed to write ${filename}`)
+        }
+    }
+
+    protected defineCommand() {
+        this.command
+            .description('generate bash completion')
+            .option(`-f, --file <filename>', 'complete path of the filename where the bash completion will be stored (default to ${this.default_filename}).`)
+            .action(this.commandAction.bind(this));
+    }
+
+    protected async commandAction(options: { file?: string }): Promise<void> {
+        const filename = options.file ?? this.default_filename
+        if (existsSync(filename)) {
+            // File exists in path
+            const confirm: boolean = (await inquirer.prompt({
+                name: 'confirm',
+                message: `${options.file} is going to be overwritten. Are you sure?`,
+                type: 'confirm',
+                default: false
+            })).confirm;
+
+            if (confirm) {
+                this.writeFile(filename)
+            }
+        } else {
+            this.writeFile(filename)
+        }
+    }
+}
+
+
+export default new CompletionBashCommand();
\ No newline at end of file
diff --git a/NodeApp/src/commander/completion/subcommands/CompletionFishCommand.ts b/NodeApp/src/commander/completion/subcommands/CompletionFishCommand.ts
new file mode 100644
index 0000000000000000000000000000000000000000..b6ad117aa4c0272cffd0e57810d1bf3591bf2a3e
--- /dev/null
+++ b/NodeApp/src/commander/completion/subcommands/CompletionFishCommand.ts
@@ -0,0 +1,56 @@
+import { existsSync, writeFileSync } from 'fs';
+import { generateFishCompletion, getRoot } from '../../../helpers/AutoCompletionHelper';
+import CommanderCommand from '../../CommanderCommand';
+import inquirer from 'inquirer';
+
+
+class CompletionFishCommand extends CommanderCommand {
+    protected commandName: string = 'fish';
+
+    protected default_filename: string = './dojo.fish'
+
+    writeFile(filename: string) {
+        const installInstructions: string = `
+The easiest way to install the completion is to copy the ${filename} into the
+~/.config/fish/completions directory
+
+cp -i ${filename} ~/.config/fish/completions  # interactive cp to avoid accidents
+`
+        try {
+            writeFileSync(filename, generateFishCompletion(getRoot(this.command)))
+            console.log(`Bash completion successfully written.`)
+            console.log(`${installInstructions}`)
+        } catch (error) {
+            console.log(`Error: ${error}, failed to write ${filename}`)
+        }
+    }
+
+    protected defineCommand() {
+        this.command
+            .description('generate fish completion')
+            .option('-f, --file <filename>', 'filename where the bash completion will be stored (default to ./dojo.fish).')
+            .action(this.commandAction.bind(this));
+    }
+
+    protected async commandAction(options: { file?: string }): Promise<void> {
+        const filename = options.file ?? this.default_filename
+        if (existsSync(filename)) {
+            // File exists in path
+            const confirm: boolean = (await inquirer.prompt({
+                name: 'confirm',
+                message: `${options.file} is going to be overwritten. Are you sure?`,
+                type: 'confirm',
+                default: false
+            })).confirm;
+
+            if (confirm) {
+                this.writeFile(filename)
+            }
+        } else {
+            this.writeFile(filename)
+        }
+    }
+}
+
+
+export default new CompletionFishCommand();
\ No newline at end of file
diff --git a/NodeApp/src/commander/completion/subcommands/CompletionZshCommand.ts b/NodeApp/src/commander/completion/subcommands/CompletionZshCommand.ts
new file mode 100644
index 0000000000000000000000000000000000000000..d162dc98393d28fbb2d3c946010b06b58133be86
--- /dev/null
+++ b/NodeApp/src/commander/completion/subcommands/CompletionZshCommand.ts
@@ -0,0 +1,69 @@
+import { existsSync, writeFileSync } from 'fs';
+import CommanderCommand from '../../CommanderCommand';
+import { generateBashCompletion, getRoot } from '../../../helpers/AutoCompletionHelper';
+import inquirer from 'inquirer';
+
+
+
+class CompletionZshCommand extends CommanderCommand {
+    protected commandName: string = 'zsh';
+
+    protected default_filename: string = './dojo_bash_completion.sh'
+
+    writeFile(filename: string) {
+        const installInstructions: string = `
+The easiest way to install the completion is to append the content of the
+generated file to the end of the '~/.bash_completion' file or to
+overwrite it, if it only contains the 'dojo' completion.
+
+This can be performed by either
+
+cat ${filename} > ~/.bash_completion  # overwrites .bash_completion
+cat ${filename} >> ~/.bash_completion # appends to .bash_completion
+
+For more details: <https://github.com/scop/bash-completion/blob/master/README.md>
+
+Next add the following lines to your ~/.zprofile file:
+
+autoload -U +X compinit && compinit
+autoload -U +X bashcompinit && bashcompinit
+source .bash_completion
+`
+        try {
+            writeFileSync(filename, generateBashCompletion(getRoot(this.command)))
+            console.log(`Bash completion successfully written.`)
+            console.log(`${installInstructions}`)
+        } catch (error) {
+            console.log(`Error: ${error}, failed to write ${filename}`)
+        }
+    }
+
+    protected async commandAction(options: { file?: string }): Promise<void> {
+        const filename = options.file ?? this.default_filename
+        if (existsSync(filename)) {
+            // File exists in path
+            const confirm: boolean = (await inquirer.prompt({
+                name: 'confirm',
+                message: `${options.file} is going to be overwritten. Are you sure?`,
+                type: 'confirm',
+                default: false
+            })).confirm;
+
+            if (confirm) {
+                this.writeFile(filename)
+            }
+        } else {
+            this.writeFile(filename)
+        }
+    }
+
+    protected defineCommand() {
+        this.command
+            .description('generate zsh completion, which is derived from the bash completion')
+            .option('-f, --file <filename>', 'bash completion filename (defaults to ./dojo_bash_completion.sh).')
+            .action(this.commandAction.bind(this));
+    }
+}
+
+
+export default new CompletionZshCommand();
\ No newline at end of file
diff --git a/NodeApp/src/helpers/AutoCompletionHelper.ts b/NodeApp/src/helpers/AutoCompletionHelper.ts
index 2e1f42ec093ee7f0c55b29a7cb861d59b6cfafad..3af7148ae53fbe5e61b7cf887ffcac87e96ceb2b 100644
--- a/NodeApp/src/helpers/AutoCompletionHelper.ts
+++ b/NodeApp/src/helpers/AutoCompletionHelper.ts
@@ -53,6 +53,15 @@ function computeHeight(cmd: Command | null): number {
     return height
 }
 
+// Computes the maximum number of commands until the root is reached
+export function getRoot(cmd: Command): Command {
+    if (cmd.parent == null) {
+        return cmd
+    } else {
+        return getRoot(cmd.parent)
+    }
+}
+
 function getOptions(cmd: Command): string {
     // we remove <args>, [command], and , from option lines
     return cmd.options.filter(opt => !opt.hidden).map(opt =>
@@ -93,7 +102,7 @@ function generateBashSubCommands(cmd: Command, current: number, maxDepth: number
     }
 }
 
-export function writeBashCompletion(root: Command, filename: string) {
+export function generateBashCompletion(root: Command): string {
     const depth = computeDepth(root)
     let data =
         addLine(0, '#/usr/bin/env bash\nfunction _dojo_completions()')
@@ -110,7 +119,7 @@ export function writeBashCompletion(root: Command, filename: string) {
         + addLine(0, '}')
         + addLine(0, 'complete -F _dojo_completions dojo')
 
-    writeFileSync(filename, data);
+    return data
 }
 
 const prefix = 'complete -f -c dojo -n \'__fish_dojo_using_commands'
@@ -134,7 +143,7 @@ function optionsToString(cmd: Command): string {
     }).join('\n').concat('\n')
 }
 
-export function writeFishCompletion(root: Command, filename: string) {
+export function generateFishCompletion(root: Command): string {
     const commands = flatten(root)
 
     const data = fishFunction.concat(
@@ -149,7 +158,7 @@ export function writeFishCompletion(root: Command, filename: string) {
                 }).join('\n').concat('\n')).join('')
     )
 
-    writeFileSync(filename, data);
+    return data
 }