diff --git a/NodeApp/.idea/.gitignore b/NodeApp/.idea/.gitignore
index 13566b81b018ad684f3a35fee301741b2734c8f4..a9d7db9c0a81b2db47ca92e4e180b30090b27632 100644
--- a/NodeApp/.idea/.gitignore
+++ b/NodeApp/.idea/.gitignore
@@ -6,3 +6,5 @@
 # Datasource local storage ignored files
 /dataSources/
 /dataSources.local.xml
+# GitHub Copilot persisted chat sessions
+/copilot/chatSessions
diff --git a/NodeApp/src/commander/CommanderApp.ts b/NodeApp/src/commander/CommanderApp.ts
index d7ecd85f6471d907525a42449cd8b3e95602dae1..1f3619e55f76e4d40af303dc9fa0f39969521f03 100644
--- a/NodeApp/src/commander/CommanderApp.ts
+++ b/NodeApp/src/commander/CommanderApp.ts
@@ -14,7 +14,21 @@ import SessionCommand      from './auth/SessionCommand';
 
 
 class CommanderApp {
-    program: Command = new Command();
+    public program: Command = new Command();
+
+    private readonly commandHookDisabled: Array<string> = [ 'completion' ];
+
+    private hasToExecuteHook(actionCommand: Command): boolean {
+        if ( actionCommand.parent == null ) {
+            return true;
+        } else {
+            if ( this.commandHookDisabled.includes(actionCommand.name()) ) {
+                return false;
+            }
+
+            return this.hasToExecuteHook(actionCommand.parent);
+        }
+    }
 
     constructor() {
         this.program
@@ -30,11 +44,16 @@ class CommanderApp {
             .option('-H, --host <string>', 'override the Dojo API endpoint', ClientsSharedConfig.apiURL)
             .option('-I, --interactive', 'show interactive interface when available', Config.interactiveMode)
             .addOption(new Option('--debug').hideHelp())
-            .hook('preAction', () => {
-                this.warnDevelopmentVersion();
-            }).hook('postAction', () => {
-            this.informNewVersion();
-        });
+            .hook('preAction', (_thisCommand: Command, actionCommand: Command) => {
+                if ( this.hasToExecuteHook(actionCommand) ) {
+                    this.warnDevelopmentVersion();
+                }
+            })
+            .hook('postAction', (_thisCommand: Command, actionCommand: Command) => {
+                if ( this.hasToExecuteHook(actionCommand) ) {
+                    this.informNewVersion();
+                }
+            });
 
         this.program.on('option:host', () => {
             ClientsSharedConfig.apiURL = this.program.opts().host;
diff --git a/NodeApp/src/commander/completion/CompletionCommand.ts b/NodeApp/src/commander/completion/CompletionCommand.ts
index 1ee0668b994796b04d303357de071c7a3b4f75ff..ba4f3cfe026a2da2f66992aa2ec535c35afcf5b5 100644
--- a/NodeApp/src/commander/completion/CompletionCommand.ts
+++ b/NodeApp/src/commander/completion/CompletionCommand.ts
@@ -1,7 +1,7 @@
-import CommanderCommand      from '../CommanderCommand';
-import CompletionBashCommand from './subcommands/CompletionBashCommand';
-import CompletionFishCommand from './subcommands/CompletionFishCommand';
-import CompletionZshCommand  from './subcommands/CompletionZshCommand';
+import CommanderCommand              from '../CommanderCommand';
+import CompletionCreateUpdateCommand from './subcommands/CompletionCreateUpdateCommand';
+import CompletionGetCommand          from './subcommands/CompletionGetCommand';
+import CompletionScriptCommand       from './subcommands/CompletionScriptCommand';
 
 
 class CompletionCommand extends CommanderCommand {
@@ -13,9 +13,9 @@ class CompletionCommand extends CommanderCommand {
     }
 
     protected defineSubCommands() {
-        CompletionBashCommand.registerOnCommand(this.command);
-        CompletionFishCommand.registerOnCommand(this.command);
-        CompletionZshCommand.registerOnCommand(this.command);
+        CompletionCreateUpdateCommand.registerOnCommand(this.command);
+        CompletionGetCommand.registerOnCommand(this.command);
+        CompletionScriptCommand.registerOnCommand(this.command);
     }
 
     protected async commandAction(): Promise<void> {
diff --git a/NodeApp/src/commander/completion/subcommands/CompletionBashCommand.ts b/NodeApp/src/commander/completion/subcommands/CompletionBashCommand.ts
deleted file mode 100644
index d135440dccf7310afbb1304c15bbbd507f3a6eaa..0000000000000000000000000000000000000000
--- a/NodeApp/src/commander/completion/subcommands/CompletionBashCommand.ts
+++ /dev/null
@@ -1,76 +0,0 @@
-import CommanderCommand                                   from '../../CommanderCommand';
-import { generateBashCompletion, getRoot, tryRenameFile } from '../../../helpers/AutoCompletionHelper';
-import ora                                                from 'ora';
-import TextStyle                                          from '../../../types/TextStyle';
-import fs                                                 from 'fs-extra';
-import path                                               from 'path';
-import os                                                 from 'os';
-import GlobalHelper                                       from '../../../helpers/GlobalHelper';
-
-
-class CompletionBashCommand extends CommanderCommand {
-    protected commandName: string = 'bash';
-
-    private readonly installPath = path.join(os.homedir(), '.bash_completion');
-
-    protected defineCommand() {
-        GlobalHelper.completionCommandDefinition(this.command)
-            .description('generate bash completion')
-            .action(this.commandAction.bind(this));
-    }
-
-    private writeFile(filename: string, showInstructions: boolean) {
-        const spinner: ora.Ora = ora(`Writing Bash completion in ${ TextStyle.CODE(filename) } ...`).start();
-
-        try {
-            fs.mkdirsSync(path.dirname(filename));
-
-            fs.writeFileSync(filename, generateBashCompletion(getRoot(this.command)));
-
-            spinner.succeed(`Bash completion successfully written in ${ TextStyle.CODE(filename) }`);
-            if ( showInstructions ) {
-                console.log(`
-The easiest way to install the completion is to append the content of the generated file to the end of the ${ TextStyle.CODE('~/.bash_completion') } file or to overwrite it, if it only contains the 'dojo' completion.
-
-This can be performed by either
-${ TextStyle.CODE(`
-cat ${ filename } > ~/.bash_completion  # overwrites .bash_completion
-cat ${ filename } >> ~/.bash_completion # appends to .bash_completion
-`) }
-For more details: ${ TextStyle.URL('https://github.com/scop/bash-completion/blob/master/README.md') }
-`);
-            }
-        } catch ( error ) {
-            spinner.fail(`Bash completion error: ${ error }`);
-        }
-    }
-
-    /* The completion command must do the following:
-    - if a file is provided:
-        - if force is not enabled:
-            - check if the bash completion file exists:
-                - if it exists, prompt the user that it will be overwritten
-                    - if ok is given write the file and prompt that a backup has been created
-        - else create the file containing the completion
-    - else
-        - if force is not enabled:
-            - check if the default file exists:
-                - if it exists, prompt the user that it will be erased:
-                    - if ok is given write the file and prompt that a backup has been created
-        - else
-            - create the file containing the completion
-    - create a .zprofile or append the appropriate commands into the .zprofile file
-    */
-    protected async commandAction(options: { file: string, force: boolean }): Promise<void> {
-        const filePath = path.resolve(options.file ?? this.installPath); // change that if file is empty
-        const showInstructions = !!options.file;
-        if ( !(await tryRenameFile(filePath, options.force)) ) { // means renaming was interrupted
-            return;
-        }
-        this.writeFile(filePath, showInstructions);
-    }
-
-}
-
-
-export default new CompletionBashCommand();
\ No newline at end of file
diff --git a/NodeApp/src/commander/completion/subcommands/CompletionCreateUpdateCommand.ts b/NodeApp/src/commander/completion/subcommands/CompletionCreateUpdateCommand.ts
new file mode 100644
index 0000000000000000000000000000000000000000..edc521942681dd07339e1607cc2423eeaa9586d7
--- /dev/null
+++ b/NodeApp/src/commander/completion/subcommands/CompletionCreateUpdateCommand.ts
@@ -0,0 +1,101 @@
+import CommanderCommand                                                 from '../../CommanderCommand';
+import { Option }                                                       from 'commander';
+import { generateFishCompletion, getRoot, tryRenameFile, updateRcFile } from '../../../helpers/AutoCompletionHelper';
+import os, { homedir }                                                  from 'os';
+import path                                                             from 'path';
+import ora                                                              from 'ora';
+import fs                                                               from 'fs-extra';
+import TextStyle                                                        from '../../../types/TextStyle';
+
+
+class CompletionCreateUpdateCommand extends CommanderCommand {
+    protected commandName: string = 'create';
+    protected aliasNames: Array<string> = [ 'update' ];
+
+
+    protected defineCommand() {
+        this.command.description('generate shell completion')
+            .addOption(new Option('-s, --shell <shell>', 'shell type').choices([ 'bash', 'zsh', 'fish' ]).makeOptionMandatory(true))
+            .addOption(new Option('-f, --file <filename>', '(only for fish shell)').implies({ shell: 'fish' }))
+            .addOption(new Option('-y, --force', 'don\'t ask for file overwrite confirmation (only for fish shell)').implies({ shell: 'fish' }))
+            .action(this.commandAction.bind(this));
+    }
+
+    private bash() {
+        const completionCommand = `
+# Added by DojoCLI
+eval "$(dojo completion script bash)"
+`;
+        updateRcFile('bash', path.join(os.homedir(), '.bashrc'), completionCommand);
+    }
+
+    private zsh() {
+        const completionCommand = `
+# Added by DojoCLI
+source <(dojo completion script zsh)
+`;
+        updateRcFile('zsh', path.join(homedir(), '.zshrc'), completionCommand);
+    }
+
+    /* The completion command must do the following:
+       - if a file is provided:
+            - if force is not enabled:
+                - check if the file exists:
+                    - if it exists, prompt the user that it will be erased
+                        - if ok is given write the file and prompt that a backup has been created
+            - else create the file containing the completion
+        - else
+            - if force is not enabled:
+                - check if the default file exists:
+                    - if it exists, prompt the user that it will be erased:
+                        - if ok is given write the file and prompt that a backup has been created
+            - else
+                - create the file containing the completion
+    */
+    private async fish(options: { file: string, force: boolean }) {
+        const filePath = path.resolve(options.file ?? path.join(os.homedir(), '.config/fish/completions/dojo.fish'));
+        const showInstructions = !!options.file;
+        if ( !(await tryRenameFile(filePath, options.force)) ) { // means renaming was interrupted
+            return;
+        }
+
+        const spinner: ora.Ora = ora(`Writing fish completion in ${ filePath }...`).start();
+
+        try {
+            fs.mkdirsSync(path.dirname(filePath));
+
+            fs.writeFileSync(filePath, generateFishCompletion(getRoot(this.command)));
+
+            spinner.succeed(`Fish completion successfully written in ${ filePath }.`);
+            if ( showInstructions ) {
+                const cpCommand = ` cp -i ${ filePath } ~/.config/fish/completions  # interactive cp to avoid accidents `;
+                console.log(`
+The easiest way to install the completion is to copy the ${ TextStyle.CODE(filePath) } into the ${ TextStyle.CODE('~/.config/fish/completions') } directory.
+
+${ TextStyle.CODE(cpCommand) }`);
+            }
+        } catch ( error ) {
+            spinner.fail(`Fish completion error: ${ error }.`);
+        }
+    }
+
+    protected async commandAction(options: { shell: 'bash' | 'zsh' | 'fish', file: string, force: boolean }): Promise<void> {
+        switch ( options.shell ) {
+            case 'bash':
+                this.bash();
+                break;
+            case 'zsh':
+                this.zsh();
+                break;
+            case 'fish':
+                await this.fish(options);
+                break;
+            default:
+                console.error('Unsupported shell.');
+                break;
+        }
+    }
+}
+
+
+export default new CompletionCreateUpdateCommand();
\ No newline at end of file
diff --git a/NodeApp/src/commander/completion/subcommands/CompletionFishCommand.ts b/NodeApp/src/commander/completion/subcommands/CompletionFishCommand.ts
deleted file mode 100644
index 2be5716ee17ae7347d146533f48c8594801b73e5..0000000000000000000000000000000000000000
--- a/NodeApp/src/commander/completion/subcommands/CompletionFishCommand.ts
+++ /dev/null
@@ -1,70 +0,0 @@
-import { generateFishCompletion, getRoot, tryRenameFile } from '../../../helpers/AutoCompletionHelper';
-import CommanderCommand                                   from '../../CommanderCommand';
-import ora                                                from 'ora';
-import TextStyle                                          from '../../../types/TextStyle';
-import path                                               from 'path';
-import os                                                 from 'os';
-import fs                                                 from 'fs-extra';
-import GlobalHelper                                       from '../../../helpers/GlobalHelper';
-
-
-class CompletionFishCommand extends CommanderCommand {
-    protected commandName: string = 'fish';
-
-    private readonly installPath = path.join(os.homedir(), '.config/fish/completions/dojo.fish');
-
-    protected defineCommand() {
-        GlobalHelper.completionCommandDefinition(this.command)
-            .description('generate fish completion')
-            .action(this.commandAction.bind(this));
-    }
-
-    private writeFile(filename: string, showInstructions: boolean) {
-        const spinner: ora.Ora = ora(`Writing fish completion in ${ filename }...`).start();
-
-        try {
-            fs.mkdirsSync(path.dirname(filename));
-
-            fs.writeFileSync(filename, generateFishCompletion(getRoot(this.command)));
-
-            spinner.succeed(`Fish completion successfully written in ${ filename }.`);
-            if ( showInstructions ) {
-                const cpCommand = ` cp -i ${ filename } ~/.config/fish/completions  # interactive cp to avoid accidents `;
-                console.log(`
-The easiest way to install the completion is to copy the ${ TextStyle.CODE(filename) } into the ${ TextStyle.CODE('~/.config/fish/completions') } directory.
-
-${ TextStyle.CODE(cpCommand) }`);
-            }
-        } catch ( error ) {
-            spinner.fail(`Fish completion error: ${ error }.`);
-        }
-    }
-
-
-    /* The completion command must do the following:
-       - if a file is provided:
-            - if force is not enabled:
-                - check if the file exists:
-                    - if it exists, prompt the user that it will be erased
-                        - if ok is given write the file and prompt that a backup has been created
-            - else create the file containing the completion
-        - else
-            - if force is not enabled:
-                - check if the default file exists:
-                    - if it exists, prompt the user that it will be erased:
-                        - if ok is given write the file and prompt that a backup has been created
-            - else
-                - create the file containing the completion
-    */
-    protected async commandAction(options: { file: string, force: boolean }): Promise<void> {
-        const filePath = path.resolve(options.file ?? this.installPath); // change that if file is empty
-        const showInstructions = !!options.file;
-        if ( !(await tryRenameFile(filePath, options.force)) ) { // means renaming was interrupted
-            return;
-        }
-        this.writeFile(filePath, showInstructions);
-    }
-}
-
-
-export default new CompletionFishCommand();
\ No newline at end of file
diff --git a/NodeApp/src/commander/completion/subcommands/CompletionGetCommand.ts b/NodeApp/src/commander/completion/subcommands/CompletionGetCommand.ts
new file mode 100644
index 0000000000000000000000000000000000000000..f57aa7059a1b1459f7d6c0cc9b4d560ed5a0cd44
--- /dev/null
+++ b/NodeApp/src/commander/completion/subcommands/CompletionGetCommand.ts
@@ -0,0 +1,69 @@
+import CommanderCommand                    from '../../CommanderCommand';
+import { Command, CommandOptions, Option } from 'commander';
+import * as AutoCompletionHelper           from '../../../helpers/AutoCompletionHelper';
+
+
+type CompletionProposal = { name: string, description: string };
+
+
+class CompletionGetCommand extends CommanderCommand {
+    protected commandName: string = 'get';
+    protected options: CommandOptions = {
+        hidden: true
+    };
+
+    protected defineCommand() {
+        this.command.description('generate completion options for a given command')
+            .addOption(new Option('-s, --shell <shell>', 'shell completion result format').choices([ 'bash', 'zsh' ]))
+            .argument('<commands...>', 'command chain to complete')
+            .action(this.commandAction.bind(this));
+    }
+
+    private completion(commandsChain: Array<string>, displayFunction: (completionProposals: Array<CompletionProposal>) => void) {
+        const command = AutoCompletionHelper.getCommandFromChain(AutoCompletionHelper.getRoot(this.command), commandsChain.slice(1));
+
+        if ( command ) {
+            const commands = command.commands.filter(cmd => !(cmd as Command & { _hidden: boolean })._hidden);
+            const options = command.options.filter(option => !option.hidden);
+
+            displayFunction([ ...commands.flatMap(cmd => [ {
+                name       : cmd.name(),
+                description: cmd.description()
+            }, ...cmd.aliases().map(alias => ({
+                name       : alias,
+                description: cmd.description()
+            })) ]), ...options.flatMap(option => [ {
+                name       : option.long,
+                description: option.description
+            }, {
+                name       : option.short,
+                description: option.description
+            } ]) ].filter(proposal => proposal.name) as Array<CompletionProposal>);
+        }
+    }
+
+    private bashCompletion(commandsChain: Array<string>) {
+        this.completion(commandsChain, (completionProposals: Array<CompletionProposal>) => console.log(completionProposals.map(proposal => proposal.name).join(' ')));
+    }
+
+    private zshCompletion(commandsChain: Array<string>) {
+        this.completion(commandsChain, (completionProposals: Array<CompletionProposal>) => completionProposals.forEach(proposal => console.log(`${ proposal.name }:${ proposal.description }`)));
+    }
+
+    protected async commandAction(commandsChain: Array<string>, options: { shell: 'bash' | 'zsh' }): Promise<void> {
+        switch ( options.shell ) {
+            case 'bash':
+                this.bashCompletion(commandsChain);
+                break;
+            case 'zsh':
+                this.zshCompletion(commandsChain);
+                break;
+            default:
+                console.error('Unsupported shell completion format');
+                break;
+        }
+    }
+}
+
+
+export default new CompletionGetCommand();
\ No newline at end of file
diff --git a/NodeApp/src/commander/completion/subcommands/CompletionScriptCommand.ts b/NodeApp/src/commander/completion/subcommands/CompletionScriptCommand.ts
new file mode 100644
index 0000000000000000000000000000000000000000..45c5f3e52a167b0eb76c370e5010d75959f2e56e
--- /dev/null
+++ b/NodeApp/src/commander/completion/subcommands/CompletionScriptCommand.ts
@@ -0,0 +1,75 @@
+import CommanderCommand from '../../CommanderCommand';
+import { Argument }     from 'commander';
+
+
+class CompletionScriptCommand extends CommanderCommand {
+    protected commandName: string = 'script';
+
+    protected defineCommand() {
+        this.command.description('generate script completion')
+            .addArgument(new Argument('<shell>', 'shell completion format').choices([ 'bash', 'zsh' ]))
+            .action(this.commandAction.bind(this));
+    }
+
+    private bashCompletionScript() {
+        console.log(`
+#/usr/bin/env bash
+###-begin-dojo-completions-###
+#
+# dojo command completion script for bash
+#
+# Installation: dojo completion bash
+#
+function _dojo_completions()
+{
+    latest="\${COMP_WORDS[$COMP_CWORD]}"
+    words=$(dojo completion get --shell bash \${COMP_WORDS[@]})
+
+    COMPREPLY=($(compgen -W "$words" -- $latest))
+    return 0
+}
+complete -F _dojo_completions dojo
+###-end-dojo-completions-###
+        `);
+    }
+
+    private zshCompletionScript() {
+        console.log(`
+#compdef dojo
+###-begin-dojo-completions-###
+#
+# dojo command completion script for zsh
+#
+# Installation: dojo completion zsh
+#
+_dojo_completions()
+{
+  local reply
+  local si=$IFS
+  IFS=$'
+' reply=($(dojo completion get --shell zsh \${words[@]}))
+  IFS=$si
+  _describe 'values' reply
+}
+compdef _dojo_completions dojo
+###-end-dojo-completions-###
+        `);
+    }
+
+    protected async commandAction(shell: 'bash' | 'zsh'): Promise<void> {
+        switch ( shell ) {
+            case 'bash':
+                this.bashCompletionScript();
+                break;
+            case 'zsh':
+                this.zshCompletionScript();
+                break;
+            default:
+                console.error('Unsupported shell completion format');
+                break;
+        }
+    }
+}
+
+
+export default new CompletionScriptCommand();
\ No newline at end of file
diff --git a/NodeApp/src/commander/completion/subcommands/CompletionZshCommand.ts b/NodeApp/src/commander/completion/subcommands/CompletionZshCommand.ts
deleted file mode 100644
index f9d279eb2e86b29f567db78797f98333b70c4ec2..0000000000000000000000000000000000000000
--- a/NodeApp/src/commander/completion/subcommands/CompletionZshCommand.ts
+++ /dev/null
@@ -1,125 +0,0 @@
-import CommanderCommand                                   from '../../CommanderCommand';
-import { generateBashCompletion, getRoot, tryRenameFile } from '../../../helpers/AutoCompletionHelper';
-import ora                                                from 'ora';
-import TextStyle                                          from '../../../types/TextStyle';
-import path                                               from 'path';
-import { homedir }                                        from 'os';
-import fs                                                 from 'fs-extra';
-import GlobalHelper                                       from '../../../helpers/GlobalHelper';
-
-
-class CompletionZshCommand extends CommanderCommand {
-    protected commandName: string = 'zsh';
-
-    private readonly zprofile: string = path.join(homedir(), '.zprofile');
-    private readonly bashCompletion = path.join(homedir(), '.bash_completion');
-    private readonly loadBashCompletion = `
-# Added by DojoCLI
-autoload -U +X compinit && compinit
-autoload -U +X bashcompinit && bashcompinit
-source ${ this.bashCompletion }
-`;
-
-
-    protected defineCommand() {
-        GlobalHelper.completionCommandDefinition(this.command)
-            .description('generate zsh completion, which is derived from the bash completion')
-            .action(this.commandAction.bind(this));
-    }
-
-    private writeFile(filename: string, showInstructions: boolean) {
-        const spinner: ora.Ora = ora(`Writing Bash completion in ${ TextStyle.CODE(filename) } ...`).start();
-
-        try {
-            fs.mkdirsSync(path.dirname(filename));
-
-            fs.writeFileSync(filename, generateBashCompletion(getRoot(this.command)));
-
-            spinner.succeed(`Bash completion successfully written in ${ TextStyle.CODE(filename) }`);
-            if ( showInstructions ) {
-                const zprofileContent = TextStyle.CODE(`
-autoload -U +X compinit && compinit
-autoload -U +X bashcompinit && bashcompinit
-source ${ filename }
-`);
-                console.log(`
-The easiest way to install the completion is to append the content of the generated file to the end of the ${ TextStyle.CODE(filename) } file or to overwrite it, if it only contains the 'dojo' completion.
-
-This can be performed by either ${ TextStyle.CODE(`
-cat ${ filename } > ~/.bash_completion  # overwrites .bash_completion
-cat ${ filename } >> ~/.bash_completion # appends to .bash_completion`) }
-For more details: ${ TextStyle.URL('https://github.com/scop/bash-completion/blob/master/README.md') }
-
-Next add the following lines to your ${ TextStyle.CODE('~/.zprofile') } file: ${ zprofileContent } `);
-            }
-        } catch ( error ) {
-            spinner.fail(`Bash completion writing error: ${ error }`);
-        }
-    }
-
-    protected addToZprofile(zprofilePath: string, bashPath: string) {
-        const spinner: ora.Ora = ora(`Modifying ${ zprofilePath } ...`).start();
-        if ( fs.existsSync(zprofilePath) ) {
-            const data = fs.readFileSync(zprofilePath);
-            let updated = false;
-            try {
-                if ( !data.includes('autoload -U +X compinit && compinit') ) {
-                    fs.appendFileSync(zprofilePath, '\nautoload -U +X compinit && compinit');
-                    updated = true;
-                }
-                if ( !data.includes('autoload -U +X bashcompinit && bashcompinit') ) {
-                    fs.appendFileSync(zprofilePath, '\nautoload -U +X bashcompinit && bashcompinit');
-                    updated = true;
-                }
-                if ( !data.includes(`source ${ bashPath }`) ) {
-                    fs.appendFileSync(zprofilePath, `\nsource ${ bashPath }`);
-                    updated = true;
-                }
-            } catch {
-                spinner.fail(`Error appending in ${ this.zprofile }`);
-                return;
-            }
-
-            spinner.succeed(updated ? `Zsh profile updated.` : `Zsh profile already up to date.`);
-        } else {
-            try {
-                fs.writeFileSync(zprofilePath, this.loadBashCompletion);
-                spinner.succeed(`Zsh profile written.`);
-            } catch ( error ) {
-                spinner.fail(`Error writing in ${ this.zprofile }`);
-            }
-        }
-    }
-
-    /* The completion command must do the following:
-    - if a file is provided:
-        - if force is not enabled:
-            - check if the bash completion file exists:
-                - if it exists, prompt the user that it will be overwritten
-                    - if ok is given write the file and prompt that a backup has been created
-        - else create the file containing the completion
-    - else
-        - if force is not enabled:
-            - check if the default file exists:
-                - if it exists, prompt the user that it will be erased:
-                    - if ok is given write the file and prompt that a backup has been created
-        - else
-            - create the file containing the completion
-    - create a .zprofile or append the appropriate commands into the .zprofile file
-    */
-    protected async commandAction(options: { file: string, force: boolean }): Promise<void> {
-        const filePath = path.resolve(options.file ?? this.bashCompletion); // change that if file is empty
-        const showInstructions = !!options.file;
-        if ( !(await tryRenameFile(filePath, options.force)) ) { // means renaming was interrupted
-            return;
-        }
-        this.writeFile(filePath, showInstructions);
-        // Do not modify if custom file was provided
-        if ( !options.file ) {
-            this.addToZprofile(this.zprofile, filePath);
-        }
-    }
-}
-
-
-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 56988ebdceb9f25b1ba53fff4a6021059ca138c9..04464f6205fd62b86cfe19631fc1d3e988ea694d 100644
--- a/NodeApp/src/helpers/AutoCompletionHelper.ts
+++ b/NodeApp/src/helpers/AutoCompletionHelper.ts
@@ -3,6 +3,7 @@ import { existsSync, renameSync } from 'fs';
 import ora                        from 'ora';
 import TextStyle                  from '../types/TextStyle';
 import inquirer                   from 'inquirer';
+import fs                         from 'fs-extra';
 
 
 function renameFile(filename: string, showWarning: boolean) {
@@ -126,6 +127,19 @@ function addLine(identLevel: number, pattern: string): string {
     return `${ '    '.repeat(identLevel) }${ pattern }\n`;
 }
 
+export function getCommandFromChain(currentCmd: Command, chain: Array<string>): Command | null {
+    if ( chain.length === 0 ) {
+        return currentCmd;
+    } else {
+        const subCmd = currentCmd.commands.find(c => c.name() === chain[0]);
+        if ( subCmd === undefined ) {
+            return currentCmd;
+        } else {
+            return getCommandFromChain(subCmd, chain.slice(1));
+        }
+    }
+}
+
 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');
@@ -178,6 +192,32 @@ export function generateFishCompletion(root: Command): string {
                                                                                                                                                                              commands.filter(c => !isHidden(c)).filter(cmd => !isLeaf(cmd)).map(cmd => cmd.commands.filter(c => !isHidden(c)).map(subCmd => `${ prefix } ${ computeHeight(cmd) } ${ generateCommandChain(cmd) }' -a ${ subCmd.name() } -d "${ subCmd.description() }"`).join('\n').concat('\n')).join(''));
 }
 
+export function updateRcFile(shellType: 'bash' | 'zsh', filePath: string, completionCommand: string) {
+    const spinner: ora.Ora = ora(`Modifying ${ filePath } ...`).start();
+    if ( fs.existsSync(filePath) ) {
+        const data = fs.readFileSync(filePath);
+        let updated = false;
+        try {
+            if ( !data.includes(completionCommand) ) {
+                fs.appendFileSync(filePath, completionCommand);
+                updated = true;
+            }
+        } catch {
+            spinner.fail(`Error appending in ${ filePath }`);
+            return;
+        }
+
+        spinner.succeed(updated ? `${ shellType } updated. Please restart your shell session.` : `${ shellType } already up to date.`);
+    } else {
+        try {
+            fs.writeFileSync(filePath, completionCommand);
+            spinner.succeed(`${ shellType } written. Please restart your shell session.`);
+        } catch ( error ) {
+            spinner.fail(`Error writing in ${ filePath }`);
+        }
+    }
+}
+
 
 // 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
diff --git a/NodeApp/src/helpers/GlobalHelper.ts b/NodeApp/src/helpers/GlobalHelper.ts
index 7f0991cbd6ce17a58c1a7845057a2800f99cd7d4..c7dccb10389fda604b31052c01e9fe1e8e54b0cf 100644
--- a/NodeApp/src/helpers/GlobalHelper.ts
+++ b/NodeApp/src/helpers/GlobalHelper.ts
@@ -5,18 +5,10 @@ import Config              from '../config/Config';
 class GlobalHelper {
     public runCommandDefinition(command: Command) {
         command
-        .option('-p, --path <value>', 'assignment 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 }));
-
-        return command;
-    }
-
-    public completionCommandDefinition(command: Command) {
-        command
-        .option('-f, --file <filename>')
-        .option('-y, --force', 'don\'t ask for file overwrite confirmation');
+            .option('-p, --path <value>', 'assignment 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 }));
 
         return command;
     }