diff --git a/NodeApp/src/commander/CommanderApp.ts b/NodeApp/src/commander/CommanderApp.ts
index e4d16320e81c2c3555d988008372c6567f72f9f2..e70ce933f692f7cf8b4cac50fdd0b571cb5e4d11 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 {getBashCompletion, getFishCompletion} from '../helpers/AutoCompletionHelper';
+import { writeBashCompletion, writeFishCompletion } from '../helpers/AutoCompletionHelper';
 
 
 class CommanderApp {
@@ -44,8 +44,8 @@ class CommanderApp {
 
         this.registerCommands();
 
-        getBashCompletion(this.program, "bash_completion.sh")
-        getFishCompletion(this.program, "dojo.fish")
+        writeBashCompletion(this.program, "bash_completion.sh")
+        writeFishCompletion(this.program, "dojo.fish")
 
         this.program.parse();
     }
diff --git a/NodeApp/src/helpers/AutoCompletionHelper.ts b/NodeApp/src/helpers/AutoCompletionHelper.ts
index 34c5d7e3d7196fcbd382291a3f16c11237400ed5..0e3fbdb228aca95721443c2a9981ad22d25ed261 100644
--- a/NodeApp/src/helpers/AutoCompletionHelper.ts
+++ b/NodeApp/src/helpers/AutoCompletionHelper.ts
@@ -1,131 +1,36 @@
 import { Command } from 'commander';
 import { writeFileSync } from 'fs';
 
-// The following code should create a bash completion automatically from the Commander
-// CLI library. The file should look something like that (it looks at the time
-// this comment is written).
+const fishFunction = `
+function __fish_dojo_using_commands
+    set cmd (commandline -opc)
+    set num_cmd (count $cmd)
+    if [ $num_cmd -eq $argv[1] ]
+        for i in (seq 1 (math $num_cmd))
+            if [ $argv[(math $i+1)] != $cmd[$i] ]
+                return 1
+            end
+        end
+        return 0
+    end
+    return 1
+end
 
-// #/usr/bin/env bash
-// function _dojo_completions()
-// {
-//     latest="${COMP_WORDS[$COMP_CWORD]}"
-//     parent="${COMP_WORDS[$COMP_CWORD - 1]}"
-//     grand_parent="${COMP_WORDS[$COMP_CWORD - 2]}"
-//     words=""
-//     case "${parent}" in
-//         dojo)
-//                 words="session assignment exercise -V --version -H --host --help -h"
-//         ;;
-//         session)
-//                 words="login logout test --help -h"
-//         ;;
-//         login)
-//                 words="-c --cli --help -h"
-//         ;;
-//         logout)
-//                 words="-f --force --help -h"
-//         ;;
-//         assignment)
-//                 words="create check run publish unpublish --help -h"
-//         ;;
-//         create)
-//             case "${grand_parent}" in
-//                 assignment)
-//                     words="-n --name -i --members_id -u --members_username -t --template -c --clone --help -h"
-//                 ;;
-//                 *)
-//                 ;;
-//             esac
-//             case "${grand_parent}" in
-//                 exercise)
-//                     words="-a --assignment -i --members_id -u --members_username -c --clone --help -h"
-//                 ;;
-//                 *)
-//                 ;;
-//             esac
-//         ;;
-//         check)
-//                 words="-p --path -v --verbose -w --super-verbose --help -h"
-//         ;;
-//         run)
-//             case "${grand_parent}" in
-//                 assignment)
-//                     words="-p --path -v --verbose -w --super-verbose --help -h"
-//                 ;;
-//                 *)
-//                 ;;
-//             esac
-//             case "${grand_parent}" in
-//                 exercise)
-//                     words="-p --path -v --verbose -w --super-verbose --help -h"
-//                 ;;
-//                 *)
-//                 ;;
-//             esac
-//         ;;
-//         publish)
-//                 words="-f --force --help -h"
-//         ;;
-//         unpublish)
-//                 words="-f --force --help -h"
-//         ;;
-//         exercise)
-//                 words="create run --help -h"
-//         ;;
-//         *)
-//         ;;
-//     esac
-//     COMPREPLY=($(compgen -W "$words" -- $latest))
-//     return 0
-// }
+complete -f -c dojo
+`
 
 function isLeaf(cmd: Command): boolean {
     return cmd.commands.length == 0
 }
 
-function search(cmd: Command, cmdName: string): Array<Command> {
-    if (isLeaf(cmd)) {
-        if (cmd.name() != cmdName) {
-            return []
-        } else {
-            return [cmd]
-        }
-    } else if (cmd.name() == cmdName) {
-        return cmd.commands.flatMap(st => search(st, cmdName)).concat(cmd)
-    } else {
-        return cmd.commands.flatMap(st => search(st, cmdName))
-    }
-}
-
-function flatten(cmd: Command): Array<Command> {
-    if (isLeaf(cmd)) {
-        return [cmd]
+function computeDepth(cmd: Command | undefined): number {
+    if (cmd === undefined) {
+        return 0
     } else {
-        return cmd.commands
-            .map(child => flatten(child))
-            .reduce((acc, cmd) => acc.concat(cmd), [cmd])
+        return 1 + cmd.commands.map(subCmd => computeDepth(subCmd)).reduce((acc, depth) => depth > acc ? depth : acc, 0)
     }
 }
 
-function addWordsGrandParents(cmd: Command): string {
-    let parentWords = ""
-    let ident = 3
-    if (cmd.parent !== null) {
-        parentWords += openCase(ident, 'grand_parent')
-        ident += 1
-        parentWords += addLine(ident, `${cmd.parent.name()})`)
-        ident += 1
-    }
-    parentWords += addLine(ident, `words="${commandsAndOptionsToString(cmd)}"`)
-    ident -= 1
-    parentWords += addLine(ident, ';;')
-    if (cmd.parent !== null) {
-        ident -= 1
-        parentWords += `${closeCase(ident)}`
-    }
-    return parentWords
-}
-
 function getOptions(cmd: Command): string {
     // we remove <args>, [command], and , from option lines
     return cmd.options.filter(opt => !opt.hidden).map(opt =>
@@ -137,90 +42,223 @@ function commandsAndOptionsToString(cmd: Command): string {
     return cmd.commands.map(c => c.name()).join(" ").concat(' ' + getOptions(cmd)).trim().concat(' --help -h').trim()
 }
 
-function openCase(identLevel: number, pattern: string): string {
-    return `${'    '.repeat(identLevel)}case "\${${pattern}}" in\n`
-}
-
-function closeCase(identLevel: number): string {
-    return `${'    '.repeat(identLevel + 1)}*)\n`
-        + `${'    '.repeat(identLevel + 1)};;\n`
-        + `${'    '.repeat(identLevel)}esac\n`
-}
-
 function addLine(identLevel: number, pattern: string): string {
     return `${'    '.repeat(identLevel)}${pattern}\n`
 }
 
-const fishFunctions = `### Fish completions for dojo ###
-function __fish_dojo_needs_command
-    set cmd (commandline -opc)
-    if [ (count $cmd) -eq 1 -a $cmd[1] = dojo ]
-        return 0
-    end
-    return 1
-end
+function generateBashSubCommands(cmd: Command, current: number, maxDepth: number, ident: number): string {
+    if (current == maxDepth) {
+        return addLine(ident, `case "\${COMP_WORDS[$COMP_CWORD - ${maxDepth - current + 1}]}" in`)
+            + addLine(ident + 1, `${cmd.name()})`)
+            + addLine(ident + 2, `words="${commandsAndOptionsToString(cmd)}"`)
+            + addLine(ident + 1, ';;')
+            + addLine(ident + 1, '*)')
+            + addLine(ident + 1, ';;')
+            + addLine(ident, 'esac')
 
-function __fish_dojo_using_command
-    set cmd (commandline -opc)
-    set num_cmd (count $cmd)
-    if [ $num_cmd -eq 2 ]
-        if [ $argv[(math $num_cmd - 1)] = $cmd[$num_cmd] ]
-            return 0
-        end
-    end
-    return 1
-end
-
-function __fish_dojo_using_two_commands
-    set cmd (commandline -opc)
-    set num_cmd (count $cmd)
-    if [ $num_cmd -gt 3 ]
-        if [ $argv[(math $num_cmd - 2)] = $cmd[(math $num_cmd - 1)] -a $argv[(math $num_cmd - 1)] = $cmd[$num_cmd] ]
-            return 0
-        end
-    end
-    return 1
-end
-`
-
-export function getFishCompletion(root: Command, filename: string) {
-    const commands = Array.from(new Set(flatten(root).filter(cmd => !isLeaf(cmd)).map(cmd => cmd.name()))).map(name => search(root, name))
-    let data = fishFunctions
-    for (const node of commands) {
-        const cmd = node[0]
-        data += addLine(0, `complete -f -c dojo -n __fish_dojo_needs_command ${cmd.name()} -a "${commandsAndOptionsToString(cmd)}"`)
+    } else {
+        let data = addLine(ident, `case "\${COMP_WORDS[$COMP_CWORD - ${maxDepth - current + 1}]}" in`)
+            + addLine(ident + 1, `${cmd.name()})`)
+        cmd.commands.forEach(subCmd => {
+            data += generateBashSubCommands(subCmd, current + 1, maxDepth, ident + 2)
+        })
+        data +=
+            addLine(ident + 1, ';;')
+            + addLine(ident + 1, '*)')
+            + addLine(ident + 1, ';;')
+            + addLine(ident, 'esac')
+        return data
     }
+}
 
+export function writeBashCompletion(root: Command, filename: string) {
+    const depth = computeDepth(root)
+    let data =
+        addLine(0, '#/usr/bin/env bash\nfunction _dojo_completions()')
+        + addLine(0, '{')
+        + addLine(1, 'latest="${COMP_WORDS[$COMP_CWORD]}"')
+    for (let i = 1; i <= depth; i++) {
+        data += addLine(1, `${i == 1 ? 'if' : 'elif'} [ $COMP_CWORD -eq ${depth - i + 1} ]`)
+            + addLine(1, 'then')
+        data += generateBashSubCommands(root, i, depth, 2)
+    }
+    data += addLine(1, 'fi')
+        + addLine(1, 'COMPREPLY=($(compgen -W "$words" -- $latest))')
+        + addLine(1, 'return 0')
+        + addLine(0, '}')
+        + addLine(0, 'complete -F _dojo_completions dojo')
 
     writeFileSync(filename, data);
 }
 
+const prefix = 'complete -f -c dojo -n \'__fish_dojo_using_commands'
+
+function generateFishSubCommands(cmd: Command, crtDepth: number): string {
+    let data = ''
+    for (const arg of cmd.commands) {
+        data += `${prefix} ${crtDepth} ${cmd.name()}' -a ${arg.name()} -d "${arg.description()}"\n`
+    }
+    return data
+}
+
+export function writeFishCompletion(root: Command, filename: string) {
+    const depth = computeDepth(root)
 
-export function getBashCompletion(root: Command, filename: string) {
     let data =
-        `${addLine(0, '#/usr/bin/env bash\nfunction _dojo_completions()')}`
-        + `${addLine(0, '{')}${addLine(1, 'latest="${COMP_WORDS[$COMP_CWORD]}"')}`
-        + `${addLine(1, 'parent="${COMP_WORDS[$COMP_CWORD - 1]}"')}`
-        + `${addLine(1, 'grand_parent="${COMP_WORDS[$COMP_CWORD - 2]}"')}`
-        + `${addLine(1, 'words=""')}`
-        + `${openCase(1, "parent")}`
-    const commands = Array.from(new Set(flatten(root).map(cmd => cmd.name()))).map(name => search(root, name))
-    for (const cmdNode of commands) {
-        const cmd = cmdNode[0]
-        data += addLine(2, `${cmd.name()})`)
-        if (cmd !== undefined && cmdNode.length == 1) {
-            data += addLine(3, `words="${commandsAndOptionsToString(cmd)}"`)
-        } else if (cmd !== undefined && cmdNode.length > 1) {
-            cmdNode.forEach(n => data += addWordsGrandParents(n))
-        }
-        data += addLine(2, ';;')
+        fishFunction
+    for (let i = 1; i <= depth; i++) {
+        data += generateFishSubCommands(root, i)
     }
-    data +=
-        `${closeCase(1)}`
-        + `${addLine(1, 'COMPREPLY=($(compgen -W "$words" -- $latest))')}`
-        + `${addLine(1, 'return 0')}`
-        + `${addLine(0, '}')}`
-        + `${addLine(0, 'complete -F _dojo_completions dojo')}`
 
     writeFileSync(filename, data);
 }
+
+
+
+// The following code should create a bash completion automatically from the Commander
+// CLI library. The file should look something like that (it looks at the time
+// this comment is written).
+
+// #/usr/bin/env bash
+// function _dojo_completions()
+// {
+//     latest="${COMP_WORDS[$COMP_CWORD]}"
+//     if [ $COMP_CWORD -eq 3 ]
+//     then
+//         case "${COMP_WORDS[$COMP_CWORD - 3]}" in
+//             dojo)
+//                 case "${COMP_WORDS[$COMP_CWORD - 2]}" in
+//                     session)
+//                         case "${COMP_WORDS[$COMP_CWORD - 1]}" in
+//                             login)
+//                                 words="-c --cli --help -h"
+//                             ;;
+//                             *)
+//                             ;;
+//                         esac
+//                         case "${COMP_WORDS[$COMP_CWORD - 1]}" in
+//                             logout)
+//                                 words="-f --force --help -h"
+//                             ;;
+//                             *)
+//                             ;;
+//                         esac
+//                         case "${COMP_WORDS[$COMP_CWORD - 1]}" in
+//                             test)
+//                                 words="--help -h"
+//                             ;;
+//                             *)
+//                             ;;
+//                         esac
+//                     ;;
+//                     *)
+//                     ;;
+//                 esac
+//                 case "${COMP_WORDS[$COMP_CWORD - 2]}" in
+//                     assignment)
+//                         case "${COMP_WORDS[$COMP_CWORD - 1]}" in
+//                             create)
+//                                 words="-n --name -i --members_id -u --members_username -t --template -c --clone --help -h"
+//                             ;;
+//                             *)
+//                             ;;
+//                         esac
+//                         case "${COMP_WORDS[$COMP_CWORD - 1]}" in
+//                             check)
+//                                 words="-p --path -v --verbose -w --super-verbose --help -h"
+//                             ;;
+//                             *)
+//                             ;;
+//                         esac
+//                         case "${COMP_WORDS[$COMP_CWORD - 1]}" in
+//                             run)
+//                                 words="-p --path -v --verbose -w --super-verbose --help -h"
+//                             ;;
+//                             *)
+//                             ;;
+//                         esac
+//                         case "${COMP_WORDS[$COMP_CWORD - 1]}" in
+//                             publish)
+//                                 words="-f --force --help -h"
+//                             ;;
+//                             *)
+//                             ;;
+//                         esac
+//                         case "${COMP_WORDS[$COMP_CWORD - 1]}" in
+//                             unpublish)
+//                                 words="-f --force --help -h"
+//                             ;;
+//                             *)
+//                             ;;
+//                         esac
+//                     ;;
+//                     *)
+//                     ;;
+//                 esac
+//                 case "${COMP_WORDS[$COMP_CWORD - 2]}" in
+//                     exercise)
+//                         case "${COMP_WORDS[$COMP_CWORD - 1]}" in
+//                             create)
+//                                 words="-a --assignment -i --members_id -u --members_username -c --clone --help -h"
+//                             ;;
+//                             *)
+//                             ;;
+//                         esac
+//                         case "${COMP_WORDS[$COMP_CWORD - 1]}" in
+//                             run)
+//                                 words="-p --path -v --verbose -w --super-verbose --help -h"
+//                             ;;
+//                             *)
+//                             ;;
+//                         esac
+//                     ;;
+//                     *)
+//                     ;;
+//                 esac
+//             ;;
+//             *)
+//             ;;
+//         esac
+//     elif [ $COMP_CWORD -eq 2 ]
+//     then
+//         case "${COMP_WORDS[$COMP_CWORD - 2]}" in
+//             dojo)
+//                 case "${COMP_WORDS[$COMP_CWORD - 1]}" in
+//                     session)
+//                         words="login logout test --help -h"
+//                     ;;
+//                     *)
+//                     ;;
+//                 esac
+//                 case "${COMP_WORDS[$COMP_CWORD - 1]}" in
+//                     assignment)
+//                         words="create check run publish unpublish --help -h"
+//                     ;;
+//                     *)
+//                     ;;
+//                 esac
+//                 case "${COMP_WORDS[$COMP_CWORD - 1]}" in
+//                     exercise)
+//                         words="create run --help -h"
+//                     ;;
+//                     *)
+//                     ;;
+//                 esac
+//             ;;
+//             *)
+//             ;;
+//         esac
+//     elif [ $COMP_CWORD -eq 1 ]
+//     then
+//         case "${COMP_WORDS[$COMP_CWORD - 1]}" in
+//             dojo)
+//                 words="session assignment exercise -V --version -H --host --help -h"
+//             ;;
+//             *)
+//             ;;
+//         esac
+//     fi
+//     COMPREPLY=($(compgen -W "$words" -- $latest))
+//     return 0
+// }
+// complete -F _dojo_completions dojo