Repository: JanDeDobbeleer/oh-my-posh
Pull request #4187 (2023-08-25 19:42:04)

feat(platform): add support for OpenBSD
### Prerequisites

- [X] I have read and understood the [contributing guide][CONTRIBUTING.md].
- [X] The commit message follows the [conventional commits][cc] guidelines.
- [X] Tests for the changes have been added (for bug fixes / features).
- [X] Docs have been added/updated (for bug fixes / features).

### Description

This PR adds battery support for OpenBSD, allowing Oh My Posh to compile and run on this operating system.

<!---

Tips:

If you're not comfortable with working with Git,
we're working a guide (https://ohmyposh.dev/docs/contributing_git) to help you out.
Oh My Posh advises GitKraken (https://www.gitkraken.com/invite/nQmDPR9D) as your preferred cross platform Git GUI power tool.

-->

[CONTRIBUTING.md]: https://github.com/JanDeDobbeleer/oh-my-posh/blob/main/CONTRIBUTING.md
[cc]: https://www.conventionalcommits.org/en/v1.0.0/#summary



Patch:
diff --git a/.all-contributorsrc b/.all-contributorsrc
index 7d2accc0..9bbcc152 100644
--- a/.all-contributorsrc
+++ b/.all-contributorsrc
@@ -1651,7 +1651,9 @@
       "avatar_url": "https://avatars.githubusercontent.com/u/54537427?v=4",
       "profile": "http://www.kemadev.fr/fr/",
       "contributions": [
-        "code"
+        "code",
+        "design",
+        "doc"
       ]
     },
     {
@@ -1861,6 +1863,53 @@
       "contributions": [
         "code"
       ]
+    },
+    {
+      "login": "darthwalsh",
+      "name": "Carl Walsh",
+      "avatar_url": "https://avatars.githubusercontent.com/u/2829438?v=4",
+      "profile": "https://carlwa.com",
+      "contributions": [
+        "doc"
+      ]
+    },
+    {
+      "login": "ercpereda",
+      "name": "Ernesto R. C. Pereda",
+      "avatar_url": "https://avatars.githubusercontent.com/u/13546685?v=4",
+      "profile": "https://github.com/ercpereda",
+      "contributions": [
+        "code",
+        "design",
+        "doc"
+      ]
+    },
+    {
+      "login": "0Ky",
+      "name": "ｃｒｙｐｔｉｘ",
+      "avatar_url": "https://avatars.githubusercontent.com/u/16103757?v=4",
+      "profile": "https://github.com/0Ky",
+      "contributions": [
+        "doc"
+      ]
+    },
+    {
+      "login": "ehawman",
+      "name": "Evan Hawman",
+      "avatar_url": "https://avatars.githubusercontent.com/u/52979227?v=4",
+      "profile": "https://github.com/ehawman",
+      "contributions": [
+        "design"
+      ]
+    },
+    {
+      "login": "ZerdoX-x",
+      "name": "Mark Lansky",
+      "avatar_url": "https://avatars.githubusercontent.com/u/49815452?v=4",
+      "profile": "https://zerdox.dev",
+      "contributions": [
+        "design"
+      ]
     }
   ],
   "contributorsPerLine": 7,
diff --git a/.github/workflows/android.yml b/.github/workflows/android.yml
index 5000a635..fc6555d9 100644
--- a/.github/workflows/android.yml
+++ b/.github/workflows/android.yml
@@ -9,7 +9,7 @@ jobs:
     container: ghcr.io/jandedobbeleer/golang-android-container:latest
     steps:
     - name: Checkout code 👋
-      uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744
+      uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac
     - name: Build
       run: |
         VERSION=$(echo "${{ github.event.release.name }}" | cut -c2-)
diff --git a/.github/workflows/build_code.yml b/.github/workflows/build_code.yml
index e090e295..5f8c76b2 100644
--- a/.github/workflows/build_code.yml
+++ b/.github/workflows/build_code.yml
@@ -17,18 +17,18 @@ jobs:
         shell: pwsh
     steps:
     - name: Checkout code 👋
-      uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744
+      uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac
     - name: Install Go 🗳
       uses: ./.github/workflows/composite/bootstrap-go
     - name: Run GoReleaser 🚀
-      uses: goreleaser/goreleaser-action@3fa32b8bb5620a2c1afe798654bbad59f9da4906
+      uses: goreleaser/goreleaser-action@7ec5c2b0c6cdda6e8bbb49444bc797dd33d74dd8
       with:
         distribution: goreleaser
         version: latest
         args: build --rm-dist --snapshot --skip-post-hooks --skip-before
         workdir: src
     - name: Archive production artifacts
-      uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce
+      uses: actions/upload-artifact@a8a3f3ad30e3422c9c7b888a15615d19a852ae32
       with:
         name: builds
         retention-days: 1
diff --git a/.github/workflows/close_themes_pr.yml b/.github/workflows/close_themes_pr.yml
index dc733fd3..77963f6d 100644
--- a/.github/workflows/close_themes_pr.yml
+++ b/.github/workflows/close_themes_pr.yml
@@ -9,7 +9,7 @@ jobs:
     runs-on: ubuntu-latest
     steps:
       - name: Checkout code 👋
-        uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744
+        uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac
       - name: Check and close 🔐
         uses: actions/github-script@d7906e4ad0b1822421a7e6a35d5ca353c962f410
         with:
diff --git a/.github/workflows/code.yml b/.github/workflows/code.yml
index 4cf13383..0d1bdeb1 100644
--- a/.github/workflows/code.yml
+++ b/.github/workflows/code.yml
@@ -22,10 +22,10 @@ jobs:
     - name: Install Go
       uses: actions/setup-go@93397bea11091df50f3d7e59dc26a7711a8bcfbe
       with:
-        go-version: 1.20.5
+        go-version: 1.21
         cache-dependency-path: src/go.sum
     - name: Checkout code
-      uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744
+      uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac
     - name: Golang CI
       uses: golangci/golangci-lint-action@3a919529898de77ec3da873e3063ca4b10e7f5cc
       with:
diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml
index bd797b1e..af5524a8 100644
--- a/.github/workflows/codeql.yml
+++ b/.github/workflows/codeql.yml
@@ -22,10 +22,10 @@ jobs:
     - name: Install Go
       uses: actions/setup-go@93397bea11091df50f3d7e59dc26a7711a8bcfbe
       with:
-        go-version: 1.20.5
+        go-version: 1.21
         cache-dependency-path: src/go.sum
     - name: Checkout code
-      uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744
+      uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac
     - name: Initialize CodeQL
       uses: github/codeql-action/init@cdcdbb579706841c47f7063dda365e292e5cad7a
       with:
diff --git a/.github/workflows/commits.yml b/.github/workflows/commits.yml
index 5130d2c4..aa802b05 100644
--- a/.github/workflows/commits.yml
+++ b/.github/workflows/commits.yml
@@ -6,7 +6,7 @@ jobs:
   commitlint:
     runs-on: ubuntu-latest
     steps:
-      - uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744
+      - uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac
         with:
           fetch-depth: 0
       - uses: wagoid/commitlint-github-action@6319f54d83768b60acd6fd60e61007ccc583e62f
diff --git a/.github/workflows/composite/bootstrap-go/action.yml b/.github/workflows/composite/bootstrap-go/action.yml
index 7de30772..176ed106 100644
--- a/.github/workflows/composite/bootstrap-go/action.yml
+++ b/.github/workflows/composite/bootstrap-go/action.yml
@@ -9,7 +9,7 @@ runs:
   steps:
     - name: Install Go 🚀
       env:
-        VERSION: "1.20.5"
+        VERSION: "1.21.0"
       shell: pwsh
       run: |
         # Delete conflicting go versions first
diff --git a/.github/workflows/contributors.yml b/.github/workflows/contributors.yml
index 5cb14a0f..108d7841 100644
--- a/.github/workflows/contributors.yml
+++ b/.github/workflows/contributors.yml
@@ -10,7 +10,7 @@ jobs:
     if: github.event.pull_request.merged == true
     steps:
       - name: Checkout code 👋
-        uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744
+        uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac
       - name: Check and comment 👩🏾‍💻
         uses: actions/github-script@d7906e4ad0b1822421a7e6a35d5ca353c962f410
         with:
diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml
index 61ed90ee..ae18d524 100644
--- a/.github/workflows/docs.yml
+++ b/.github/workflows/docs.yml
@@ -18,14 +18,14 @@ jobs:
     runs-on: ubuntu-latest
     name: Build and Deploy
     steps:
-      - uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744
+      - uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac
         with:
           submodules: true
           persist-credentials: false
       - name: Install Go
         uses: actions/setup-go@93397bea11091df50f3d7e59dc26a7711a8bcfbe
         with:
-          go-version: 1.20.5
+          go-version: 1.21
           cache-dependency-path: src/go.sum
       - uses: actions/setup-node@5e21ff4d9bc1a8cf6de233a3057d20ec6b3fb69d
         with:
diff --git a/.github/workflows/gomod.yml b/.github/workflows/gomod.yml
index 1e695fa8..06b20845 100644
--- a/.github/workflows/gomod.yml
+++ b/.github/workflows/gomod.yml
@@ -12,10 +12,10 @@ jobs:
     - name: Install Go
       uses: actions/setup-go@93397bea11091df50f3d7e59dc26a7711a8bcfbe
       with:
-        go-version: 1.20.5
+        go-version: 1.21
         cache-dependency-path: src/go.sum
     - name: Checkout code
-      uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744
+      uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac
     - name: Check for unused dependencies
       run: |
         go mod tidy
diff --git a/.github/workflows/inno.yml b/.github/workflows/inno.yml
index c0104170..6ed7da6a 100644
--- a/.github/workflows/inno.yml
+++ b/.github/workflows/inno.yml
@@ -15,7 +15,7 @@ jobs:
         working-directory: ${{ github.workspace }}/packages/inno
     steps:
     - name: Checkout code 👋
-      uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744
+      uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac
     - name: Build installer 📦
       id: build
       env:
diff --git a/.github/workflows/markdown.yml b/.github/workflows/markdown.yml
index 3d9daeb9..c93398b0 100644
--- a/.github/workflows/markdown.yml
+++ b/.github/workflows/markdown.yml
@@ -7,7 +7,7 @@ jobs:
     runs-on: ubuntu-latest
     steps:
     - name: Checkout code
-      uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744
+      uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac
     - name: Lint files
       uses: articulate/actions-markdownlint@17b8abe7407cd17590c006ecc837c35e1ac3ed83
       with:
diff --git a/.github/workflows/merge_contributions_pr.yml b/.github/workflows/merge_contributions_pr.yml
index 8e552ba4..4c83cf7b 100644
--- a/.github/workflows/merge_contributions_pr.yml
+++ b/.github/workflows/merge_contributions_pr.yml
@@ -10,7 +10,7 @@ jobs:
     runs-on: ubuntu-latest
     steps:
       - name: Checkout code 👋
-        uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744
+        uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac
       - name: Check and merge ⛙
         uses: actions/github-script@d7906e4ad0b1822421a7e6a35d5ca353c962f410
         with:
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index cfa21628..09e0240e 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -19,7 +19,7 @@ jobs:
       skipped: ${{ steps.changelog.outputs.skipped }}
     steps:
       - name: Checkout code 👋
-        uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744
+        uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac
       - name: Create changelog ✍️
         id: changelog
         uses: TriPSs/conventional-changelog-action@5f865e4ca373f189640721c0cca04ea3e0e7bfcd
@@ -41,7 +41,7 @@ jobs:
         working-directory: ${{ github.workspace }}/src
     steps:
       - name: Checkout code 👋
-        uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744
+        uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac
       - name: Install Go 🗳
         uses: ./.github/workflows/composite/bootstrap-go
       - name: Tag HEAD 😸
@@ -50,7 +50,7 @@ jobs:
           git config --global user.email "41898282+github-actions[bot]@users.noreply.github.com"
           git tag ${{ needs.changelog.outputs.tag }}
       - name: Install cosign 🔑
-        uses: sigstore/cosign-installer@6e04d228eb30da1757ee4e1dd75a0ec73a653e06
+        uses: sigstore/cosign-installer@11086d25041f77fe8fe7b9ea4e48e3b9192b8f19
         with:
           cosign-release: 'v1.4.0'
       - name: Private Key 🔐
@@ -60,7 +60,7 @@ jobs:
         env:
           COSIGN_KEY: ${{secrets.COSIGN_KEY}}
       - name: Run GoReleaser 🚀
-        uses: goreleaser/goreleaser-action@3fa32b8bb5620a2c1afe798654bbad59f9da4906
+        uses: goreleaser/goreleaser-action@7ec5c2b0c6cdda6e8bbb49444bc797dd33d74dd8
         with:
           distribution: goreleaser
           version: latest
diff --git a/.github/workflows/scoop.yml b/.github/workflows/scoop.yml
index f5c4a714..fc5db0bd 100644
--- a/.github/workflows/scoop.yml
+++ b/.github/workflows/scoop.yml
@@ -18,7 +18,7 @@ jobs:
         working-directory: ${{ github.workspace }}/packages/scoop
     steps:
     - name: Checkout code 👋
-      uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744
+      uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac
     - name: Update Template ✍️
       run: |
         ./build.ps1 -Version ${{ github.event.inputs.version }}
diff --git a/.github/workflows/social.yml b/.github/workflows/social.yml
index 26bd412b..616c87b4 100644
--- a/.github/workflows/social.yml
+++ b/.github/workflows/social.yml
@@ -13,7 +13,7 @@ jobs:
         with:
           node-version: 18
       - name: Checkout code 👋
-        uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744
+        uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac
       - name: Skyt 🌤️
         working-directory: scripts/bluesky
         env:
diff --git a/.github/workflows/winget.yml b/.github/workflows/winget.yml
index 79613a91..90e7f6d6 100644
--- a/.github/workflows/winget.yml
+++ b/.github/workflows/winget.yml
@@ -17,7 +17,7 @@ jobs:
       WINGETCREATE_TOKEN: ${{ secrets.WINGETCREATE_TOKEN }}
     steps:
     - name: Checkout code 👋
-      uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744
+      uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac
     - name: Create manifest and submit PR 📦
       run: |
         ./build.ps1 -Version "${{ github.event.inputs.version }}" -Token $env:WINGETCREATE_TOKEN
diff --git a/src/ansi/ansi_writer.go b/src/ansi/ansi_writer.go
index 7a8290f7..820ad1a2 100644
--- a/src/ansi/ansi_writer.go
+++ b/src/ansi/ansi_writer.go
@@ -86,13 +86,12 @@ type Writer struct {
 	builder strings.Builder
 	length  int
 
-	foreground        Color
-	background        Color
-	currentForeground Color
-	currentBackground Color
-	runes             []rune
-	transparent       bool
-	invisible         bool
+	foreground  Color
+	background  Color
+	current     ColorHistory
+	runes       []rune
+	transparent bool
+	invisible   bool
 
 	shell                 string
 	format                string
@@ -303,7 +302,8 @@ func (w *Writer) Write(background, foreground, text string) {
 			colorOverride = false
 		}
 		if colorOverride {
-			w.currentBackground, w.currentForeground = w.asAnsiColors(match[BG], match[FG])
+			w.current.Add(w.asAnsiColors(match[BG], match[FG]))
+			// w.current.Background(), w.current.Foreground() = w.asAnsiColors(match[BG], match[FG])
 		}
 	}
 	w.writeSegmentColors()
@@ -343,8 +343,7 @@ func (w *Writer) Write(background, foreground, text string) {
 	w.writeEscapedAnsiString(resetStyle.End)
 
 	// reset current
-	w.currentBackground = ""
-	w.currentForeground = ""
+	w.current.Pop()
 }
 
 func (w *Writer) String() (string, int) {
@@ -378,11 +377,11 @@ func (w *Writer) writeSegmentColors() {
 	// use correct starting colors
 	bg := w.background
 	fg := w.foreground
-	if !w.currentBackground.IsEmpty() {
-		bg = w.currentBackground
+	if !w.current.Background().IsEmpty() {
+		bg = w.current.Background()
 	}
-	if !w.currentForeground.IsEmpty() {
-		fg = w.currentForeground
+	if !w.current.Foreground().IsEmpty() {
+		fg = w.current.Foreground()
 	}
 
 	// ignore processing fully tranparent colors
@@ -408,45 +407,14 @@ func (w *Writer) writeSegmentColors() {
 	}
 
 	// set current colors
-	w.currentBackground = bg
-	w.currentForeground = fg
+	w.current.Add(bg, fg)
 }
 
 func (w *Writer) writeColorOverrides(match map[string]string, background string, i int) int {
 	position := i
 	// check color reset first
 	if match[ANCHOR] == resetStyle.AnchorEnd {
-		// make sure to reset the colors if needed
-		position += len([]rune(resetStyle.AnchorEnd)) - 1
-
-		// do not reset when colors are identical
-		if w.currentBackground == w.background && w.currentForeground == w.foreground {
-			return position
-		}
-
-		// do not restore colors at the end of the string, we print it anyways
-		if position == len(w.runes)-1 {
-			return position
-		}
-
-		if w.transparent {
-			w.writeEscapedAnsiString(transparentEnd)
-		}
-
-		if w.background.IsClear() {
-			w.writeEscapedAnsiString(backgroundStyle.End)
-		}
-
-		if w.currentBackground != w.background && !w.background.IsClear() {
-			w.writeEscapedAnsiString(fmt.Sprintf(colorise, w.background))
-		}
-
-		if (w.currentForeground != w.foreground || w.transparent) && !w.foreground.IsClear() {
-			w.writeEscapedAnsiString(fmt.Sprintf(colorise, w.foreground))
-		}
-
-		w.transparent = false
-		return position
+		return w.endColorOverride(position)
 	}
 
 	position += len([]rune(match[ANCHOR])) - 1
@@ -465,48 +433,110 @@ func (w *Writer) writeColorOverrides(match map[string]string, background string,
 	if match[FG] == Transparent && len(match[BG]) == 0 {
 		match[BG] = background
 	}
-	w.currentBackground, w.currentForeground = w.asAnsiColors(match[BG], match[FG])
+
+	bg, fg := w.asAnsiColors(match[BG], match[FG])
 
 	// ignore processing fully tranparent colors
-	w.invisible = w.currentForeground.IsTransparent() && w.currentBackground.IsTransparent()
+	w.invisible = fg.IsTransparent() && bg.IsTransparent()
 	if w.invisible {
 		return position
 	}
 
 	// make sure we have colors
-	if w.currentForeground.IsEmpty() {
-		w.currentForeground = w.foreground
+	if fg.IsEmpty() {
+		fg = w.foreground
 	}
-	if w.currentBackground.IsEmpty() {
-		w.currentBackground = w.background
+	if bg.IsEmpty() {
+		bg = w.background
 	}
 
-	if w.currentForeground.IsTransparent() && len(w.TerminalBackground) != 0 {
+	w.current.Add(bg, fg)
+
+	if w.current.Foreground().IsTransparent() && len(w.TerminalBackground) != 0 {
 		background := w.getAnsiFromColorString(w.TerminalBackground, false)
 		w.writeEscapedAnsiString(fmt.Sprintf(colorise, background))
-		w.writeEscapedAnsiString(fmt.Sprintf(colorise, w.currentBackground.ToForeground()))
+		w.writeEscapedAnsiString(fmt.Sprintf(colorise, w.current.Background().ToForeground()))
 		return position
 	}
 
-	if w.currentForeground.IsTransparent() && !w.currentBackground.IsTransparent() {
+	if w.current.Foreground().IsTransparent() && !w.current.Background().IsTransparent() {
 		w.transparent = true
-		w.writeEscapedAnsiString(fmt.Sprintf(transparent, w.currentBackground))
+		w.writeEscapedAnsiString(fmt.Sprintf(transparent, w.current.Background()))
 		return position
 	}
 
-	if w.currentBackground != w.background {
+	if w.current.Background() != w.background {
 		// end the colors in case we have a transparent background
-		if w.currentBackground.IsTransparent() {
+		if w.current.Background().IsTransparent() {
 			w.writeEscapedAnsiString(backgroundEnd)
 		} else {
-			w.writeEscapedAnsiString(fmt.Sprintf(colorise, w.currentBackground))
+			w.writeEscapedAnsiString(fmt.Sprintf(colorise, w.current.Background()))
 		}
 	}
 
-	if w.currentForeground != w.foreground {
-		w.writeEscapedAnsiString(fmt.Sprintf(colorise, w.currentForeground))
+	if w.current.Foreground() != w.foreground {
+		w.writeEscapedAnsiString(fmt.Sprintf(colorise, w.current.Foreground()))
+	}
+
+	return position
+}
+
+func (w *Writer) endColorOverride(position int) int {
+	// pop the last colors from the stack
+	defer w.current.Pop()
+
+	// make sure to reset the colors if needed
+	position += len([]rune(resetStyle.AnchorEnd)) - 1
+
+	// reset colors to previous when we have > 2 in stack
+	// - first is always the starting colors used in Write()
+	// - second is the first override
+	// as soon as we have  more than 2, we can pop the last one
+	// and print the previous override as it wasn't ended yet
+	if w.current.Len() >= 3 {
+		fg := w.current.Foreground()
+		bg := w.current.Background()
+
+		w.current.Pop()
+
+		if w.current.Background() != bg {
+			w.writeEscapedAnsiString(fmt.Sprintf(colorise, w.current.Background()))
+		}
+
+		if w.current.Foreground() != fg {
+			w.writeEscapedAnsiString(fmt.Sprintf(colorise, w.current.Foreground()))
+		}
+
+		return position
+	}
+
+	// do not reset when colors are identical
+	if w.current.Background() == w.background && w.current.Foreground() == w.foreground {
+		return position
+	}
+
+	// do not restore colors at the end of the string, we print it anyways
+	if position == len(w.runes)-1 {
+		return position
+	}
+
+	if w.transparent {
+		w.writeEscapedAnsiString(transparentEnd)
+	}
+
+	if w.background.IsClear() {
+		w.writeEscapedAnsiString(backgroundStyle.End)
+	}
+
+	if w.current.Background() != w.background && !w.background.IsClear() {
+		w.writeEscapedAnsiString(fmt.Sprintf(colorise, w.background))
+	}
+
+	if (w.current.Foreground() != w.foreground || w.transparent) && !w.foreground.IsClear() {
+		w.writeEscapedAnsiString(fmt.Sprintf(colorise, w.foreground))
 	}
 
+	w.transparent = false
 	return position
 }
 
diff --git a/src/ansi/ansi_writer_hyperlink.go b/src/ansi/ansi_writer_hyperlink.go
index 3846678b..7a27c166 100644
--- a/src/ansi/ansi_writer_hyperlink.go
+++ b/src/ansi/ansi_writer_hyperlink.go
@@ -76,8 +76,9 @@ func (w *Writer) replaceHyperlink(text string) string {
 		return strings.Replace(text, results["ALL"], linkText, 1)
 	}
 
-	// we only care about the length of the text part
-	w.length += runewidth.StringWidth(linkText)
+	// we only care about the length of the actual text part
+	characters := w.trimAnsi(linkText)
+	w.length += runewidth.StringWidth(characters)
 
 	if w.Plain {
 		return linkText
diff --git a/src/ansi/colors.go b/src/ansi/colors.go
index fa95b45a..6c5590ce 100644
--- a/src/ansi/colors.go
+++ b/src/ansi/colors.go
@@ -19,6 +19,61 @@ type ColorString interface {
 	ToColor(colorString string, isBackground bool, trueColor bool) Color
 }
 
+type ColorSet struct {
+	Foreground Color
+	Background Color
+}
+
+type ColorHistory []*ColorSet
+
+func (c *ColorHistory) Len() int {
+	return len(*c)
+}
+
+func (c *ColorHistory) Add(background, foreground Color) {
+	colors := &ColorSet{
+		Foreground: foreground,
+		Background: background,
+	}
+
+	if c.Len() == 0 {
+		*c = append(*c, colors)
+		return
+	}
+
+	last := (*c)[c.Len()-1]
+	// never add the same colors twice
+	if last.Foreground == colors.Foreground && last.Background == colors.Background {
+		return
+	}
+
+	*c = append(*c, colors)
+}
+
+func (c *ColorHistory) Pop() {
+	if c.Len() == 0 {
+		return
+	}
+
+	*c = (*c)[:c.Len()-1]
+}
+
+func (c *ColorHistory) Background() Color {
+	if c.Len() == 0 {
+		return emptyColor
+	}
+
+	return (*c)[c.Len()-1].Background
+}
+
+func (c *ColorHistory) Foreground() Color {
+	if c.Len() == 0 {
+		return emptyColor
+	}
+
+	return (*c)[c.Len()-1].Foreground
+}
+
 // Color is an ANSI color code ready to be printed to the console.
 // Example: "38;2;255;255;255", "48;2;255;255;255", "31", "95".
 type Color string
diff --git a/src/engine/engine.go b/src/engine/engine.go
index 3a524a65..73d93f61 100644
--- a/src/engine/engine.go
+++ b/src/engine/engine.go
@@ -91,6 +91,12 @@ func (e *Engine) pwd() {
 	}
 
 	cwd := e.Env.Pwd()
+
+	// in BASH, we need to escape the path
+	if e.Env.Shell() == shell.BASH {
+		cwd = strings.ReplaceAll(cwd, `\`, `\\`)
+	}
+
 	// Backwards compatibility for deprecated OSC99
 	if e.Config.OSC99 {
 		e.write(e.Writer.ConsolePwd(ansi.OSC99, "", "", cwd))
@@ -158,9 +164,22 @@ func (e *Engine) getTitleTemplateText() string {
 	return ""
 }
 
-func (e *Engine) renderBlock(block *Block, cancelNewline bool) {
+func (e *Engine) renderBlock(block *Block, cancelNewline bool) bool {
 	defer e.patchPowerShellBleed()
 
+	// This is deprecated but we leave it in to not break configs
+	// It is encouraged to used "newline": true on block level
+	// rather than the standalone the linebreak block
+	if block.Type == LineBreak {
+		// do not print a newline to avoid a leading space
+		// when we're printin the first primary prompt in
+		// the shell
+		if !cancelNewline {
+			e.newline()
+		}
+		return false
+	}
+
 	// when in bash, for rprompt blocks we need to write plain
 	// and wrap in escaped mode or the prompt will not render correctly
 	if e.Env.Shell() == shell.BASH && block.Type == RPrompt {
@@ -170,7 +189,7 @@ func (e *Engine) renderBlock(block *Block, cancelNewline bool) {
 	}
 
 	if !block.Enabled() {
-		return
+		return false
 	}
 
 	// do not print a newline to avoid a leading space
@@ -180,36 +199,29 @@ func (e *Engine) renderBlock(block *Block, cancelNewline bool) {
 		e.newline()
 	}
 
-	switch block.Type {
-	// This is deprecated but we leave it in to not break configs
-	// It is encouraged to used "newline": true on block level
-	// rather than the standalone the linebreak block
-	case LineBreak:
-		// do not print a newline to avoid a leading space
-		// when we're printin the first primary prompt in
-		// the shell
-		if !cancelNewline {
-			return
-		}
-		e.newline()
+	text, length := block.RenderSegments()
+
+	// do not print anything when we don't have any text
+	if length == 0 {
+		return false
+	}
+
+	switch block.Type { //nolint:exhaustive
 	case Prompt:
 		if block.VerticalOffset != 0 {
 			e.write(e.Writer.ChangeLine(block.VerticalOffset))
 		}
 
 		if block.Alignment == Left {
-			text, length := block.RenderSegments()
 			e.currentLineLength += length
 			e.write(text)
-			return
+			return true
 		}
 
 		if block.Alignment != Right {
-			return
+			return false
 		}
 
-		text, length := block.RenderSegments()
-
 		space, OK := e.canWriteRightBlock(false)
 		// we can't print the right block as there's not enough room available
 		if !OK {
@@ -222,7 +234,7 @@ func (e *Engine) renderBlock(block *Block, cancelNewline bool) {
 					e.write(padText)
 				}
 				e.currentLineLength = 0
-				return
+				return true
 			}
 		}
 
@@ -234,7 +246,7 @@ func (e *Engine) renderBlock(block *Block, cancelNewline bool) {
 		if padText, OK := e.shouldFill(block.Filler, space, length); OK {
 			e.write(padText)
 			e.write(text)
-			return
+			return true
 		}
 
 		var prompt string
@@ -247,8 +259,11 @@ func (e *Engine) renderBlock(block *Block, cancelNewline bool) {
 		prompt += text
 		e.write(prompt)
 	case RPrompt:
-		e.rprompt, e.rpromptLength = block.RenderSegments()
+		e.rprompt = text
+		e.rpromptLength = length
 	}
+
+	return true
 }
 
 func (e *Engine) patchPowerShellBleed() {
diff --git a/src/engine/prompt.go b/src/engine/prompt.go
index 9f9a6308..8b0c1e5f 100644
--- a/src/engine/prompt.go
+++ b/src/engine/prompt.go
@@ -28,13 +28,23 @@ func (e *Engine) Primary() string {
 
 	// cache a pointer to the color cycle
 	cycle = &e.Config.Cycle
+	var cancelNewline, didRender bool
+
 	for i, block := range e.Config.Blocks {
-		var cancelNewline bool
+		// do not print a leading newline when we're at the first row and the prompt is cleared
 		if i == 0 {
 			row, _ := e.Env.CursorPosition()
 			cancelNewline = e.Env.Flags().Cleared || e.Env.Flags().PromptCount == 1 || row == 1
 		}
-		e.renderBlock(block, cancelNewline)
+
+		// skip setting a newline when we didn't print anything yet
+		if i != 0 {
+			cancelNewline = !didRender
+		}
+
+		if e.renderBlock(block, cancelNewline) {
+			didRender = true
+		}
 	}
 
 	if len(e.Config.ConsoleTitleTemplate) > 0 && !e.Env.Flags().Plain {
diff --git a/src/engine/segment.go b/src/engine/segment.go
index 43cb83f5..26f76ef2 100644
--- a/src/engine/segment.go
+++ b/src/engine/segment.go
@@ -150,6 +150,8 @@ const (
 	GOLANG SegmentType = "go"
 	// HASKELL segment
 	HASKELL SegmentType = "haskell"
+	// HELM segment
+	HELM SegmentType = "helm"
 	// IPIFY segment
 	IPIFY SegmentType = "ipify"
 	// ITERM inserts the Shell Integration prompt mark on iTerm zsh/bash/fish
@@ -280,6 +282,7 @@ var Segments = map[SegmentType]func() SegmentWriter{
 	GITVERSION:    func() SegmentWriter { return &segments.GitVersion{} },
 	GOLANG:        func() SegmentWriter { return &segments.Golang{} },
 	HASKELL:       func() SegmentWriter { return &segments.Haskell{} },
+	HELM:          func() SegmentWriter { return &segments.Helm{} },
 	IPIFY:         func() SegmentWriter { return &segments.IPify{} },
 	ITERM:         func() SegmentWriter { return &segments.ITerm{} },
 	JAVA:          func() SegmentWriter { return &segments.Java{} },
diff --git a/src/font/cli.go b/src/font/cli.go
index 18e1a683..72e84722 100644
--- a/src/font/cli.go
+++ b/src/font/cli.go
@@ -29,7 +29,7 @@ type loadMsg []*Asset
 
 type zipMsg []byte
 
-type successMsg int
+type successMsg []string
 
 type errMsg error
 
@@ -69,12 +69,13 @@ const (
 )
 
 type main struct {
-	spinner spinner.Model
-	list    *list.Model
-	system  bool
-	font    string
-	state   state
-	err     error
+	spinner  spinner.Model
+	list     *list.Model
+	system   bool
+	font     string
+	state    state
+	err      error
+	families []string
 }
 
 func (m *main) buildFontList(nerdFonts []*Asset) {
@@ -124,12 +125,12 @@ func installLocalFontZIP(zipFile string, user bool) {
 }
 
 func installFontZIP(zipFile []byte, user bool) {
-	err := InstallZIP(zipFile, user)
+	families, err := InstallZIP(zipFile, user)
 	if err != nil {
 		program.Send(errMsg(err))
 		return
 	}
-	program.Send(successMsg(0))
+	program.Send(successMsg(families))
 }
 
 func (m *main) Init() tea.Cmd {
@@ -216,6 +217,7 @@ func (m *main) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
 
 	case successMsg:
 		m.state = done
+		m.families = msg
 		return m, tea.Quit
 
 	case errMsg:
@@ -251,7 +253,13 @@ func (m *main) View() string {
 	case quit:
 		return textStyle.Render("No need to install a new font? That's cool.")
 	case done:
-		return textStyle.Render(fmt.Sprintf("Successfully installed %s 🚀", m.font))
+		var builder strings.Builder
+		builder.WriteString(fmt.Sprintf("Successfully installed %s 🚀\n\n", m.font))
+		builder.WriteString("The following font families are now available for configuration:\n")
+		for _, family := range m.families {
+			builder.WriteString(fmt.Sprintf("  • %s\n", family))
+		}
+		return textStyle.Render(builder.String())
 	}
 	return ""
 }
diff --git a/src/font/install.go b/src/font/install.go
index a2482fbe..c49ac7f9 100644
--- a/src/font/install.go
+++ b/src/font/install.go
@@ -10,12 +10,22 @@ import (
 	"strings"
 )
 
-func InstallZIP(data []byte, user bool) error {
+func contains[S ~[]E, E comparable](s S, e E) bool {
+	for _, a := range s {
+		if a == e {
+			return true
+		}
+	}
+	return false
+}
+
+func InstallZIP(data []byte, user bool) ([]string, error) {
+	var families []string
 	bytesReader := bytes.NewReader(data)
 
 	zipReader, err := zip.NewReader(bytesReader, int64(bytesReader.Len()))
 	if err != nil {
-		return err
+		return families, err
 	}
 
 	fonts := make(map[string]*Font)
@@ -23,13 +33,13 @@ func InstallZIP(data []byte, user bool) error {
 	for _, zf := range zipReader.File {
 		rc, err := zf.Open()
 		if err != nil {
-			return err
+			return families, err
 		}
 		defer rc.Close()
 
 		data, err := io.ReadAll(rc)
 		if err != nil {
-			return err
+			return families, err
 		}
 
 		fontData, err := newFont(zf.Name, data)
@@ -51,9 +61,13 @@ func InstallZIP(data []byte, user bool) error {
 
 	for _, font := range fonts {
 		if err = install(font, user); err != nil {
-			return err
+			return families, err
+		}
+
+		if !contains(families, font.Family) {
+			families = append(families, font.Family)
 		}
 	}
 
-	return nil
+	return families, nil
 }
diff --git a/src/go.mod b/src/go.mod
index 18d847d5..f0914eb9 100644
--- a/src/go.mod
+++ b/src/go.mod
@@ -1,6 +1,6 @@
 module github.com/jandedobbeleer/oh-my-posh/src
 
-go 1.20
+go 1.21
 
 require (
 	github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161
diff --git a/src/go.sum b/src/go.sum
index 1f718b20..bbabc2c9 100644
--- a/src/go.sum
+++ b/src/go.sum
@@ -47,12 +47,17 @@ github.com/fatih/color v1.15.0/go.mod h1:0h5ZqXfHYED7Bhv2ZJamyIOUej9KtShiJESRwBD
 github.com/fogleman/gg v1.3.0 h1:/7zJX8F6AaYQc57WQCyN9cAIz+4bCJGO9B+dyW29am8=
 github.com/fogleman/gg v1.3.0/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k=
 github.com/frankban/quicktest v1.14.3 h1:FJKSZTDHjyhriyC81FLQ0LY93eSai0ZyR/ZIkd3ZUKE=
+github.com/frankban/quicktest v1.14.3/go.mod h1:mgiwOwqx65TmIk1wJ6Q7wvnVMocbUorkibMOrVTHZps=
 github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY=
 github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
 github.com/go-playground/locales v0.13.0 h1:HyWk6mgj5qFqCT5fjGBuRArbVDfE4hi8+e8ceBS/t7Q=
+github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8=
 github.com/go-playground/universal-translator v0.17.0 h1:icxd5fm+REJzpZx7ZfpaD876Lmtgy7VtROAbHHXk8no=
+github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA=
 github.com/go-playground/validator/v10 v10.4.1 h1:pH2c5ADXtd66mxoE0Zm9SUhxE20r7aM3F26W0hOn+GE=
+github.com/go-playground/validator/v10 v10.4.1/go.mod h1:nlOn6nFhuKACm19sB/8EGNn9GlaMV7XkbRSipzJ0Ii4=
 github.com/go-test/deep v1.0.3 h1:ZrJSEWsXzPOxaZnFteGEfooLba+ju3FYIbOrS+rQd68=
+github.com/go-test/deep v1.0.3/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA=
 github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU=
 github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
 github.com/goccy/go-yaml v1.11.0 h1:n7Z+zx8S9f9KgzG6KtQKf+kwqXZlLNR2F6018Dgau54=
@@ -72,6 +77,7 @@ github.com/gookit/config/v2 v2.2.3/go.mod h1:FhmMu+2wg0UhyOjVGo+DZ1+ov34q4G4aWXz
 github.com/gookit/goutil v0.6.10 h1:iq7CXOf+fYLvrVAh3+ZoLgufGfK65TwbzE8NpnPGtyk=
 github.com/gookit/goutil v0.6.10/go.mod h1:qqrPoX+Pm6YmxqqccgkNLPirTFX7UYMES1SK+fokqQU=
 github.com/gookit/ini/v2 v2.2.2 h1:3B8abZJrVH1vi/7TU4STuTBxdhiAq1ORSt6NJZCahaI=
+github.com/gookit/ini/v2 v2.2.2/go.mod h1:wGEfnBxv+7nVXytWM44tiqczv5hLKJ+m9MaA2uJg3iM=
 github.com/hashicorp/hcl/v2 v2.17.0 h1:z1XvSUyXd1HP10U4lrLg5e0JMVz6CPaJvAgxM0KNZVY=
 github.com/hashicorp/hcl/v2 v2.17.0/go.mod h1:gJyW2PTShkJqQBKpAmPO3yxMxIuoXkOF2TpqXzrQyx4=
 github.com/huandu/xstrings v1.3.3/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE=
@@ -88,11 +94,15 @@ github.com/klauspost/compress v1.4.1/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0
 github.com/klauspost/cpuid v1.2.0/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek=
 github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
 github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0=
+github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk=
 github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
 github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
 github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
+github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
 github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
+github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
 github.com/leodido/go-urn v1.2.0 h1:hpXL4XnriNwQ/ABnpepYM/1vCLWNDfUNts8dX3xTG6Y=
+github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII=
 github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY=
 github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
 github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I=
@@ -136,6 +146,7 @@ github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJ
 github.com/rivo/uniseg v0.4.4 h1:8TfxU8dW6PdqD27gjM8MVNuicgxIjxpm4K7x4jp8sis=
 github.com/rivo/uniseg v0.4.4/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
 github.com/rogpeppe/go-internal v1.6.1 h1:/FiVV8dS/e+YqF2JvO3yXRFbBLTIuSDkuC7aBOAvL+k=
+github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
 github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
 github.com/sahilm/fuzzy v0.1.0 h1:FzWGaw2Opqyu+794ZQ9SYifWv2EIXpwP4q8dY1kDAwI=
 github.com/sahilm/fuzzy v0.1.0/go.mod h1:VFvziUEIMCrT6A6tw2RFIXPXXmzXbOsSHF0DOI8ZK9Y=
@@ -190,6 +201,7 @@ golang.org/x/crypto v0.3.0/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4
 golang.org/x/crypto v0.8.0 h1:pd9TJtTueMTVQXzk8E2XESSMQDj/U7OUu0PqJqPXQjQ=
 golang.org/x/crypto v0.8.0/go.mod h1:mRqEX+O9/h5TFCrQhkgjo2yKi0yYA+9ecGkdQoHrywE=
 golang.org/x/exp v0.0.0-20220909182711-5c715a9e8561 h1:MDc5xs78ZrZr3HMQugiXOAkSZtfTpbJLDr/lwfgO53E=
+golang.org/x/exp v0.0.0-20220909182711-5c715a9e8561/go.mod h1:cyybsKvd6eL0RnXn6p/Grxp8F5bW7iYuBgsNCOHpMYE=
 golang.org/x/image v0.11.0 h1:ds2RoQvBvYTiJkwpSFDwCcDFNX7DqjL2WsUgTNk0Ooo=
 golang.org/x/image v0.11.0/go.mod h1:bglhjqbqVuEb9e9+eNR45Jfu7D+T4Qan+NhQk8Ck2P8=
 golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
diff --git a/src/platform/battery/battery_openbsd.go b/src/platform/battery/battery_openbsd.go
new file mode 100644
index 00000000..9c06d1ce
--- /dev/null
+++ b/src/platform/battery/battery_openbsd.go
@@ -0,0 +1,57 @@
+package battery
+
+import (
+	"errors"
+	"strconv"
+	"strings"
+
+	"github.com/jandedobbeleer/oh-my-posh/src/platform/cmd"
+)
+
+// See https://man.openbsd.org/man8/apm.8
+func mapMostLogicalState(state string) State {
+	switch state {
+	case "3":
+		return Charging
+	case "0", "1":
+		return Discharging
+	case "2":
+		return Empty
+	default:
+		return Unknown
+	}
+}
+
+func parseBatteryOutput(apm_percentage string, apm_status string) (*Info, error) {
+	percentage, err := strconv.Atoi(strings.TrimSpace(apm_percentage))
+	if err != nil {
+		return nil, errors.New("Unable to parse battery percentage")
+	}
+
+	if percentage == 100 {
+		return &Info{
+			Percentage: percentage,
+			State:      Full,
+		}, nil
+	}
+
+	return &Info{
+		Percentage: percentage,
+		State:      mapMostLogicalState(apm_status),
+	}, nil
+}
+
+func Get() (*Info, error) {
+	apm_percentage, err := cmd.Run("apm", "-l")
+	if err != nil {
+		return nil, err
+	}
+
+	apm_status, err := cmd.Run("apm", "-b")
+	if err != nil {
+		return nil, err
+	}  
+
+  return parseBatteryOutput(apm_percentage, apm_status)
+
+}
diff --git a/src/platform/battery/battery_windows_nix.go b/src/platform/battery/battery_windows_nix.go
index 5e518726..cdeef62f 100644
--- a/src/platform/battery/battery_windows_nix.go
+++ b/src/platform/battery/battery_windows_nix.go
@@ -1,4 +1,4 @@
-//go:build !darwin && !netbsd
+//go:build !darwin && !netbsd && !openbsd
 
 package battery
 
diff --git a/src/platform/shell.go b/src/platform/shell.go
index a18dec43..72dca15e 100644
--- a/src/platform/shell.go
+++ b/src/platform/shell.go
@@ -3,6 +3,7 @@ package platform
 import (
 	"bytes"
 	"context"
+	"encoding/base64"
 	"encoding/json"
 	"errors"
 	"fmt"
@@ -345,7 +346,9 @@ func (env *Shell) resolveConfigPath() {
 func (env *Shell) downloadConfig(location string) error {
 	defer env.Trace(time.Now(), location)
 	ext := filepath.Ext(location)
-	configPath := filepath.Join(env.CachePath(), "config.omp"+ext)
+	fileHash := base64.StdEncoding.EncodeToString([]byte(location))
+	filename := fmt.Sprintf("config.%s.omp%s", fileHash, ext)
+	configPath := filepath.Join(env.CachePath(), filename)
 	cfg, err := env.HTTPRequest(location, nil, 5000)
 	if err != nil {
 		return err
diff --git a/src/segments/git.go b/src/segments/git.go
index f99b9d36..7450dc34 100644
--- a/src/segments/git.go
+++ b/src/segments/git.go
@@ -44,7 +44,7 @@ func (s *GitStatus) add(code string) {
 		s.Added++
 	case "?":
 		s.Untracked++
-	case "U":
+	case "U", "AA":
 		s.Unmerged++
 	case "M", "R", "C", "m":
 		s.Modified++
@@ -93,12 +93,14 @@ const (
 	MergeIcon properties.Property = "merge_icon"
 	// UpstreamIcons allows to add custom upstream icons
 	UpstreamIcons properties.Property = "upstream_icons"
-	// GithubIcon shows√ when upstream is github
+	// GithubIcon shows when upstream is github
 	GithubIcon properties.Property = "github_icon"
 	// BitbucketIcon shows  when upstream is bitbucket
 	BitbucketIcon properties.Property = "bitbucket_icon"
 	// AzureDevOpsIcon shows  when upstream is azure devops
 	AzureDevOpsIcon properties.Property = "azure_devops_icon"
+	// CodeCommit shows  when upstream is aws codecommit
+	CodeCommit properties.Property = "codecommit_icon"
 	// GitlabIcon shows when upstream is gitlab
 	GitlabIcon properties.Property = "gitlab_icon"
 	// GitIcon shows when the upstream can't be identified
@@ -133,6 +135,11 @@ type Git struct {
 	IsWorkTree     bool
 	IsBare         bool
 	User           *User
+	Detached       bool
+	Merge          bool
+	Rebase         bool
+	CherryPick     bool
+	Revert         bool
 
 	// needed for posh-git support
 	poshgit       bool
@@ -242,6 +249,10 @@ func (g *Git) StashCount() int {
 
 func (g *Git) Kraken() string {
 	root := g.getGitCommandOutput("rev-list", "--max-parents=0", "HEAD")
+	if strings.Contains(root, "\n") {
+		root = strings.Split(root, "\n")[0]
+	}
+
 	if len(g.RawUpstreamURL) == 0 {
 		if len(g.Upstream) == 0 {
 			g.Upstream = "origin"
@@ -425,6 +436,11 @@ func (g *Git) cleanUpstreamURL(url string) string {
 		path = strings.TrimSuffix(path, ".git")
 		return fmt.Sprintf("https://%s/%s", match["URL"], path)
 	}
+	// codecommit::region-identifier-id://repo-name
+	match = regex.FindNamedRegexMatch(`codecommit::(?P<URL>[a-z0-9-]+)://(?P<PATH>[\w\.@\:/\-~]+)`, url)
+	if len(match) != 0 {
+		return fmt.Sprintf("https://%s.console.aws.amazon.com/codesuite/codecommit/repositories/%s/browse?region=%s", match["URL"], match["PATH"], match["URL"])
+	}
 	// user@host.xz:/path/to/repo.git
 	match = regex.FindNamedRegexMatch(`.*@(?P<URL>.*):(?P<PATH>.*).git`, url)
 	if len(match) == 0 {
@@ -457,6 +473,7 @@ func (g *Git) getUpstreamIcon() string {
 		"bitbucket":        {BitbucketIcon, "\uF171 "},
 		"dev.azure.com":    {AzureDevOpsIcon, "\uEBE8 "},
 		"visualstudio.com": {AzureDevOpsIcon, "\uEBE8 "},
+		"codecommit":       {CodeCommit, "\uF270 "},
 	}
 	for key, value := range defaults {
 		if strings.Contains(g.UpstreamURL, key) {
@@ -476,6 +493,17 @@ func (g *Git) setGitStatus() {
 		if len(status) <= 4 {
 			return
 		}
+
+		// map conflicts separately when in a merge or rebase
+		if g.Rebase || g.Merge {
+			conflict := "AA"
+			full := status[2:4]
+			if full == conflict {
+				g.Staging.add(conflict)
+				return
+			}
+		}
+
 		workingCode := status[3:4]
 		stagingCode := status[2:3]
 		g.Working.add(workingCode)
@@ -543,6 +571,7 @@ func (g *Git) getGitCommandOutput(args ...string) string {
 func (g *Git) setGitHEADContext() {
 	branchIcon := g.props.GetString(BranchIcon, "\uE0A0")
 	if g.Ref == DETACHED {
+		g.Detached = true
 		g.setPrettyHEADName()
 	} else {
 		head := g.formatHEAD(g.Ref)
@@ -569,6 +598,7 @@ func (g *Git) setGitHEADContext() {
 	}
 
 	if g.env.HasFolder(g.workingDir + "/rebase-merge") {
+		g.Rebase = true
 		origin := getPrettyNameOrigin("rebase-merge/head-name")
 		onto := g.getGitRefFileSymbolicName("rebase-merge/onto")
 		onto = g.formatHEAD(onto)
@@ -578,7 +608,9 @@ func (g *Git) setGitHEADContext() {
 		g.HEAD = fmt.Sprintf("%s%s onto %s%s (%s/%s) at %s", icon, origin, branchIcon, onto, step, total, g.HEAD)
 		return
 	}
+
 	if g.env.HasFolder(g.workingDir + "/rebase-apply") {
+		g.Rebase = true
 		origin := getPrettyNameOrigin("rebase-apply/head-name")
 		step := g.FileContents(g.workingDir, "rebase-apply/next")
 		total := g.FileContents(g.workingDir, "rebase-apply/last")
@@ -586,9 +618,12 @@ func (g *Git) setGitHEADContext() {
 		g.HEAD = fmt.Sprintf("%s%s (%s/%s) at %s", icon, origin, step, total, g.HEAD)
 		return
 	}
+
 	// merge
 	commitIcon := g.props.GetString(CommitIcon, "\uF417")
+
 	if g.hasGitFile("MERGE_MSG") {
+		g.Merge = true
 		icon := g.props.GetString(MergeIcon, "\uE727 ")
 		mergeContext := g.FileContents(g.workingDir, "MERGE_MSG")
 		matches := regex.FindNamedRegexMatch(`Merge (remote-tracking )?(?P<type>branch|commit|tag) '(?P<theirs>.*)'`, mergeContext)
@@ -610,23 +645,28 @@ func (g *Git) setGitHEADContext() {
 			return
 		}
 	}
+
 	// sequencer status
 	// see if a cherry-pick or revert is in progress, if the user has committed a
 	// conflict resolution with 'git commit' in the middle of a sequence of picks or
 	// reverts then CHERRY_PICK_HEAD/REVERT_HEAD will not exist so we have to read
 	// the todo file.
 	if g.hasGitFile("CHERRY_PICK_HEAD") {
+		g.CherryPick = true
 		sha := g.FileContents(g.workingDir, "CHERRY_PICK_HEAD")
 		cherry := g.props.GetString(CherryPickIcon, "\uE29B ")
 		g.HEAD = fmt.Sprintf("%s%s%s onto %s", cherry, commitIcon, g.formatSHA(sha), formatDetached())
 		return
 	}
+
 	if g.hasGitFile("REVERT_HEAD") {
+		g.Revert = true
 		sha := g.FileContents(g.workingDir, "REVERT_HEAD")
 		revert := g.props.GetString(RevertIcon, "\uF0E2 ")
 		g.HEAD = fmt.Sprintf("%s%s%s onto %s", revert, commitIcon, g.formatSHA(sha), formatDetached())
 		return
 	}
+
 	if g.hasGitFile("sequencer/todo") {
 		todo := g.FileContents(g.workingDir, "sequencer/todo")
 		matches := regex.FindNamedRegexMatch(`^(?P<action>p|pick|revert)\s+(?P<sha>\S+)`, todo)
@@ -635,16 +675,19 @@ func (g *Git) setGitHEADContext() {
 			sha := matches["sha"]
 			switch action {
 			case "p", "pick":
+				g.CherryPick = true
 				cherry := g.props.GetString(CherryPickIcon, "\uE29B ")
 				g.HEAD = fmt.Sprintf("%s%s%s onto %s", cherry, commitIcon, g.formatSHA(sha), formatDetached())
 				return
 			case "revert":
+				g.Revert = true
 				revert := g.props.GetString(RevertIcon, "\uF0E2 ")
 				g.HEAD = fmt.Sprintf("%s%s%s onto %s", revert, commitIcon, g.formatSHA(sha), formatDetached())
 				return
 			}
 		}
 	}
+
 	g.HEAD = formatDetached()
 }
 
diff --git a/src/segments/helm.go b/src/segments/helm.go
new file mode 100644
index 00000000..9df1e745
--- /dev/null
+++ b/src/segments/helm.go
@@ -0,0 +1,55 @@
+package segments
+
+import (
+	"github.com/jandedobbeleer/oh-my-posh/src/platform"
+	"github.com/jandedobbeleer/oh-my-posh/src/properties"
+)
+
+type Helm struct {
+	props properties.Properties
+	env   platform.Environment
+
+	Version string
+}
+
+func (h *Helm) Enabled() bool {
+	displayMode := h.props.GetString(DisplayMode, DisplayModeAlways)
+	if displayMode != DisplayModeFiles {
+		return h.getVersion()
+	}
+
+	inChart := false
+	files := []string{"Chart.yml", "Chart.yaml"}
+	for _, file := range files {
+		if _, err := h.env.HasParentFilePath(file); err == nil {
+			inChart = true
+			break
+		}
+	}
+
+	return inChart && h.getVersion()
+}
+
+func (h *Helm) Template() string {
+	return " Helm {{.Version}}"
+}
+
+func (h *Helm) Init(props properties.Properties, env platform.Environment) {
+	h.props = props
+	h.env = env
+}
+
+func (h *Helm) getVersion() bool {
+	cmd := "helm"
+	if !h.env.HasCommand(cmd) {
+		return false
+	}
+
+	result, err := h.env.RunCommand(cmd, "version", "--short", "--template={{.Version}}")
+	if err != nil {
+		return false
+	}
+
+	h.Version = result[1:]
+	return true
+}
diff --git a/src/segments/project.go b/src/segments/project.go
index c8241fba..3afdd614 100644
--- a/src/segments/project.go
+++ b/src/segments/project.go
@@ -237,13 +237,18 @@ func (n *Project) getPowerShellModuleData(_ ProjectItem) *ProjectData {
 	files := n.env.LsDir(n.env.Pwd())
 	var content string
 	// get the first match only
+	// excluding PSScriptAnalyzerSettings.psd1
 	for _, file := range files {
-		if filepath.Ext(file.Name()) == ".psd1" {
+		if filepath.Ext(file.Name()) == ".psd1" && file.Name() != "PSScriptAnalyzerSettings.psd1" {
 			content = n.env.FileContent(file.Name())
 			break
 		}
 	}
 
+	if len(content) == 0 {
+		return nil
+	}
+
 	data := &ProjectData{}
 	lines := strings.Split(content, "\n")
 
diff --git a/src/template/compare.go b/src/template/compare.go
index 5a69a112..52462f01 100644
--- a/src/template/compare.go
+++ b/src/template/compare.go
@@ -30,6 +30,9 @@ func gt(e1, e2 interface{}) bool {
 	if val, OK := e1.(int); OK {
 		return val > interFaceToInt(e2)
 	}
+	if val, OK := e1.(int64); OK {
+		return val > int64(interFaceToInt(e2))
+	}
 	if val, OK := e1.(float64); OK {
 		return val > interfaceToFloat64(e2)
 	}
diff --git a/themes/M365Princess.omp.json b/themes/M365Princess.omp.json
index 4d1368e4..12bb380c 100644
--- a/themes/M365Princess.omp.json
+++ b/themes/M365Princess.omp.json
@@ -1,5 +1,15 @@
 {
   "$schema": "https://raw.githubusercontent.com/JanDeDobbeleer/oh-my-posh/main/themes/schema.json",
+  "palette": {
+    "white": "#FFFFFF",
+    "tan": "#CC3802",
+    "teal": "#047E84",
+    "plum": "#9A348E",
+    "blush": "#DA627D",
+    "salmon": "#FCA17D",
+    "sky": "#86BBD8",
+    "teal_blue": "#33658A"
+  },
   "blocks": [
     {
       "alignment": "left",
@@ -8,15 +18,15 @@
           "type": "text",
           "style": "diamond",
           "leading_diamond": "\ue0b6",
-          "foreground": "#ffffff",
-          "background": "#cc3802",
+          "foreground": "p:white",
+          "background": "p:tan",
           "template": "{{ if .Env.PNPPSHOST }} \uf8c5 {{ .Env.PNPPSHOST }} {{ end }}"
         },
         {
           "type": "text",
           "style": "powerline",
-          "foreground": "#ffffff",
-          "background": "#047e84",
+          "foreground": "p:white",
+          "background": "p:teal",
           "powerline_symbol": "\ue0b0",
           "template": "{{ if .Env.PNPPSSITE }} \uf2dd {{ .Env.PNPPSSITE }}{{ end }}"
         },
@@ -24,8 +34,8 @@
           "type": "text",
           "style": "diamond",
           "trailing_diamond": "\ue0b4",
-          "foreground": "#ffffff",
-          "background": "#047e84",
+          "foreground": "p:white",
+          "background": "p:teal",
           "template": "{{ if .Env.PNPPSSITE }}\u00A0{{ end }}"
         }
       ],
@@ -35,16 +45,16 @@
       "alignment": "left",
       "segments": [
         {
-          "background": "#9A348E",
-          "foreground": "#ffffff",
+          "background": "p:plum",
+          "foreground": "p:white",
           "leading_diamond": "\ue0b6",
           "style": "diamond",
           "template": "{{ .UserName }} ",
           "type": "session"
         },
         {
-          "background": "#DA627D",
-          "foreground": "#ffffff",
+          "background": "p:blush",
+          "foreground": "p:white",
           "powerline_symbol": "\ue0b0",
           "properties": {
             "style": "folder"
@@ -54,8 +64,8 @@
           "type": "path"
         },
         {
-          "background": "#FCA17D",
-          "foreground": "#ffffff",
+          "background": "p:salmon",
+          "foreground": "p:white",
           "powerline_symbol": "\ue0b0",
           "properties": {
             "branch_icon": "",
@@ -68,16 +78,16 @@
           "type": "git"
         },
         {
-          "background": "#86BBD8",
-          "foreground": "#ffffff",
+          "background": "p:sky",
+          "foreground": "p:white",
           "powerline_symbol": "\ue0b0",
           "style": "powerline",
           "template": " \ue718 {{ if .PackageManagerIcon }}{{ .PackageManagerIcon }} {{ end }}{{ .Full }} ",
           "type": "node"
         },
         {
-          "background": "#33658A",
-          "foreground": "#ffffff",
+          "background": "p:teal_blue",
+          "foreground": "p:white",
           "properties": {
             "time_format": "15:04"
           },
diff --git a/themes/devious-diamonds.omp.yaml b/themes/devious-diamonds.omp.yaml
index c3b8268f..ffda11a8 100644
--- a/themes/devious-diamonds.omp.yaml
+++ b/themes/devious-diamonds.omp.yaml
@@ -11,7 +11,6 @@ blocks:
         template: "{{ .PromptMark }}"
       - type: os
         style: diamond
-        trailing_diamond: <transparent,></>
         foreground: cyan
         properties:
           alpine: 
@@ -67,7 +66,7 @@ blocks:
         foreground: black
         properties:
           display_default: false
-        template: " ﴃ Subscription {{ .Name }} ({{ if .EnvironmentName | contains 'AzureCloud' }}{{ 'Global' }}{{ else }}{{ .EnvironmentName }}{{ end }}) "
+        template: " ﴃ Subscription {{ .Name }} ({{ if .EnvironmentName | contains \"AzureCloud\" }}{{ \"Global\" }}{{ else }}{{ .EnvironmentName }}{{ end }}) "
       - type: azfunc
         style: powerline
         powerline_symbol: 
@@ -84,13 +83,13 @@ blocks:
         foreground: black
         properties:
           fetch_version: true
-        template: "  {{ if .Error }}{{ .Error }}{{ else }}{{ .Full }}{{ end }} "
+        template: " ﳑ {{ if .Error }}{{ .Error }}{{ else }}{{ .Full }}{{ end }} "
       - type: java
         style: powerline
         powerline_symbol: 
         background: lightCyan
         foreground: black
-        template: "  {{ .Full }}"
+        template: "  {{ if .Error }}{{ .Error }}{{ else }}{{ .Full }}{{ end }} "
       - type: julia
         style: powerline
         powerline_symbol: 
@@ -118,7 +117,7 @@ blocks:
         powerline_symbol: 
         background: lightCyan
         foreground: black
-        template: "  {{ .Full }} "
+        template: "  {{ if .PackageManagerIcon }}{{ .PackageManagerIcon }} {{ end }}{{ .Full }} "
       - type: project
         style: powerline
         powerline_symbol: 
@@ -143,6 +142,24 @@ blocks:
           display_mode: files
           fetch_version: true
         template: "  {{ if .Error }}{{ .Error }}{{ else }}{{ .Full }}{{ end }} "
+      - type: rust
+        style: powerline
+        powerline_symbol: 
+        background: lightRed
+        foreground: black
+        properties:
+          display_mode: files
+          fetch_version: true
+        template: "  {{ if .Error }}{{ .Error }}{{ else }}{{ .Full }}{{ end }} "
+      - type: swift
+        style: powerline
+        powerline_symbol: 
+        background: blue
+        foreground: black
+        properties:
+          display_mode: files
+          fetch_version: true
+        template: "  {{ if .Error }}{{ .Error }}{{ else }}{{ .Full }}{{ end }} "
       - type: time
         style: powerline
         powerline_symbol: 
@@ -184,7 +201,7 @@ blocks:
         style: plain
         foreground: cyan
         template: " ╚"
-      - type: exit
+      - type: status
         style: diamond
         leading_diamond: 
         background: blue
diff --git a/themes/schema.json b/themes/schema.json
index 735ea4ee..3b70bbb3 100644
--- a/themes/schema.json
+++ b/themes/schema.json
@@ -272,6 +272,7 @@
             "gitversion",
             "go",
             "haskell",
+            "helm",
             "ipify",
             "iterm",
             "julia",
@@ -982,6 +983,12 @@
                     "description": "Icon/text to display when the upstream is Azure DevOps",
                     "default": "\uEBE8"
                   },
+                  "codecommit_icon": {
+                    "type": "string",
+                    "title": "CodeCommit Icon",
+                    "description": "Icon/text to display when the upstream is CodeCommit",
+                    "default": "\uF270"
+                  },
                   "git_icon": {
                     "type": "string",
                     "title": "Git Icon",
@@ -2860,6 +2867,28 @@
             }
           }
         },
+        {
+          "if": {
+            "properties": {
+              "type": {
+                "const": "helm"
+              }
+            }
+          },
+          "then": {
+            "title": "Helm segment",
+            "description": "https://ohmyposh.dev/docs/segments/helm",
+            "properties": {
+              "properties": {
+                "properties": {
+                  "display_mode": {
+                    "$ref": "#/definitions/display_mode"
+                  }
+                }
+              }
+            }
+          }
+        },
         {
           "if": {
             "properties": {
diff --git a/website/docs/configuration/general.mdx b/website/docs/configuration/general.mdx
index 1522b8c6..4357f3c2 100644
--- a/website/docs/configuration/general.mdx
+++ b/website/docs/configuration/general.mdx
@@ -129,7 +129,7 @@ For example, the following is a valid `--config` flag:
 | Name                         | Type             | Description                                                                                                                                                                           |
 | ---------------------------- | ---------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
 | `final_space`                | `boolean`        | when true adds a space at the end of the prompt                                                                                                                                       |
-| `pwd`                        | `string`         | notify terminal of current working directory, values can be `osc99`, `osc7`, or `osc51` depending on your terminal                                                                    |
+| `pwd`                        | `string`         | notify terminal of current working directory, values can be `osc99`, `osc7`, or `osc51` depending on your terminal. Supports [templates][templates]                                   |
 | `terminal_background`        | `string`         | [color][colors] - terminal background color, set to your terminal's background color when you notice black elements in Windows Terminal or the Visual Studio Code integrated terminal |
 | `accent_color`               | `string`         | [color][colors] - accent color, used as a fallback when the `accent` [color][accent] is not supported                                                                                 |
 | `var`                        | `map[string]any` | config variables to use in [templates][templates]. Can be any value                                                                                                                   |
diff --git a/website/docs/configuration/templates.mdx b/website/docs/configuration/templates.mdx
index ce31e1e5..bcc31711 100644
--- a/website/docs/configuration/templates.mdx
+++ b/website/docs/configuration/templates.mdx
@@ -177,7 +177,7 @@ use them.
 To use another segment's template properties in a template, you can make use of `{{ .Segments.Segment }}`
 in your template where `.Segment` is the name of the segment you want to use with the first letter uppercased.
 
-If you want to for example use the [git][git] segment's `.UpstreamGone` property in the [exit][exit] segment, you can
+If you want to for example use the [git][git] segment's `.UpstreamGone` property in the [status][status] segment, you can
 do so like this:
 
 ```json
@@ -255,7 +255,7 @@ This can be used in templates and icons/text inside your config.
 [sprig]: https://masterminds.github.io/sprig/
 [glob]: https://pkg.go.dev/path/filepath#Glob
 [git]: /docs/segments/git
-[exit]: /docs/segments/exit
+[status]: /docs/segments/status
 [templates]: /docs/configuration/segment#templates
 [regexpms]: https://pkg.go.dev/regexp#Regexp.MatchString
 [regexpra]: https://pkg.go.dev/regexp#Regexp.ReplaceAllString
diff --git a/website/docs/contributors.md b/website/docs/contributors.md
index 77529fd9..44d114c1 100644
--- a/website/docs/contributors.md
+++ b/website/docs/contributors.md
@@ -240,7 +240,7 @@ Thanks goes to these wonderful people ([emoji key][acek]):
       <td align="center" valign="top" width="14.28%"><a href="https://mespin.me/"><img src="https://avatars.githubusercontent.com/u/38158676?v=4?s=100" width="100px;" alt="Marc Espín"/><br /><sub><b>Marc Espín</b></sub></a><br /><a href="https://github.com/JanDeDobbeleer/oh-my-posh/commits?author=marc2332" title="Code">💻</a></td>
       <td align="center" valign="top" width="14.28%"><a href="https://github.com/ksdpmx"><img src="https://avatars.githubusercontent.com/u/3256083?v=4?s=100" width="100px;" alt="jasonz"/><br /><sub><b>jasonz</b></sub></a><br /><a href="https://github.com/JanDeDobbeleer/oh-my-posh/commits?author=ksdpmx" title="Code">💻</a> <a href="https://github.com/JanDeDobbeleer/oh-my-posh/commits?author=ksdpmx" title="Documentation">📖</a></td>
       <td align="center" valign="top" width="14.28%"><a href="https://bentsukun.ch"><img src="https://avatars.githubusercontent.com/u/866330?v=4?s=100" width="100px;" alt="Benny Siegert"/><br /><sub><b>Benny Siegert</b></sub></a><br /><a href="https://github.com/JanDeDobbeleer/oh-my-posh/commits?author=bsiegert" title="Code">💻</a></td>
-      <td align="center" valign="top" width="14.28%"><a href="http://www.kemadev.fr/fr/"><img src="https://avatars.githubusercontent.com/u/54537427?v=4?s=100" width="100px;" alt="kema"/><br /><sub><b>kema</b></sub></a><br /><a href="https://github.com/JanDeDobbeleer/oh-my-posh/commits?author=kema-dev" title="Code">💻</a></td>
+      <td align="center" valign="top" width="14.28%"><a href="http://www.kemadev.fr/fr/"><img src="https://avatars.githubusercontent.com/u/54537427?v=4?s=100" width="100px;" alt="kema"/><br /><sub><b>kema</b></sub></a><br /><a href="https://github.com/JanDeDobbeleer/oh-my-posh/commits?author=kema-dev" title="Code">💻</a> <a href="#design-kema-dev" title="Design">🎨</a> <a href="https://github.com/JanDeDobbeleer/oh-my-posh/commits?author=kema-dev" title="Documentation">📖</a></td>
       <td align="center" valign="top" width="14.28%"><a href="http://mavaddat.ca"><img src="https://avatars.githubusercontent.com/u/5055400?v=4?s=100" width="100px;" alt="Mavaddat Javid"/><br /><sub><b>Mavaddat Javid</b></sub></a><br /><a href="https://github.com/JanDeDobbeleer/oh-my-posh/commits?author=mavaddat" title="Code">💻</a></td>
       <td align="center" valign="top" width="14.28%"><a href="https://iavael.name/"><img src="https://avatars.githubusercontent.com/u/905853?v=4?s=100" width="100px;" alt="Iavael"/><br /><sub><b>Iavael</b></sub></a><br /><a href="https://github.com/JanDeDobbeleer/oh-my-posh/commits?author=iavael" title="Code">💻</a></td>
       <td align="center" valign="top" width="14.28%"><a href="https://github.com/Kushal-Chandar"><img src="https://avatars.githubusercontent.com/u/83660514?v=4?s=100" width="100px;" alt="Kushal-Chandar"/><br /><sub><b>Kushal-Chandar</b></sub></a><br /><a href="#design-Kushal-Chandar" title="Design">🎨</a></td>
@@ -269,6 +269,13 @@ Thanks goes to these wonderful people ([emoji key][acek]):
       <td align="center" valign="top" width="14.28%"><a href="https://github.com/ChandanChainani"><img src="https://avatars.githubusercontent.com/u/28807775?v=4?s=100" width="100px;" alt="ChandanChainani"/><br /><sub><b>ChandanChainani</b></sub></a><br /><a href="https://github.com/JanDeDobbeleer/oh-my-posh/commits?author=ChandanChainani" title="Documentation">📖</a></td>
       <td align="center" valign="top" width="14.28%"><a href="https://github.com/jenspinney"><img src="https://avatars.githubusercontent.com/u/3200507?v=4?s=100" width="100px;" alt="Jen Spinney"/><br /><sub><b>Jen Spinney</b></sub></a><br /><a href="https://github.com/JanDeDobbeleer/oh-my-posh/commits?author=jenspinney" title="Code">💻</a> <a href="https://github.com/JanDeDobbeleer/oh-my-posh/commits?author=jenspinney" title="Documentation">📖</a></td>
       <td align="center" valign="top" width="14.28%"><a href="https://github.com/rotu"><img src="https://avatars.githubusercontent.com/u/119948?v=4?s=100" width="100px;" alt="Dan Rose"/><br /><sub><b>Dan Rose</b></sub></a><br /><a href="https://github.com/JanDeDobbeleer/oh-my-posh/commits?author=rotu" title="Code">💻</a></td>
+      <td align="center" valign="top" width="14.28%"><a href="https://carlwa.com"><img src="https://avatars.githubusercontent.com/u/2829438?v=4?s=100" width="100px;" alt="Carl Walsh"/><br /><sub><b>Carl Walsh</b></sub></a><br /><a href="https://github.com/JanDeDobbeleer/oh-my-posh/commits?author=darthwalsh" title="Documentation">📖</a></td>
+      <td align="center" valign="top" width="14.28%"><a href="https://github.com/ercpereda"><img src="https://avatars.githubusercontent.com/u/13546685?v=4?s=100" width="100px;" alt="Ernesto R. C. Pereda"/><br /><sub><b>Ernesto R. C. Pereda</b></sub></a><br /><a href="https://github.com/JanDeDobbeleer/oh-my-posh/commits?author=ercpereda" title="Code">💻</a> <a href="#design-ercpereda" title="Design">🎨</a> <a href="https://github.com/JanDeDobbeleer/oh-my-posh/commits?author=ercpereda" title="Documentation">📖</a></td>
+    </tr>
+    <tr>
+      <td align="center" valign="top" width="14.28%"><a href="https://github.com/0Ky"><img src="https://avatars.githubusercontent.com/u/16103757?v=4?s=100" width="100px;" alt="ｃｒｙｐｔｉｘ"/><br /><sub><b>ｃｒｙｐｔｉｘ</b></sub></a><br /><a href="https://github.com/JanDeDobbeleer/oh-my-posh/commits?author=0Ky" title="Documentation">📖</a></td>
+      <td align="center" valign="top" width="14.28%"><a href="https://github.com/ehawman"><img src="https://avatars.githubusercontent.com/u/52979227?v=4?s=100" width="100px;" alt="Evan Hawman"/><br /><sub><b>Evan Hawman</b></sub></a><br /><a href="#design-ehawman" title="Design">🎨</a></td>
+      <td align="center" valign="top" width="14.28%"><a href="https://zerdox.dev"><img src="https://avatars.githubusercontent.com/u/49815452?v=4?s=100" width="100px;" alt="Mark Lansky"/><br /><sub><b>Mark Lansky</b></sub></a><br /><a href="#design-ZerdoX-x" title="Design">🎨</a></td>
     </tr>
   </tbody>
 </table>
diff --git a/website/docs/faq.mdx b/website/docs/faq.mdx
index bd7b502b..c12ecbf1 100644
--- a/website/docs/faq.mdx
+++ b/website/docs/faq.mdx
@@ -26,7 +26,19 @@ oh-my-posh enable notice
 ### The prompt is slow (delay in showing the prompt between commands)
 
 :::tip Windows
-Make sure Windows Defender, or your AntiVirus, has an exclusion [configured][exclusion].
+The delay you're experiencing may be linked to the real-time protection features of antivirus software.
+
+To potentially alleviate this delay, ensure that Windows Defender or your primary antivirus software has an [exclusion][exclusion] set for the full path of the executable. To identify the full path of the executable in question, you can run the following command in a PowerShell prompt: 
+
+```powershell
+(Get-Command oh-my-posh).Source
+```
+
+Additionally, check your antivirus settings for features that actively scan scripts during execution. Even with exclusions set, some antivirus programs might still actively scan scripts, which can introduce a noticeable delay.
+:::
+
+:::caution
+Always proceed with caution to maintain a balance between system performance and security. Modifying security settings or adding exclusions can expose your system to potential threats.
 :::
 
 <Tabs
diff --git a/website/docs/segments/git.mdx b/website/docs/segments/git.mdx
index b084d0ed..a20654db 100644
--- a/website/docs/segments/git.mdx
+++ b/website/docs/segments/git.mdx
@@ -116,6 +116,7 @@ You can set the following properties to `true` to enable fetching additional inf
 | `gitlab_icon`       | `string`            | icon/text to display when the upstream is Gitlab - defaults to `\uF296 `                                                                                                     |
 | `bitbucket_icon`    | `string`            | icon/text to display when the upstream is Bitbucket - defaults to `\uF171 `                                                                                                  |
 | `azure_devops_icon` | `string`            | icon/text to display when the upstream is Azure DevOps - defaults to `\uEBE8 `                                                                                               |
+| `codecommit_icon`   | `string`            | icon/text to display when the upstream is AWS CodeCommit - defaults to `\uF270 `                                                                                             |
 | `git_icon`          | `string`            | icon/text to display when the upstream is not known/mapped - defaults to `\uE5FB `                                                                                           |
 | `upstream_icons`    | `map[string]string` | a key, value map representing the remote URL (or a part of that URL) and icon to use in case the upstream URL contains the key. These get precedence over the standard icons |
 
@@ -152,6 +153,11 @@ You can set the following properties to `true` to enable fetching additional inf
 | `.Dir`           | `string`  | the repository's root directory                                                                                                  |
 | `.Kraken`        | `string`  | a link to the current HEAD in [GitKraken][kraken-ref] for use in [hyperlinks][hyperlinks] in templates `{{ url .HEAD .Kraken }}` |
 | `.Commit`        | `Commit`  | HEAD commit information (see below)                                                                                              |
+| `.Detached`      | `boolean` | true when the head is detached                                                                                                   |
+| `.Merge`         | `boolean` | true when in a merge                                                                                                             |
+| `.Rebase`        | `boolean` | true when in a rebase                                                                                                            |
+| `.CherryPick`    | `boolean` | true when in a cherry pick                                                                                                       |
+| `.Revert`        | `boolean` | true when in a revert                                                                                                            |
 
 ### Status
 
diff --git a/website/docs/segments/helm.mdx b/website/docs/segments/helm.mdx
new file mode 100644
index 00000000..3edff120
--- /dev/null
+++ b/website/docs/segments/helm.mdx
@@ -0,0 +1,46 @@
+---
+id: helm
+title: Helm
+sidebar_label: Helm
+---
+
+## What
+
+Display the version of helm
+
+## Sample Configuration
+
+import Config from '@site/src/components/Config.js';
+
+<Config data={{
+  "background": "#a7cae1",
+  "foreground": "#100e23",
+  "powerline_symbol": "\ue0b0",
+  "template": " Helm {{ .Version }}",
+  "style": "powerline",
+  "type": "helm"
+}}/>
+
+## Properties
+
+| Name           | Type     | Description                                                                                                                                                                      |
+| -------------- | -------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
+| `display_mode` | `string` | <ul><li>`always`: the segment is always displayed (**default**)</li><li>`files`: the segment is only displayed when a `Chart.yaml` (or `Chart.yml`) file is present </li></ul>   |
+
+## Template ([info][templates])
+
+:::note default template
+
+```template
+ Helm {{ .Version }}
+```
+
+:::
+
+### Properties
+
+| Name         | Type     | Description                |
+| ------------ | -------- | -------------------------- |
+| `.Version`   | `string` | Helm cli version           |
+
+[templates]: /docs/configuration/templates
diff --git a/website/docs/segments/project.mdx b/website/docs/segments/project.mdx
index f1980234..1c62cb9b 100644
--- a/website/docs/segments/project.mdx
+++ b/website/docs/segments/project.mdx
@@ -17,19 +17,23 @@ Supports:
 - Any nuspec based project (`*.nuspec`, first file match info is displayed)
 - .NET project (`*.csproj`, `*.vbproj` or `*.fsproj`, first file match info is displayed)
 - Julia project (`JuliaProject.toml`, `Project.toml`)
+- PowerShell project (`*.psd1`, first file match info is displayed)
 
 ## Sample Configuration
 
-import Config from '@site/src/components/Config.js';
+import Config from "@site/src/components/Config.js";
 
-<Config data={{
-  "type": "project",
-  "style": "powerline",
-  "powerline_symbol": "\uE0B0",
-  "foreground": "#193549",
-  "background": "#ffeb3b",
-  "template": " {{ if .Error }}{{ .Error }}{{ else }}{{ if .Version }}\uf487 {{.Version}}{{ end }} {{ if .Name }}{{ .Name }}{{ end }}{{ end }} "
-}}/>
+<Config
+  data={{
+    type: "project",
+    style: "powerline",
+    powerline_symbol: "\uE0B0",
+    foreground: "#193549",
+    background: "#ffeb3b",
+    template:
+      " {{ if .Error }}{{ .Error }}{{ else }}{{ if .Version }}\uf487 {{.Version}}{{ end }} {{ if .Name }}{{ .Name }}{{ end }}{{ end }} ",
+  }}
+/>
 
 ## Properties
 
@@ -49,12 +53,12 @@ import Config from '@site/src/components/Config.js';
 
 ### Properties
 
-| Name       | Type     | Description                                                                           |
-| ---------- | -------- | ------------------------------------------------------------------------------------- |
-| `.Type`    | `string` | The type of project (`node`, `cargo`, `poetry`, `php`, `nuspec`, `dotnet` or `julia`) |
-| `.Version` | `string` | The version of your project                                                           |
-| `.Target`  | `string` | The target framwork/language version of your project                                  |
-| `.Name`    | `string` | The name of your project                                                              |
-| `.Error`   | `string` | The error context when we can't fetch the project info                                |
+| Name       | Type     | Description                                                                                                                                                        |
+| ---------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
+| `.Type`    | `string` | The type of project:<ul><li>`node`</li><li>`cargo`</li><li>`poetry`</li><li>`php`</li><li>`nuspec`</li><li>`dotnet`</li><li>`julia`</li><li>`powershell`</li></ul> |
+| `.Version` | `string` | The version of your project                                                                                                                                        |
+| `.Target`  | `string` | The target framwork/language version of your project                                                                                                               |
+| `.Name`    | `string` | The name of your project                                                                                                                                           |
+| `.Error`   | `string` | The error context when we can't fetch the project info                                                                                                             |
 
 [templates]: /docs/configuration/templates
diff --git a/website/sidebars.js b/website/sidebars.js
index c0d2d845..22bfabad 100644
--- a/website/sidebars.js
+++ b/website/sidebars.js
@@ -79,6 +79,7 @@ module.exports = {
         "segments/gitversion",
         "segments/golang",
         "segments/haskell",
+        "segments/helm",
         "segments/ipify",
         "segments/iterm",
         "segments/java",
diff --git a/website/static/install.sh b/website/static/install.sh
index 567b92ed..5aa80920 100755
--- a/website/static/install.sh
+++ b/website/static/install.sh
@@ -126,7 +126,11 @@ install() {
 
     info "⬇️  Downloading oh-my-posh from ${url}"
 
-    curl -s -L https://github.com/JanDeDobbeleer/oh-my-posh/releases/latest/download/posh-${target} -o $executable
+    http_response=$(curl -s -f -L $url -o $executable -w "%{http_code}")
+    if [ $http_response != "200" ]; then
+        error "Unable to download executable at ${url}\nPlease validate your connection and/or proxy settings"
+    fi
+
     chmod +x $executable
 
     # install themes in cache
@@ -135,16 +139,23 @@ install() {
     info "🎨 Installing oh-my-posh themes in ${cache_dir}/themes\n"
 
     theme_dir="${cache_dir}/themes"
+    url="https://github.com/JanDeDobbeleer/oh-my-posh/releases/latest/download/themes.zip"
     mkdir -p $theme_dir
-    curl -s -L https://github.com/JanDeDobbeleer/oh-my-posh/releases/latest/download/themes.zip -o ${cache_dir}/themes.zip
-    unzip -o -q ${cache_dir}/themes.zip -d $theme_dir
-    chmod u+rw ${theme_dir}/*.omp.*
-    rm ${cache_dir}/themes.zip
+    http_response=$(curl -s -f -L $url -o ${cache_dir}/themes.zip -w "%{http_code}")
+    if [ $http_response == "200" ]; then
+        unzip -o -q ${cache_dir}/themes.zip -d $theme_dir
+        chmod u+rw ${theme_dir}/*.omp.*
+        rm ${cache_dir}/themes.zip
+    else
+        warn "Unable to download themes at ${url}\nPlease validate your connection and/or proxy settings"
+    fi
 
     info "🚀 Installation complete.\n\nYou can follow the instructions at https://ohmyposh.dev/docs/installation/prompt"
-    info "to setup your shell to use oh-my-posh.\n"
-    info "If you want to use a built-in theme, you can find them in the ${theme_dir} directory:"
-    info "  oh-my-posh init {shell} --config ${theme_dir}/{theme}.omp.json\n"
+    info "to setup your shell to use oh-my-posh."
+    if [ $http_response == "200" ]; then
+        info "\nIf you want to use a built-in theme, you can find them in the ${theme_dir} directory:"
+        info "  oh-my-posh init {shell} --config ${theme_dir}/{theme}.omp.json\n"
+    fi
 }
 
 detect_arch() {


Test patch:
diff --git a/src/ansi/ansi_writer_test.go b/src/ansi/ansi_writer_test.go
index 24011b6a..c23ad58c 100644
--- a/src/ansi/ansi_writer_test.go
+++ b/src/ansi/ansi_writer_test.go
@@ -195,6 +195,12 @@ func TestWriteANSIColors(t *testing.T) {
 			Expected: "\x1b[40m\x1b[30mtest\x1b[0m",
 			Colors:   &Colors{Foreground: "black", Background: "white"},
 		},
+		{
+			Case:     "Nested override",
+			Input:    "hello, <red>world, <white>rabbit</> hello</>",
+			Expected: "\x1b[47m\x1b[30mhello, \x1b[31mworld, \x1b[37mrabbit\x1b[31m hello\x1b[0m",
+			Colors:   &Colors{Foreground: "black", Background: "white"},
+		},
 	}
 
 	for _, tc := range cases {
diff --git a/src/engine/engine_test.go b/src/engine/engine_test.go
index 87c0447f..4f45603c 100644
--- a/src/engine/engine_test.go
+++ b/src/engine/engine_test.go
@@ -49,22 +49,34 @@ func TestPrintPWD(t *testing.T) {
 	cases := []struct {
 		Case     string
 		Expected string
-		PWD      string
+		Config   string
+		Pwd      string
+		Shell    string
 		OSC99    bool
 	}{
 		{Case: "Empty PWD"},
-		{Case: "OSC99", PWD: ansi.OSC99, Expected: "\x1b]9;9;pwd\x1b\\"},
-		{Case: "OSC7", PWD: ansi.OSC7, Expected: "\x1b]7;file://host/pwd\x1b\\"},
-		{Case: "OSC51", PWD: ansi.OSC51, Expected: "\x1b]51;Auser@host:pwd\x1b\\"},
+		{Case: "OSC99", Config: ansi.OSC99, Expected: "\x1b]9;9;pwd\x1b\\"},
+		{Case: "OSC7", Config: ansi.OSC7, Expected: "\x1b]7;file://host/pwd\x1b\\"},
+		{Case: "OSC51", Config: ansi.OSC51, Expected: "\x1b]51;Auser@host:pwd\x1b\\"},
 		{Case: "Deprecated OSC99", OSC99: true, Expected: "\x1b]9;9;pwd\x1b\\"},
-		{Case: "Template (empty)", PWD: "{{ if eq .Shell \"pwsh\" }}osc7{{ end }}"},
-		{Case: "Template (non empty)", PWD: "{{ if eq .Shell \"shell\" }}osc7{{ end }}", Expected: "\x1b]7;file://host/pwd\x1b\\"},
+		{Case: "Template (empty)", Config: "{{ if eq .Shell \"pwsh\" }}osc7{{ end }}"},
+		{Case: "Template (non empty)", Config: "{{ if eq .Shell \"shell\" }}osc7{{ end }}", Expected: "\x1b]7;file://host/pwd\x1b\\"},
+		{
+			Case:     "OSC99 Bash",
+			Pwd:      `C:\Users\user\Documents\GitHub\oh-my-posh`,
+			Config:   ansi.OSC99,
+			Shell:    shell.BASH,
+			Expected: "\x1b]9;9;C:\\\\Users\\\\user\\\\Documents\\\\GitHub\\\\oh-my-posh\x1b\\",
+		},
 	}
 
 	for _, tc := range cases {
 		env := new(mock.MockedEnvironment)
-		env.On("Pwd").Return("pwd")
-		env.On("Shell").Return("shell")
+		if len(tc.Pwd) == 0 {
+			tc.Pwd = "pwd"
+		}
+		env.On("Pwd").Return(tc.Pwd)
+		env.On("Shell").Return(tc.Shell)
 		env.On("User").Return("user")
 		env.On("Host").Return("host", nil)
 		env.On("DebugF", mock2.Anything, mock2.Anything).Return(nil)
@@ -78,7 +90,7 @@ func TestPrintPWD(t *testing.T) {
 		engine := &Engine{
 			Env: env,
 			Config: &Config{
-				PWD:   tc.PWD,
+				PWD:   tc.Config,
 				OSC99: tc.OSC99,
 			},
 			Writer: writer,
diff --git a/src/platform/battery/battery_openbsd_test.go b/src/platform/battery/battery_openbsd_test.go
new file mode 100644
index 00000000..40b7b181
--- /dev/null
+++ b/src/platform/battery/battery_openbsd_test.go
@@ -0,0 +1,63 @@
+package battery
+
+import (
+	"testing"
+
+	"github.com/stretchr/testify/assert"
+)
+
+func TestParseBatteryOutput(t *testing.T) {
+	cases := []struct {
+		Case                string
+		PercentOutput       string
+    StatusOutput        string
+		ExpectedState       State
+		ExpectedPercentage  int
+		ExpectError        bool
+	}{
+		{
+			Case:               "charging",
+			PercentOutput:      "99",
+      StatusOutput:       "3",
+			ExpectedState:      Charging,
+			ExpectedPercentage: 99,
+		},
+		{
+			Case:               "charging 1%",
+			PercentOutput:      "1",
+      StatusOutput:       "3",
+			ExpectedState:      Charging,
+			ExpectedPercentage: 1,
+		},
+		{
+			Case:               "removed",
+			PercentOutput:      "0",
+      StatusOutput:       "4",
+			ExpectedState:      Unknown,
+			ExpectedPercentage: 0,
+		},
+		{
+			Case:               "charged",
+			PercentOutput:      "100",
+      StatusOutput:       "0",
+			ExpectedState:      Full,
+			ExpectedPercentage: 100,
+		},
+		{
+			Case:               "discharging",
+			PercentOutput:      "25",
+      StatusOutput:       "1",
+			ExpectedState:      Discharging,
+			ExpectedPercentage: 25,
+		},
+	}
+	for _, tc := range cases {
+		info, err := parseBatteryOutput(tc.PercentOutput, tc.StatusOutput)
+		if tc.ExpectError {
+			assert.Error(t, err, tc.Case)
+			return
+		}
+		assert.Equal(t, tc.ExpectedState, info.State, tc.Case)
+		assert.Equal(t, tc.ExpectedPercentage, info.Percentage, tc.Case)
+	}
+}
diff --git a/src/platform/battery/battery_windows_nix_test.go b/src/platform/battery/battery_windows_nix_test.go
index 00a97c54..02739523 100644
--- a/src/platform/battery/battery_windows_nix_test.go
+++ b/src/platform/battery/battery_windows_nix_test.go
@@ -1,4 +1,4 @@
-//go:build !darwin && !netbsd
+//go:build !darwin && !netbsd && !openbsd
 
 // battery
 // Copyright (C) 2016-2017 Karol 'Kenji Takahashi' Woźniak
diff --git a/src/segments/git_test.go b/src/segments/git_test.go
index ecbcf593..e6f179dd 100644
--- a/src/segments/git_test.go
+++ b/src/segments/git_test.go
@@ -436,6 +436,8 @@ func TestSetGitStatus(t *testing.T) {
 		ExpectedUpstreamGone bool
 		ExpectedAhead        int
 		ExpectedBehind       int
+		Rebase               bool
+		Merge                bool
 	}{
 		{
 			Case: "all different options on working and staging, no remote",
@@ -535,6 +537,40 @@ func TestSetGitStatus(t *testing.T) {
 			ExpectedRef:          "branch-is-gone",
 			ExpectedUpstreamGone: true,
 		},
+		{
+			Case: "rebase with 2 merge conflicts",
+			Output: `
+			# branch.oid 1234567891011121314
+			# branch.head rework-git-status
+			# branch.upstream origin/rework-git-status
+			# branch.ab +0 -0
+			1 AA N...
+			1 AA N...
+			`,
+			ExpectedUpstream: "origin/rework-git-status",
+			ExpectedHash:     "1234567",
+			ExpectedRef:      "rework-git-status",
+			Rebase:           true,
+			ExpectedStaging:  &GitStatus{ScmStatus: ScmStatus{Unmerged: 2}},
+		},
+		{
+			Case: "merge with 4 merge conflicts",
+			Output: `
+			# branch.oid 1234567891011121314
+			# branch.head rework-git-status
+			# branch.upstream origin/rework-git-status
+			# branch.ab +0 -0
+			1 AA N...
+			1 AA N...
+			1 AA N...
+			1 AA N...
+			`,
+			ExpectedUpstream: "origin/rework-git-status",
+			ExpectedHash:     "1234567",
+			ExpectedRef:      "rework-git-status",
+			Merge:            true,
+			ExpectedStaging:  &GitStatus{ScmStatus: ScmStatus{Unmerged: 4}},
+		},
 	}
 	for _, tc := range cases {
 		env := new(mock.MockedEnvironment)
@@ -554,6 +590,8 @@ func TestSetGitStatus(t *testing.T) {
 		if tc.ExpectedStaging == nil {
 			tc.ExpectedStaging = &GitStatus{}
 		}
+		g.Rebase = tc.Rebase
+		g.Merge = tc.Merge
 		tc.ExpectedStaging.Formats = map[string]string{}
 		tc.ExpectedWorking.Formats = map[string]string{}
 		g.setGitStatus()
@@ -630,6 +668,7 @@ func TestGitUpstream(t *testing.T) {
 		{Case: "Bitbucket", Expected: "BB", Upstream: "bitbucket.org/test"},
 		{Case: "Azure DevOps", Expected: "AD", Upstream: "dev.azure.com/test"},
 		{Case: "Azure DevOps Dos", Expected: "AD", Upstream: "test.visualstudio.com"},
+		{Case: "CodeCommit", Expected: "AC", Upstream: "codecommit::eu-west-1://test-repository"},
 		{Case: "Gitstash", Expected: "G", Upstream: "gitstash.com/test"},
 		{Case: "My custom server", Expected: "CU", Upstream: "mycustom.server/test"},
 	}
@@ -644,6 +683,7 @@ func TestGitUpstream(t *testing.T) {
 			GitlabIcon:      "GL",
 			BitbucketIcon:   "BB",
 			AzureDevOpsIcon: "AD",
+			CodeCommit:      "AC",
 			GitIcon:         "G",
 			UpstreamIcons: map[string]string{
 				"mycustom.server": "CU",
diff --git a/src/segments/golang_test.go b/src/segments/golang_test.go
index 4cde06bb..4374bd2d 100644
--- a/src/segments/golang_test.go
+++ b/src/segments/golang_test.go
@@ -49,7 +49,7 @@ func TestGolang(t *testing.T) {
 	}{
 		{Case: "Go 1.15", ExpectedString: "1.15.8", Version: "go version go1.15.8 darwin/amd64"},
 		{Case: "Go 1.16", ExpectedString: "1.16", Version: "go version go1.16 darwin/amd64"},
-		{Case: "go.mod 1.20", ParseModFile: true, HasModFileInParentDir: true, ExpectedString: "1.20"},
+		{Case: "go.mod 1.21", ParseModFile: true, HasModFileInParentDir: true, ExpectedString: "1.21"},
 		{Case: "no go.mod file fallback", ParseModFile: true, ExpectedString: "1.16", Version: "go version go1.16 darwin/amd64"},
 		{
 			Case:                  "invalid go.mod file fallback",
diff --git a/src/segments/helm_test.go b/src/segments/helm_test.go
new file mode 100644
index 00000000..83e5d4a3
--- /dev/null
+++ b/src/segments/helm_test.go
@@ -0,0 +1,89 @@
+package segments
+
+import (
+	"errors"
+	"testing"
+
+	"github.com/stretchr/testify/assert"
+	testify_mock "github.com/stretchr/testify/mock"
+
+	"github.com/jandedobbeleer/oh-my-posh/src/mock"
+	"github.com/jandedobbeleer/oh-my-posh/src/platform"
+	"github.com/jandedobbeleer/oh-my-posh/src/properties"
+)
+
+func TestHelmSegment(t *testing.T) {
+	cases := []struct {
+		Case            string
+		HelmExists      bool
+		ExpectedEnabled bool
+		ExpectedString  string
+		Template        string
+		DisplayMode     string
+		ChartFile       string
+	}{
+		{
+			Case:            "Helm not installed",
+			HelmExists:      false,
+			ExpectedEnabled: false,
+		},
+		{
+			Case:            "DisplayMode always inside chart",
+			HelmExists:      true,
+			ExpectedEnabled: true,
+			ExpectedString:  "Helm 3.12.3",
+			DisplayMode:     "always",
+		},
+		{
+			Case:            "DisplayMode always outside chart",
+			HelmExists:      true,
+			ExpectedEnabled: true,
+			ExpectedString:  "Helm 3.12.3",
+			DisplayMode:     "always",
+		},
+		{
+			Case:            "DisplayMode files inside chart. Chart file Chart.yml",
+			HelmExists:      true,
+			ExpectedEnabled: true,
+			ExpectedString:  "Helm 3.12.3",
+			DisplayMode:     "files",
+			ChartFile:       "Chart.yml",
+		},
+		{
+			Case:            "DisplayMode always inside chart. Chart file Chart.yaml",
+			HelmExists:      true,
+			ExpectedEnabled: true,
+			ExpectedString:  "Helm 3.12.3",
+			DisplayMode:     "files",
+			ChartFile:       "Chart.yaml",
+		},
+		{
+			Case:            "DisplayMode always outside chart",
+			HelmExists:      true,
+			ExpectedEnabled: false,
+			DisplayMode:     "files",
+		},
+	}
+
+	for _, tc := range cases {
+		env := new(mock.MockedEnvironment)
+		env.On("HasCommand", "helm").Return(tc.HelmExists)
+		env.On("RunCommand", "helm", []string{"version", "--short", "--template={{.Version}}"}).Return("v3.12.3", nil)
+
+		env.On("HasParentFilePath", tc.ChartFile).Return(&platform.FileInfo{}, nil)
+		env.On("HasParentFilePath", testify_mock.Anything).Return(&platform.FileInfo{}, errors.New("no such file or directory"))
+
+		props := properties.Map{
+			DisplayMode: tc.DisplayMode,
+		}
+
+		h := &Helm{
+			env:   env,
+			props: props,
+		}
+		assert.Equal(t, tc.ExpectedEnabled, h.Enabled(), tc.Case)
+		if tc.ExpectedEnabled {
+			assert.Equal(t, tc.ExpectedString, renderTemplate(env, h.Template(), h), tc.Case)
+		}
+	}
+}