diff --git a/Taskfile.yaml b/Taskfile.yaml
new file mode 120000
index 0000000000000000000000000000000000000000..be388a9b6f3cb9f81816a8a556c5329e9b472e3c
--- /dev/null
+++ b/Taskfile.yaml
@@ -0,0 +1 @@
+/nix/store/iyrsyrz5b4g0sqcinrck6sk1diy868x0-Taskfile.yaml
\ No newline at end of file
diff --git a/Taskfile.yml b/Taskfile.yml
deleted file mode 100644
index adca507c01815ac863d31cb599de1917770a0498..0000000000000000000000000000000000000000
--- a/Taskfile.yml
+++ /dev/null
@@ -1,24 +0,0 @@
-version: '3'
-
-tasks:
-  
-  default:
-    aliases:
-      - help
-    cmds:
-      - task --list
-  
-  build:
-    desc: Build the app
-    aliases:
-      - b
-    vars:
-      DEVENV_ROOT:
-        sh: echo "$DEVENV_ROOT"
-    
-    cmds:
-      - devenv shell build-app
-    sources:
-      - source/**/*.go
-      - source/**/*.mod
-      - dist/**
diff --git a/devenv.nix b/devenv.nix
index 41fa742225642b676a641894c5cfd8ab6500e17b..b919e059e404255fbcd751b729dac7c120d2470c 100644
--- a/devenv.nix
+++ b/devenv.nix
@@ -1,40 +1,149 @@
 # See full reference at https://devenv.sh/reference/options/
 { pkgs, inputs, phps, lib, config, modulesPath,... }:
 
+let 
+
+  taskfileYaml = pkgs.writeTextFile {
+    name = "Taskfile.yaml";
+    text = ''
+## THIS FILE IS AUTOGENERATED. DO NOT EDIT THIS FILE DIRECTLY.
+version: '3'
+
+tasks:
+    build:
+      desc: Build the app
+    aliases:
+      - b
+    vars:
+      DEVENV_ROOT:
+         sh: echo "$DEVENV_ROOT"
+    
+    cmds:
+      - devenv shell build-app
+    sources:
+      - source/**/*.go
+      - source/**/*.mod
+      - dist/**
+    
+    commit:
+      desc: Commit a feature
+      cmds:
+        - do-git-commit
+      silent: true    
+    
+    default:
+      desc: Print this help message
+      aliases: [d, help]
+      cmds:
+        - task --list
+      silent: true
+    
+    '';
+  };
+  
+  
+  in
+
+
 {
 
   env.APP_NAME = "version";
 
   # https://devenv.sh/packages/
-  packages = [
+  packages = with pkgs; [
     inputs.version.defaultPackage."${builtins.currentSystem}"
-    pkgs.git
-    pkgs.gcc12
-    pkgs.go-task
-    pkgs.blackbox
-    pkgs.blackbox-terminal
-    pkgs.jq
-    pkgs.delve
-    pkgs.gdlv
-    pkgs.wget
-    pkgs.glab
-    pkgs.unixtools.xxd
-    pkgs.libffi
-    pkgs.zlib
-    pkgs.procps
-    pkgs.php81Extensions.xdebug
-    pkgs.ranger
-    pkgs.meld
-    pkgs.gnused
-    pkgs.coreutils-full
-    pkgs.gnugrep
-    pkgs.gnumake
-    pkgs.util-linux
-    pkgs.httpie
-    pkgs.netcat
-    pkgs.memcached
-    pkgs.fd    
+    appimage-run
+    awscli2
+    blackbox
+    blackbox-terminal
+    coreutils-full
+    d2
+    dbeaver
+    delve
+    dialog
+    dive
+    docker-client
+    docker-compose
+    drill
+    exa
+    fd    
+    gawk
+    gcc12
+    gdlv
+    git
+    glab
+    gnugrep
+    gnum4
+    gnumake
+    gnupg
+    gnused
+    go-task
+    graphviz
+    gum
+    httpie
+    hurl
+    jq
+    libffi
+    libffi
+    logrotate
+    mdbook
+    mdbook-admonish
+    mdbook-cmdrun
+    mdbook-d2
+    mdbook-emojicodes
+    mdbook-graphviz
+    mdbook-linkcheck
+    mdbook-mermaid
+    mdbook-pdf
+    meld
+    memcached
+    netcat
+    netcat
+    oha
+    openai
+    oxker
+    pandoc-katex
+    plantuml
+    pprof
+    procps
+    ranger
+    termbook
+    unixtools.arp
+    unixtools.xxd
+    unzip
+    util-linux
+    wget
+    zlib
+    
   ];
+  
+  scripts.update-files.exec = ''
+    update_symlink() {
+        local source_path="$1"
+        local target_path="$2"
+        local file_name="$(basename "$target_path")"
+
+        if [[ -L "$source_path" ]]; then
+            if [[ "$(readlink "$source_path")" != "$target_path" ]]; then
+                # Link exists but is not up to date
+                rm "$source_path"
+                ln -s "$target_path" "$source_path"
+            fi
+        elif [[ -e "$source_path" ]]; then
+            echo "$file_name already exists. Please rename or delete it."
+            exit 1  
+        else
+            ln -s "$target_path" "$source_path"
+        fi
+    }
+
+    # Usage for Taskfile.yaml
+    update_symlink "${config.devenv.root}/Taskfile.yaml" "${taskfileYaml}"
+
+
+
+
+  '';  
 
 
   # https://devenv.sh/languages/
@@ -182,5 +291,341 @@ echo "done"
 
   '';
 
+  scripts.do-git-commit.exec = ''
+    #!${pkgs.bash}/bin/bash
+
+        # Define colors if the terminal supports it
+        if [ -t 1 ]; then
+          RED='\033[0;31m'
+          GREEN='\033[0;32m'
+          RESET='\033[0m'
+          BOLD='\033[1m'
+        else
+          RED=""
+          GREEN=""
+          RESET=""
+        fi
+
+        step=1
+
+        reset
+        clear
+
+        # create random log file
+        LOGFILE="$(mktemp)"
+        if [ $? -ne 0 ]; then
+          echo -e "''${RED}✖ Could not create temporary log file. Exiting.''${RESET}"
+          exit 1
+        fi
+
+        log_and_display() {
+          echo -e "''${GREEN}==> $step. $1''${RESET}" | tee -a $LOGFILE
+          step=$((step + 1))
+        }
+
+        log_error_and_display() {
+          echo -e "''${RED}==> $step. $1''${RESET}" | tee -a $LOGFILE
+        }
+
+        printLogfileAndExit() {
+          exit_code=$1
+          echo -e "\n\n========================================\n\n\n"
+
+          echo -e "\n\n''${BOLD}Git and GitLab Automation Script''${RESET}\n\nI have performed the following actions:\n\n"
+          cat "$LOGFILE"
+
+          # Optional: Remove log file
+          ${pkgs.coreutils}/bin/rm -f "$LOGFILE"
+
+          if [ $exit_code -eq 0 ]; then
+            echo -e "\n''${GREEN}✔''${RESET} All actions were successful" | tee -a $LOGFILE
+          elif [ $exit_code -eq -1 ]; then
+            echo -e "\n''${RED}✖''${RESET} The script was manually cancelled" | tee -a $LOGFILE
+            exit_code=0
+          else
+            echo -e "\n''${RED}✖''${RESET} Some actions failed" | tee -a $LOGFILE
+          fi
+
+          exit $exit_code
+        }
+
+        print_headline() {
+          local title=$1
+          local underline=$(printf '─%.0s' $(seq 1 ''${#title}))
+          echo -e "\n\n''${BOLD}''${title}\n''${underline}''${RESET}\n"
+        }
+
+        do_cancel() {
+          echo -e "''${RED}==> ✖ Cancelled.''${RESET}" | tee -a $LOGFILE
+          printLogfileAndExit -1
+        }
+
+        # Function for unified logging and display
+        log_action() {
+          if [ $? -eq 0 ]; then
+            echo -e "    ''${GREEN}✔''${RESET} $1: Successful" | tee -a $LOGFILE
+          else
+            echo -e "    ''${RED}✖''${RESET} $1: Failed" | tee -a $LOGFILE
+            printLogfileAndExit 1
+          fi
+        }
+
+        print_what_to_do() {
+          echo -e "\n\nWhat do you want to do?\n"
+        }
+
+        git_status=$(git status --porcelain)
+        if [[ -z "$git_status" ]]; then
+          log_error_and_display "No changes to commit. Exiting."
+          printLogfileAndExit 0
+        fi
+
+        print_headline "Choose commit type"
+        selection=$(gum choose "feat: (new feature for the user, not a new feature for build script)" "fix: (bug fix for the user, not a fix to a build script)" "chore: (updating grunt tasks etc.; no production code change)" "docs: (changes to the documentation)" "style: (formatting, missing semi colons, etc; no production code change)" "refactor: (refactoring production code, eg. renaming a variable)" "test: (adding missing tests, refactoring tests; no production code change)" "Cancel")
+
+        commit_type=$(echo "$selection" | awk -F':' '{print $1}')
+
+        if [[ "$commit_type" == "Cancel" ]]; then
+          do_cancel
+        fi
+
+        log_and_display "You chose the commit type: $commit_type"
+
+        # NEXT STEP ISSUE HANDLING ############################################################################################################
+        #log_and_display "Issue handling"
+
+        gitlabIssues=()
+        while IFS= read -r line; do
+          if [[ $line =~ ^# ]]; then
+            id=$(echo "$line" | awk '{print substr($1, 2)}')
+            title=$(echo "$line" | awk -F'about' '{print $1}' | awk '{$1=$2=""; print substr($0, 3)}')
+            gitlabIssues+=("$id > $title")
+          fi
+        done < <(gum spin --spinner dot --show-output --title "Ask gitlab ..." -- glab issue list --output-format=details)
+
+        ## if issues are available, ask if user wants to use an existing issue or create a new one
+        createOption="Create new issue"
+        existingOption="Use existing issue"
+        cancelOption="Cancel"
+
+        print_headline "Choose issue handling"
+        if [ ''${#gitlabIssues[@]} -eq 0 ]; then
+          log_and_display "There are no issues available."
+
+          print_what_to_do
+          choice=$(gum choose "$createOption" "$cancelOption")
+
+        else
+          log_and_display "There are ''${#gitlabIssues[@]} issues available."
+          print_what_to_do
+          choice=$(gum choose "$createOption" "$existingOption" "$cancelOption")
+        fi
+
+        if [[ "$choice" == "$cancelOption" ]]; then
+          do_cancel
+        fi
+
+        ## array of issue ids
+        work_on_issue_ids=()
+
+        issue_text=""
+
+        if [[ "$choice" == "$createOption" ]]; then
+          print_headline "Create new issue"
+          issue_text=$(gum input --placeholder "Enter issue title")
+          echo -e "Enter issue description. ''${RED}End with Ctrl+D''${RESET}\n"
+          issue_description=$(gum write --placeholder "Enter issue description. End with Ctrl+D")
+
+          if [[ -z "$issue_text" ]]; then
+            log_error_and_display "Issue title is empty. Exiting."
+            printLogfileAndExit 1
+          fi
+
+          log_and_display "You entered the issue title: $issue_text"
+          log_and_display "You entered the issue description: $issue_description"
+          echo -e "\n"
+
+          gum confirm "Do you want to create this issue?"
+          # gum confirm exits with status 0 if confirmed and status 1 if cancelled.
+          if [ $? -eq 1 ]; then
+            do_cancel
+          fi
+
+          issue_output=$(glab issue create -t"$issue_text" --no-editor --description "$issue_description")
+          issue_id=$(echo "$issue_output" | grep -oP '(?<=/issues/)\d+')
+
+          work_on_issue_ids+=("$issue_id")
+
+          log_action "glab issue with id $issue_id created"
+
+        else
+
+          print_headline "Use existing issue"
+          echo -e "Select issue with arrow keys and press tab or space to select. Press enter to confirm.\n"
+          issue_ids=$(gum choose --no-limit "''${gitlabIssues[@]}")
+
+          # assign issue_ids to work_on_issue_ids. iterate over lines and take integer from beginning of line
+          while IFS= read -r line; do
+            work_on_issue_ids+=($(echo "$line" | grep -oP '^\d+'))
+          done <<<"$issue_ids"
+
+        fi
+
+        if [ ''${#work_on_issue_ids[@]} -eq 0 ]; then
+          log_and_display "No issue selected. Exiting."
+          printLogfileAndExit 0
+        fi 
+
+        # NEXT STEP COMMIT MESSAGE ############################################################################################################
+        # print work_on_issue_ids
+        work_on_issue_ids_string=""
+        for i in "''${work_on_issue_ids[@]}"; do
+          work_on_issue_ids_string+="#$i "
+        done
+
+        log_and_display "You chose to work on the following issues: ''${work_on_issue_ids_string}"
+
+
+        print_headline "Check for changes to commit"
+
+        # ' ' = unmodified
+        # M = modified
+        # T = file type changed (regular file, symbolic link or submodule)
+        # A = added
+        # D = deleted
+        # R = renamed
+        # C = copied (if config option status.renames is set to "copies")
+        # U = updated but unmerged
+        # https://man.freebsd.org/cgi/man.cgi?query=git-status&sektion=1&manpath=freebsd-release-ports
+
+        count_all_changes=$(echo "$git_status" | wc -l)
+        count_staged_changes=$(echo "$git_status" | grep -c '^M')
+        count_new_staged_files=$(echo "$git_status" | grep -c '^A')
+        count_staged_changes=$((count_staged_changes + count_new_staged_files))
+
+        git_options_all="All $count_all_changes changes"
+        git_options_staged="Only the $count_staged_changes staged changes"
+        git_options_select_files="Select files"
+        git_options_cancel="Cancel"
+
+        git_options_array=()
+        if [[ $count_all_changes -gt 0 ]]; then
+          git_options_array+=("$git_options_all")
+        fi
+
+        if [[ $count_staged_changes -gt 0 ]]; then
+          git_options_array+=("$git_options_staged")
+        fi
+
+        git_options_array+=( "$git_options_select_files" )
+        git_options_array+=( "$git_options_cancel" )
+
+
+        selection=$(gum choose "''${git_options_array[@]}")
+        if [[ "$selection" == "$git_options_cancel" ]]; then
+          do_cancel
+        fi
+
+        if [[ "$selection" == "$git_options_all" ]]; then
+          git add -A
+          echo "1"
+        elif [[ "$selection" == "$git_options_select_files" ]]; then
+
+          files=()
+          while IFS= read -r line; do
+            files+=("$line")
+          done <<<"$git_status"
+
+          selected_files=$(gum choose --no-limit "''${files[@]}")
+
+          # no files selected
+          if [[ -z "$selected_files" ]]; then
+            log_and_display "No files selected. Exiting."
+            printLogfileAndExit 0
+          fi
+
+          # add selected files
+          while IFS= read -r line; do
+            ## git proclimne could have letter, ? or ! at the beginning of the line
+            file=$(echo "$line" | awk '{print $2}')
+            if [[ -z "$file" || ! -f "$file" ]]; then
+              log_and_display "No file found in line: $line"
+              continue
+            fi
+
+            git add "$file"
+          done <<<"$selected_files"
+
+        fi
+
+        ## count staged changes again and print
+        count_staged_changes=$(echo "$git_status" | grep -c '^M')
+        count_new_staged_files=$(echo "$git_status" | grep -c '^A')
+        count_staged_changes=$((count_staged_changes + count_new_staged_files))
+
+        log_and_display "You have $count_staged_changes staged changes to commit."
+
+        # NEXT STEP COMMIT MESSAGE ############################################################################################################
+
+        print_headline "Enter commit message"
+        commit_message=$(gum input --placeholder "Enter commit message" --value "$commit_type: $issue_text $work_on_issue_ids_string")
+
+        if [[ -z "$commit_message" ]]; then
+          log_error_and_display "Commit message is empty. Exiting."
+          printLogfileAndExit 1
+        fi
+
+        log_and_display "You entered the commit message: $commit_message"
+
+        gum confirm "Do you want to commit with this message?"
+        if [ $? -eq 1 ]; then
+          do_cancel
+        fi
+
+        # NEXT STEP COMMIT ####################################################################################################################
+        print_headline "Committing changes"
+
+        if ! git commit -m "$commit_message" ; then
+          log_error_and_display "Commit failed. Exiting."
+          printLogfileAndExit 1
+        fi
+
+        log_and_display "Commit successful."
+
+        # NEXT STEP PUSH ######################################################################################################################
+
+        print_headline "Pushing changes"
+
+        if ! git push ; then
+          log_error_and_display "Push failed. Exiting."
+          printLogfileAndExit 1
+        fi
+
+        log_and_display "Push successful."
+
+        # Close issue ######################################################################################################################
+
+        print_headline "Closing issues"
+
+        for issue_id in "''${work_on_issue_ids[@]}"; do
+
+          gum confirm "Do you want to close issue #$issue_id?"
+          if [ $? -eq 1 ]; then
+            continue
+          fi
+
+          if ! glab issue close "$issue_id" ; then
+            log_error_and_display "Closing issue $issue_id failed. Exiting."
+          else
+            log_and_display "Closing issue $issue_id successful."  
+          fi
+        done
+
+        printLogfileAndExit 0
+  '';
+  
+  enterShell = ''
+    update-files
+  '';  
 
 }
diff --git a/source/errors.go b/source/errors.go
index 9a9b0640ccdef7352fb724860c5af53001a980a2..73afad75ac6313103e222dae1b09990e41a98007 100644
--- a/source/errors.go
+++ b/source/errors.go
@@ -9,4 +9,5 @@ var (
 	noSemverError       = fmt.Errorf("no semver")
 	notFoundError       = fmt.Errorf("not found")
 	multipleFoundError  = fmt.Errorf("multiple found")
+	ErrStopIteration    = fmt.Errorf("stop iteration")
 )
diff --git a/source/git.go b/source/git.go
index e7eb2a8d9cf5a860e2646df42841bd86f0d3e8bc..d4c8404092c41f36faa8cb63d23b59d641ddb357 100644
--- a/source/git.go
+++ b/source/git.go
@@ -127,23 +127,34 @@ func getTagCommit(tag string) (*object.Commit, error) {
 	var tagCommit *object.Commit
 	err = tags.ForEach(func(t *plumbing.Reference) error {
 		if t.Name().Short() == tag {
+
 			tagObj, err := r.TagObject(t.Hash())
-			if err != nil {
-				return err
-			}
-			tagCommit, err = tagObj.Commit()
-			if err != nil {
+			if err == plumbing.ErrObjectNotFound {
+				commit, err := r.CommitObject(t.Hash())
+				if err != nil {
+					return err
+				}
+				tagCommit = commit
+			} else if err != nil {
 				return err
+			} else {
+
+				tagCommit, err = tagObj.Commit()
+				if err != nil {
+					return err
+				}
 			}
-			return storer.ErrStop // stop iteration
+			return ErrStopIteration
 		}
 		return nil
 	})
-	if err != nil {
+
+	if err != nil && err != ErrStopIteration {
 		return nil, fmt.Errorf("failed to iterate over tags: %v", err)
 	}
+
 	if tagCommit == nil {
-		return nil, fmt.Errorf("tag commit not found in commit log")
+		return nil, fmt.Errorf("tag '%s' not found in commit log", tag)
 	}
 
 	return tagCommit, nil