Repository: JanDeDobbeleer/oh-my-posh
Pull request #1909 (2022-03-12 21:04:34)

refactor(cli): migrate to cobra
### Prerequisites

- [x] I have read and understood the `CONTRIBUTING` guide
- [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

- fix: don't panic when env is empty
- refactor(cli): migrate to cobra


<!---

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.

-->

[cc]: https://www.conventionalcommits.org/en/v1.0.0/#summary



Patch:
diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile
index 3fdab45e..b7fd3b14 100644
--- a/.devcontainer/Dockerfile
+++ b/.devcontainer/Dockerfile
@@ -71,10 +71,10 @@ RUN chmod 777 -R /home/${USERNAME}/.config
 
 # Override vscode's own Bash prompt with oh-my-posh:
 RUN sed -i 's/^__bash_prompt$/#&/' /home/${USERNAME}/.bashrc && \
-    echo "eval \"\$(oh-my-posh --init --shell bash --config $POSH_THEME)\"" >> /home/${USERNAME}/.bashrc
+    echo "eval \"\$(oh-my-posh prompt init bash --config $POSH_THEME)\"" >> /home/${USERNAME}/.bashrc
 
 # Override vscode's own ZSH prompt with oh-my-posh:
-RUN echo "eval \"\$(oh-my-posh --init --shell zsh --config $POSH_THEME)\"" >> /home/${USERNAME}/.zshrc
+RUN echo "eval \"\$(oh-my-posh prompt init zsh --config $POSH_THEME)\"" >> /home/${USERNAME}/.zshrc
 
 # Set container timezone:
 ARG TZ="UTC"
diff --git a/.devcontainer/Microsoft.PowerShell_profile.ps1 b/.devcontainer/Microsoft.PowerShell_profile.ps1
index f50f1263..b129ac7e 100644
--- a/.devcontainer/Microsoft.PowerShell_profile.ps1
+++ b/.devcontainer/Microsoft.PowerShell_profile.ps1
@@ -6,6 +6,6 @@ Import-Module Terminal-Icons
 Set-PSReadlineKeyHandler -Key Tab -Function MenuComplete
 
 $env:POSH_GIT_ENABLED=$true
-oh-my-posh --init --shell pwsh --config $env:POSH_THEME | Invoke-Expression
+oh-my-posh prompt init pwsh --config $env:POSH_THEME | Invoke-Expression
 
 # NOTE: You can override the above env var from the devcontainer.json "args" under the "build" key.
diff --git a/.devcontainer/config.fish b/.devcontainer/config.fish
index 8f64bb23..c3bb0966 100644
--- a/.devcontainer/config.fish
+++ b/.devcontainer/config.fish
@@ -1,4 +1,4 @@
 # Activate oh-my-posh prompt:
-oh-my-posh --init --shell fish --config $POSH_THEME | source
+oh-my-posh prompt init fish --config $POSH_THEME | source
 
 # NOTE: You can override the above env vars from the devcontainer.json "args" under the "build" key.
diff --git a/.vscode/launch.json b/.vscode/launch.json
index c089877c..96dca9e3 100644
--- a/.vscode/launch.json
+++ b/.vscode/launch.json
@@ -8,6 +8,9 @@
       "mode": "debug",
       "program": "${workspaceRoot}/src",
       "args": [
+        "prompt",
+        "print",
+        "primary",
         "--config=${workspaceRoot}/themes/cinnamon.omp.json",
         "--shell=pwsh",
         "--terminal-width=200",
@@ -20,6 +23,9 @@
       "mode": "debug",
       "program": "${workspaceRoot}/src",
       "args": [
+        "prompt",
+        "print",
+        "tooltip",
         "--config=${workspaceRoot}/themes/jandedobbeleer.omp.json",
         "--command=git",
         "--shell=pwsh"
@@ -32,9 +38,11 @@
       "mode": "debug",
       "program": "${workspaceRoot}/src",
       "args": [
+        "prompt",
+        "print",
+        "transient",
         "--config=${workspaceRoot}/themes/jandedobbeleer.omp.json",
         "--shell=pwsh",
-        "--print-transient"
       ]
     },
     {
@@ -54,7 +62,9 @@
       "mode": "debug",
       "program": "${workspaceRoot}/src",
       "args": [
-        "--debug",
+        "prompt",
+        "print",
+        "debug",
         "--config=${workspaceRoot}/themes/jandedobbeleer.omp.json"
       ]
     },
@@ -65,9 +75,11 @@
       "mode": "debug",
       "program": "${workspaceRoot}/src",
       "args": [
-        "--print-init",
-        "--shell=cmd",
-        "--config=${workspaceRoot}/themes/jandedobbeleer.omp.json"
+        "prompt",
+        "init",
+        "cmd",
+        "--config=${workspaceRoot}/themes/jandedobbeleer.omp.json",
+        "--print"
       ]
     },
     {
@@ -77,8 +89,9 @@
       "mode": "debug",
       "program": "${workspaceRoot}/src",
       "args": [
-        "--export-png",
-        "--shell=shell",
+        "config",
+        "export",
+        "png",
         "--rprompt-offset=40",
         "--cursor-padding=15",
         "--config=${workspaceRoot}/themes/jandedobbeleer.omp.json"
@@ -91,8 +104,9 @@
       "mode": "debug",
       "program": "${workspaceRoot}/src",
       "args": [
+        "config",
+        "migrate",
         "--config=${workspaceRoot}/themes/jandedobbeleer.omp.json",
-        "--migrate"
       ]
     },
     {
diff --git a/docs/docs/config-line-error.md b/docs/docs/config-line-error.md
index 86ef3a34..5d4e4062 100644
--- a/docs/docs/config-line-error.md
+++ b/docs/docs/config-line-error.md
@@ -67,7 +67,7 @@ properties below - defaults to ` `
 Invoke Oh My Posh in your `$PROFILE` and add the following line below.
 
 ```powershell
-oh-my-posh --init --shell pwsh --config $env:POSH_THEMES_PATH/jandedobbeleer.omp.json | Invoke-Expression
+oh-my-posh prompt init pwsh --config $env:POSH_THEMES_PATH/jandedobbeleer.omp.json | Invoke-Expression
 // highlight-start
 Enable-PoshLineError
 // highlight-end
diff --git a/docs/docs/config-tooltips.mdx b/docs/docs/config-tooltips.mdx
index 44066c9d..bf6d964b 100644
--- a/docs/docs/config-tooltips.mdx
+++ b/docs/docs/config-tooltips.mdx
@@ -75,7 +75,7 @@ For example:
 
 ```powershell
 # $PROFILE
-oh-my-posh --init --shell pwsh --config ~\wildertheme.json | Invoke-Expression
+oh-my-posh prompt init pwsh --config ~\wildertheme.json | Invoke-Expression
 Enable-PoshTooltips
 ```
 
diff --git a/docs/docs/config-transient.mdx b/docs/docs/config-transient.mdx
index 7630482d..b2889ea0 100644
--- a/docs/docs/config-transient.mdx
+++ b/docs/docs/config-transient.mdx
@@ -76,7 +76,7 @@ properties below - defaults to `{{ .Shell }}> `
 Invoke Oh My Posh in your `$PROFILE` and add the following line below.
 
 ```powershell
-oh-my-posh --init --shell pwsh --config $env:POSH_THEMES_PATH/jandedobbeleer.omp.json | Invoke-Expression
+oh-my-posh prompt init pwsh --config $env:POSH_THEMES_PATH/jandedobbeleer.omp.json | Invoke-Expression
 // highlight-start
 Enable-PoshTransientPrompt
 // highlight-end
diff --git a/docs/docs/install-customize.mdx b/docs/docs/install-customize.mdx
index 0708c816..d8e3cab6 100644
--- a/docs/docs/install-customize.mdx
+++ b/docs/docs/install-customize.mdx
@@ -47,7 +47,7 @@ Export-PoshTheme -FilePath "~/.mytheme.omp.json" -Format json
 Once you're done editing, adjust your `$PROFILE` to use your newly created theme.
 
 ```powershell
-oh-my-posh --init --shell pwsh --config "~/.mytheme.omp.json" | Invoke-Expression
+oh-my-posh prompt init pwsh --config "~/.mytheme.omp.json" | Invoke-Expression
 ```
 
 </TabItem>
@@ -60,7 +60,7 @@ export_poshconfig "~/.mytheme.omp.json" json
 Once you're done editing, adjust `~/.zshrc` to use your newly created theme.
 
 ```bash
-eval "$(oh-my-posh --init --shell zsh --config '~/.mytheme.omp.json')"
+eval "$(oh-my-posh prompt init zsh --config '~/.mytheme.omp.json')"
 ```
 
 When adjusted, reload your profile for the changes to take effect.
@@ -79,7 +79,7 @@ export_poshconfig "~/.mytheme.omp.json" json
 Once you're done editing, adjust `~/.bashrc` (could be `~/.profile` or `~/.bash_profile` depending on your environment) to use your newly created theme.
 
 ```bash
-eval "$(oh-my-posh --init --shell bash --config '~/.mytheme.omp.json')"
+eval "$(oh-my-posh prompt init bash --config '~/.mytheme.omp.json')"
 ```
 
 When adjusted, reload your profile for the changes to take effect.
@@ -98,7 +98,7 @@ export_poshconfig "~/.mytheme.omp.json" json
 Once you're done editing, adjust `config.fish` to use your newly created theme.
 
 ```bash
-oh-my-posh --init --shell fish --config '~/.mytheme.omp.json' | source
+oh-my-posh prompt init fish --config '~/.mytheme.omp.json' | source
 ```
 
 Once adjusted, reload your config for the changes to take effect.
diff --git a/docs/docs/install-prompt.mdx b/docs/docs/install-prompt.mdx
index 1da7c6ad..9a56b446 100644
--- a/docs/docs/install-prompt.mdx
+++ b/docs/docs/install-prompt.mdx
@@ -12,7 +12,7 @@ If you have no idea which shell you're currently using, Oh My Posh has a utility
 :::
 
 ```bash
-oh-my-posh --print-shell
+oh-my-posh config get shell
 ```
 
 <Tabs
@@ -38,7 +38,7 @@ notepad $PROFILE
 Then add the following line.
 
 ```powershell
-oh-my-posh --init --shell pwsh --config https://raw.githubusercontent.com/JanDeDobbeleer/oh-my-posh/v$(oh-my-posh --version)/themes/jandedobbeleer.omp.json | Invoke-Expression
+oh-my-posh prompt init pwsh --config https://raw.githubusercontent.com/JanDeDobbeleer/oh-my-posh/v$(oh-my-posh --version)/themes/jandedobbeleer.omp.json | Invoke-Expression
 ```
 
 Once added, reload your profile for the changes to take effect.
@@ -71,7 +71,7 @@ Use the full path to the config file, not the relative path.
 :::
 
 ```lua title="oh-my-posh.lua"
-load(io.popen('oh-my-posh --config="https://raw.githubusercontent.com/JanDeDobbeleer/oh-my-posh/main/themes/jandedobbeleer.omp.json" --init --shell cmd'):read("*a"))()
+load(io.popen('oh-my-posh prompt init cmd --config="https://raw.githubusercontent.com/JanDeDobbeleer/oh-my-posh/main/themes/jandedobbeleer.omp.json"'):read("*a"))()
 ```
 
 Once added, restart cmd for the changes to take effect.
@@ -82,7 +82,7 @@ Once added, restart cmd for the changes to take effect.
 Add the following to `~/.zshrc`:
 
 ```bash
-eval "$(oh-my-posh --init --shell zsh --config https://raw.githubusercontent.com/JanDeDobbeleer/oh-my-posh/v$(oh-my-posh --version)/themes/jandedobbeleer.omp.json)"
+eval "$(oh-my-posh prompt init zsh --config https://raw.githubusercontent.com/JanDeDobbeleer/oh-my-posh/v$(oh-my-posh --version)/themes/jandedobbeleer.omp.json)"
 ```
 
 Once added, reload your profile for the changes to take effect.
@@ -97,7 +97,7 @@ source ~/.zshrc
 Add the following to `~/.bashrc` (could be `~/.profile` or `~/.bash_profile` depending on your environment):
 
 ```bash
-eval "$(oh-my-posh --init --shell bash --config https://raw.githubusercontent.com/JanDeDobbeleer/oh-my-posh/v$(oh-my-posh --version)/themes/jandedobbeleer.omp.json)"
+eval "$(oh-my-posh prompt init bash --config https://raw.githubusercontent.com/JanDeDobbeleer/oh-my-posh/v$(oh-my-posh --version)/themes/jandedobbeleer.omp.json)"
 ```
 
 Once added, reload your profile for the changes to take effect.
@@ -122,7 +122,7 @@ It's advised to be on the latest version of fish. Versions below 3.1.2 have issu
 Initialize Oh My Posh in `~/.config/fish/config.fish`:
 
 ```bash
-oh-my-posh --init --shell fish --config https://raw.githubusercontent.com/JanDeDobbeleer/oh-my-posh/v(oh-my-posh --version)/themes/jandedobbeleer.omp.json | source
+oh-my-posh prompt init fish --config https://raw.githubusercontent.com/JanDeDobbeleer/oh-my-posh/v(oh-my-posh --version)/themes/jandedobbeleer.omp.json | source
 ```
 
 Once added, reload your config for the changes to take effect.
@@ -139,13 +139,13 @@ Set the prompt and restart nu shell:
 **Nu < 0.32.0**
 
 ```bash
-config set prompt  "= `{{$(oh-my-posh --config https://raw.githubusercontent.com/JanDeDobbeleer/oh-my-posh/main/themes/jandedobbeleer.omp.json | str collect)}}`"
+config set prompt  "= `{{$(oh-my-posh prompt print primary --config https://raw.githubusercontent.com/JanDeDobbeleer/oh-my-posh/main/themes/jandedobbeleer.omp.json | str collect)}}`"
 ```
 
 **Nu >= 0.32.0**
 
 ```bash
-config set prompt  "(oh-my-posh --config https://raw.githubusercontent.com/JanDeDobbeleer/oh-my-posh/main/themes/jandedobbeleer.omp.json | str collect)"
+config set prompt  "(oh-my-posh prompt print primary --config https://raw.githubusercontent.com/JanDeDobbeleer/oh-my-posh/main/themes/jandedobbeleer.omp.json | str collect)"
 ```
 
 Restart nu shell for the changes to take effect.
diff --git a/docs/docs/share-theme.md b/docs/docs/share-theme.md
new file mode 100644
index 00000000..c93a5037
--- /dev/null
+++ b/docs/docs/share-theme.md
@@ -0,0 +1,29 @@
+---
+id: share
+title: Share theme
+sidebar_label: 📸 Share theme
+---
+
+You can export your prompt to an image which you can share online. You have the ability to align
+it correctly and add your name for credits too.
+
+:::warning
+Some glyphs aren't rendered correctly, that's not you but the limitations of the renderer.
+Depending on your config, you might have to tweak the output a little bit.
+:::
+
+The oh-my-posh executable has the `config export image` command to export your current theme configuration
+to the current directory.
+
+```powershell
+oh-my-posh config export image --cursor-padding 50
+```
+
+There are a couple of additional switches you can use to tweak the image rendering:
+
+- `--cursor-padding`: spaces to add after the cursor indication (`_`)
+- `--rprompt-offset`: spaces to add **before** a block that's right aligned
+- `--author`: the name of the creator, added after `ohmyposh.dev`
+- `--background-color`: the hex background color to use (e.g. `#222222`)
+
+For all options, and additional examples, use `oh-my-posh config export image --help`
diff --git a/docs/docs/share-theme.mdx b/docs/docs/share-theme.mdx
deleted file mode 100644
index 3207a401..00000000
--- a/docs/docs/share-theme.mdx
+++ /dev/null
@@ -1,56 +0,0 @@
----
-id: share
-title: Share theme
-sidebar_label: 📸 Share theme
----
-
-import Tabs from "@theme/Tabs";
-import TabItem from "@theme/TabItem";
-
-You can export your prompt to an image which you can share online. You have the ability to align it correctly and add your name for credits too.
-
-:::warning
-Some glyphs aren't rendered correctly, that's not you but the limitations of the renderer. Depending on your config, you might have to tweak
-the output a little bit.
-:::
-
-<Tabs
-  defaultValue="powershell"
-  values={[
-    { label: 'powershell', value: 'powershell', },
-    { label: 'others', value: 'others', },
-  ]
-}>
-<TabItem value="powershell">
-
-You can make use of the `Export-PoshImage` function to export your current theme configuration to the current directory.
-
-```powershell
-Export-PoshImage -CursorPadding 50
-```
-
-There are a couple of parameters you can use to tweak the image rendering:
-
-- `CursorPadding`: spaces to add after the cursor indication (`_`)
-- `RPromptOffset`: spaces to add **before** a block that's right aligned
-- `Author`: the name of the creator, added after `https://ohmyposh.dev`
-- `BGColor`: the hex background color to use (e.g. `#222222`)
-
-</TabItem>
-<TabItem value="others">
-
-The oh-my-posh executable has the `--export-png` switch to export your current theme configuration to the current directory.
-
-```powershell
-oh-my-posh --shell shell --config ~/.mytheme.omp.json --export-png --cursor-padding 50
-```
-
-There are a couple of additional switches you can use to tweak the image rendering:
-
-- `--cursor-padding`: spaces to add after the cursor indication (`_`)
-- `--rprompt-offset`: spaces to add **before** a block that's right aligned
-- `--author`: the name of the creator, added after `https://ohmyposh.dev`
-- `--bg-color`: the hex background color to use (e.g. `#222222`)
-
-</TabItem>
-</Tabs>
diff --git a/docs/docs/themes.md b/docs/docs/themes.md
index ec103227..114d5bcb 100644
--- a/docs/docs/themes.md
+++ b/docs/docs/themes.md
@@ -12,12 +12,12 @@ display every available theme using the following PowerShell cmdlet.
 Get-PoshThemes
 ```
 
-Once you're ready to swap to a theme, follow the steps described in [🚀Installation/Replace your existing prompt][replace-you-existing-prompt].
+Once you're ready to swap to a theme, follow the steps described in [🚀 Replace your existing prompt][replace-you-existing-prompt].
 
 Themes with `minimal` in their names do not require a Nerd Font. Read about [🆎Fonts][fonts] for more information.
 
 [themes]: https://github.com/JanDeDobbeleer/oh-my-posh/tree/main/themes
 [fonts]: /docs/config-fonts
-[replace-you-existing-prompt]: /docs/windows#override-the-theme-settings
+[replace-you-existing-prompt]: /docs/prompt
 
 <!-- Do not change the content below, themes are rendered automatically -->
diff --git a/docs/export_themes.js b/docs/export_themes.js
index 679f74e6..44ee4d4b 100644
--- a/docs/export_themes.js
+++ b/docs/export_themes.js
@@ -72,10 +72,10 @@ themeConfigOverrrides.set('zash.omp.json', newThemeConfig(40, 40));
       config = themeConfigOverrrides.get(theme);
     }
 
-    let poshCommand = `oh-my-posh --config=${configPath} --shell shell --export-png`;
+    let poshCommand = `oh-my-posh config export image --config=${configPath}`;
     poshCommand += ` --rprompt-offset=${config.rpromptOffset}`;
     poshCommand += ` --cursor-padding=${config.cursorPadding}`;
-    poshCommand += ` --bg-color=${config.bgColor}`;
+    poshCommand += ` --background-color=${config.bgColor}`;
     if (config.author !== '') {
       poshCommand += ` --author="${config.author}"`;
     }
diff --git a/packages/powershell/oh-my-posh.psm1 b/packages/powershell/oh-my-posh.psm1
index 591641dc..db631647 100644
--- a/packages/powershell/oh-my-posh.psm1
+++ b/packages/powershell/oh-my-posh.psm1
@@ -185,5 +185,5 @@ function Set-PoshPrompt {
     $global:omp_global_sessionstate = $PSCmdlet.SessionState
 
     $poshCommand = Get-PoshCommand
-    (& $poshCommand --init --shell=pwsh --config="$config") | Invoke-Expression
+    (& $poshCommand prompt init pwsh --config="$config") | Invoke-Expression
 }
diff --git a/src/cmd/config.go b/src/cmd/config.go
new file mode 100644
index 00000000..79d1107e
--- /dev/null
+++ b/src/cmd/config.go
@@ -0,0 +1,22 @@
+/*
+Copyright © 2022 NAME HERE <EMAIL ADDRESS>
+
+*/
+package cmd
+
+import (
+	"github.com/spf13/cobra"
+)
+
+// configCmd represents the config command
+var configCmd = &cobra.Command{
+	Use:   "config [export|migrate|get]",
+	Short: "Interact with the configuration",
+	Long: `Interact with the configuration
+It allows to export, migrate or get a configuration value.`,
+	Run: func(cmd *cobra.Command, args []string) {},
+}
+
+func init() { // nolint:gochecknoinits
+	rootCmd.AddCommand(configCmd)
+}
diff --git a/src/cmd/config_export.go b/src/cmd/config_export.go
new file mode 100644
index 00000000..7dccbec5
--- /dev/null
+++ b/src/cmd/config_export.go
@@ -0,0 +1,57 @@
+/*
+Copyright © 2022 NAME HERE <EMAIL ADDRESS>
+
+*/
+package cmd
+
+import (
+	"fmt"
+	"oh-my-posh/engine"
+	"oh-my-posh/environment"
+
+	"github.com/spf13/cobra"
+)
+
+// exportCmd represents the export command
+var exportCmd = &cobra.Command{
+	Use:   "export",
+	Short: "Export your configuration",
+	Long: `Export your configuration
+You can choose to print the output to stdout, or export your configuration in the format of you choice.
+
+Example usage
+
+> oh-my-posh config export --config ~/myconfig.omp.json
+
+Exports the ~/myconfig.omp.json config file and prints the result to stdout.
+
+> oh-my-posh config export --config ~/myconfig.omp.json --format toml
+
+Exports the ~/myconfig.omp.json config file to toml and prints the result to stdout.
+
+> oh-my-posh config export --config ~/myconfig.omp.json --format toml --write
+
+Exports the  ~/myconfig.omp.json config file to toml and writes the result to your config file.
+A backup of the current config can be found at ~/myconfig.omp.json.bak.`,
+	Run: func(cmd *cobra.Command, args []string) {
+		env := &environment.ShellEnvironment{
+			CmdFlags: &environment.Flags{
+				Config: config,
+			},
+		}
+		env.Init(false)
+		defer env.Close()
+		cfg := engine.LoadConfig(env)
+		if write {
+			cfg.Write()
+			return
+		}
+		fmt.Print(cfg.Export(format))
+	},
+}
+
+func init() { // nolint:gochecknoinits
+	exportCmd.Flags().BoolVarP(&write, "write", "w", false, "write the migrated configuration back to the config file")
+	exportCmd.Flags().StringVarP(&format, "format", "f", "json", "configuration format to migrate to")
+	configCmd.AddCommand(exportCmd)
+}
diff --git a/src/cmd/config_export_image.go b/src/cmd/config_export_image.go
new file mode 100644
index 00000000..23c1f822
--- /dev/null
+++ b/src/cmd/config_export_image.go
@@ -0,0 +1,96 @@
+/*
+Copyright © 2022 NAME HERE <EMAIL ADDRESS>
+
+*/
+package cmd
+
+import (
+	"fmt"
+	"oh-my-posh/color"
+	"oh-my-posh/console"
+	"oh-my-posh/engine"
+	"oh-my-posh/environment"
+
+	"github.com/spf13/cobra"
+)
+
+var (
+	author        string
+	cursorPadding int
+	rPromptOffset int
+	bgColor       string
+)
+
+// imageCmd represents the image command
+var imageCmd = &cobra.Command{
+	Use:   "image",
+	Short: "Export your config to an image",
+	Long: `Export your config to an image
+
+> oh-my-posh config export image --config ~/myconfig.omp.json
+
+Exports the configuration to an image file called ~/myconfig.png.
+
+> oh-my-posh config export image --config ~/myconfig.omp.json --author "John Doe"
+
+You can tweak the output by using additional flags:
+
+- author: displays the author below the prompt
+- cursor-padding: the padding of the prompt cursor
+- rprompt-offset: the offset of the right prompt
+- background-color: the background color of the image`,
+	Run: func(cmd *cobra.Command, args []string) {
+		env := &environment.ShellEnvironment{
+			CmdFlags: &environment.Flags{
+				Config: config,
+				Shell:  "shell",
+			},
+		}
+		env.Init(false)
+		defer env.Close()
+		cfg := engine.LoadConfig(env)
+		ansi := &color.Ansi{}
+		ansi.Init(env.Shell())
+		writerColors := cfg.MakeColors(env)
+		writer := &color.AnsiWriter{
+			Ansi:               ansi,
+			TerminalBackground: engine.GetConsoleBackgroundColor(env, cfg.TerminalBackground),
+			AnsiColors:         writerColors,
+		}
+		consoleTitle := &console.Title{
+			Env:      env,
+			Ansi:     ansi,
+			Template: cfg.ConsoleTitleTemplate,
+			Style:    cfg.ConsoleTitleStyle,
+		}
+		eng := &engine.Engine{
+			Config:       cfg,
+			Env:          env,
+			Writer:       writer,
+			ConsoleTitle: consoleTitle,
+			Ansi:         ansi,
+		}
+		prompt := eng.PrintPrimary()
+		imageCreator := &engine.ImageRenderer{
+			AnsiString:    prompt,
+			Author:        author,
+			CursorPadding: cursorPadding,
+			RPromptOffset: rPromptOffset,
+			BgColor:       bgColor,
+			Ansi:          ansi,
+		}
+		imageCreator.Init(env.Flags().Config)
+		err := imageCreator.SavePNG()
+		if err != nil {
+			fmt.Print(err.Error())
+		}
+	},
+}
+
+func init() { // nolint:gochecknoinits
+	imageCmd.Flags().StringVar(&author, "author", "", "config author")
+	imageCmd.Flags().StringVar(&bgColor, "background-color", "", "image background color")
+	imageCmd.Flags().IntVar(&cursorPadding, "cursor-padding", 0, "prompt cursor padding")
+	imageCmd.Flags().IntVar(&rPromptOffset, "rprompt-offset", 0, "right prompt offset")
+	exportCmd.AddCommand(imageCmd)
+}
diff --git a/src/cmd/config_get.go b/src/cmd/config_get.go
new file mode 100644
index 00000000..47bee7eb
--- /dev/null
+++ b/src/cmd/config_get.go
@@ -0,0 +1,48 @@
+/*
+Copyright © 2022 NAME HERE <EMAIL ADDRESS>
+
+*/
+package cmd
+
+import (
+	"fmt"
+	"oh-my-posh/environment"
+	"time"
+
+	"github.com/spf13/cobra"
+)
+
+// getCmd represents the get command
+var getCmd = &cobra.Command{
+	Use:   "get [shell|cache-path|millies]",
+	Short: "Get a value from the oh-my-posh configuration",
+	Long: `Get a value from the oh-my-posh configuration.
+This command is used to get the value of the following variables:
+
+- shell
+- cache-path
+- millis`,
+	ValidArgs: []string{
+		"millis",
+		"shell",
+		"cache-path",
+	},
+	Args: cobra.OnlyValidArgs,
+	Run: func(cmd *cobra.Command, args []string) {
+		env := &environment.ShellEnvironment{}
+		env.Init(false)
+		defer env.Close()
+		switch args[0] {
+		case "millis":
+			fmt.Print(time.Now().UnixNano() / 1000000)
+		case "shell":
+			fmt.Println(env.Shell())
+		case "cache-path":
+			fmt.Print(env.CachePath())
+		}
+	},
+}
+
+func init() { // nolint:gochecknoinits
+	configCmd.AddCommand(getCmd)
+}
diff --git a/src/cmd/config_migrate.go b/src/cmd/config_migrate.go
new file mode 100644
index 00000000..00bc9227
--- /dev/null
+++ b/src/cmd/config_migrate.go
@@ -0,0 +1,63 @@
+/*
+Copyright © 2022 NAME HERE <EMAIL ADDRESS>
+
+*/
+package cmd
+
+import (
+	"fmt"
+	"oh-my-posh/engine"
+	"oh-my-posh/environment"
+
+	"github.com/spf13/cobra"
+)
+
+var (
+	write  bool
+	format string
+)
+
+// migrateCmd represents the migrate command
+var migrateCmd = &cobra.Command{
+	Use:   "migrate",
+	Short: "Migrate your configuration",
+	Long: `Migrate your configuration
+You can choose to print the output to stdout, or migrate your configuration in the format of you choice.
+
+Example usage
+
+> oh-my-posh config migrate --config ~/myconfig.omp.json
+
+Migrates the ~/myconfig.omp.json config file and prints the result to stdout.
+
+> oh-my-posh config migrate --config ~/myconfig.omp.json --format toml
+
+Migrates the ~/myconfig.omp.json config file to toml and prints the result to stdout.
+
+> oh-my-posh config migrate --config ~/myconfig.omp.json --format toml --write
+
+Migrates the  ~/myconfig.omp.json config file to toml and writes the result to your config file.
+A backup of the current config can be found at ~/myconfig.omp.json.bak.`,
+	Run: func(cmd *cobra.Command, args []string) {
+		env := &environment.ShellEnvironment{
+			CmdFlags: &environment.Flags{
+				Config: config,
+			},
+		}
+		env.Init(false)
+		defer env.Close()
+		cfg := engine.LoadConfig(env)
+		if write {
+			cfg.BackupAndMigrate(env)
+			return
+		}
+		cfg.Migrate(env)
+		fmt.Print(cfg.Export(format))
+	},
+}
+
+func init() { //nolint:gochecknoinits
+	migrateCmd.Flags().BoolVarP(&write, "write", "w", false, "write the migrated configuration back to the config file")
+	migrateCmd.Flags().StringVarP(&format, "format", "f", "json", "the configuration format to migrate to")
+	configCmd.AddCommand(migrateCmd)
+}
diff --git a/src/cmd/prompt.go b/src/cmd/prompt.go
new file mode 100644
index 00000000..e1d220bb
--- /dev/null
+++ b/src/cmd/prompt.go
@@ -0,0 +1,22 @@
+/*
+Copyright © 2022 NAME HERE <EMAIL ADDRESS>
+
+*/
+package cmd
+
+import (
+	"github.com/spf13/cobra"
+)
+
+// promptCmd represents the prompt command
+var promptCmd = &cobra.Command{
+	Use:   "prompt",
+	Short: "Setup the prompt for your shell",
+	Long: `Setup the prompt for your shell
+Allows to initialize one of the supported shells, or to set the prompt manually for a custom shell.`,
+	Run: func(cmd *cobra.Command, args []string) {},
+}
+
+func init() { // nolint:gochecknoinits
+	rootCmd.AddCommand(promptCmd)
+}
diff --git a/src/cmd/prompt_init.go b/src/cmd/prompt_init.go
new file mode 100644
index 00000000..34d860c0
--- /dev/null
+++ b/src/cmd/prompt_init.go
@@ -0,0 +1,60 @@
+/*
+Copyright © 2022 NAME HERE <EMAIL ADDRESS>
+
+*/
+package cmd
+
+import (
+	"fmt"
+	"oh-my-posh/engine"
+	"oh-my-posh/environment"
+
+	"github.com/spf13/cobra"
+)
+
+var (
+	print bool
+
+	initCmd = &cobra.Command{
+		Use:   "init [bash|zsh|fish|powershell|pwsh|cmd] --config ~/.mytheme.omp.json",
+		Short: "Initialize your shell and configuration",
+		Long: `Allows to initialize your shell and configuration.
+See the documentation to initialize your shell: https://ohmyposh.dev/docs/prompt.`,
+		ValidArgs: []string{
+			"bash",
+			"zsh",
+			"fish",
+			"powershell",
+			"pwsh",
+			"cmd",
+		},
+		Args: cobra.OnlyValidArgs,
+		Run: func(cmd *cobra.Command, args []string) {
+			runInit(args[0])
+		},
+	}
+)
+
+func init() { // nolint:gochecknoinits
+	initCmd.Flags().BoolVarP(&print, "print", "p", false, "print the init script")
+	_ = initCmd.MarkPersistentFlagRequired("config")
+	promptCmd.AddCommand(initCmd)
+}
+
+func runInit(shell string) {
+	env := &environment.ShellEnvironment{
+		CmdFlags: &environment.Flags{
+			Shell:  shell,
+			Config: config,
+		},
+	}
+	env.Init(false)
+	defer env.Close()
+	if print {
+		init := engine.PrintShellInit(env)
+		fmt.Print(init)
+		return
+	}
+	init := engine.InitShell(env)
+	fmt.Print(init)
+}
diff --git a/src/cmd/prompt_print.go b/src/cmd/prompt_print.go
new file mode 100644
index 00000000..8bb2fb8f
--- /dev/null
+++ b/src/cmd/prompt_print.go
@@ -0,0 +1,124 @@
+/*
+Copyright © 2022 NAME HERE <EMAIL ADDRESS>
+
+*/
+package cmd
+
+import (
+	"fmt"
+	"oh-my-posh/color"
+	"oh-my-posh/console"
+	"oh-my-posh/engine"
+	"oh-my-posh/environment"
+
+	"github.com/spf13/cobra"
+)
+
+var (
+	pwd           string
+	pswd          string
+	exitCode      int
+	timing        float64
+	stackCount    int
+	terminalWidth int
+	eval          bool
+
+	command string
+	plain   bool
+)
+
+// printCmd represents the prompt command
+var printCmd = &cobra.Command{
+	Use:   "print [debug|primary|secondary|transient|right|tooltip|valid|error]",
+	Short: "Print the prompt/context",
+	Long:  "Print one of the prompts based on the location/use-case.",
+	ValidArgs: []string{
+		"debug",
+		"primary",
+		"secondary",
+		"transient",
+		"right",
+		"tooltip",
+		"valid",
+		"error",
+	},
+	Args: cobra.OnlyValidArgs,
+	Run: func(cmd *cobra.Command, args []string) {
+		env := &environment.ShellEnvironment{
+			CmdFlags: &environment.Flags{
+				Config:        config,
+				PWD:           pwd,
+				PSWD:          pswd,
+				ErrorCode:     exitCode,
+				ExecutionTime: timing,
+				StackCount:    stackCount,
+				TerminalWidth: terminalWidth,
+				Eval:          eval,
+				Shell:         shell,
+			},
+		}
+		debug := args[0] == "debug"
+		env.Init(debug)
+		defer env.Close()
+		cfg := engine.LoadConfig(env)
+		ansi := &color.Ansi{}
+		ansi.Init(env.Shell())
+		var writer color.Writer
+		if plain {
+			writer = &color.PlainWriter{}
+		} else {
+			writerColors := cfg.MakeColors(env)
+			writer = &color.AnsiWriter{
+				Ansi:               ansi,
+				TerminalBackground: engine.GetConsoleBackgroundColor(env, cfg.TerminalBackground),
+				AnsiColors:         writerColors,
+			}
+		}
+		consoleTitle := &console.Title{
+			Env:      env,
+			Ansi:     ansi,
+			Template: cfg.ConsoleTitleTemplate,
+			Style:    cfg.ConsoleTitleStyle,
+		}
+		eng := &engine.Engine{
+			Config:       cfg,
+			Env:          env,
+			Writer:       writer,
+			ConsoleTitle: consoleTitle,
+			Ansi:         ansi,
+			Plain:        plain,
+		}
+		switch args[0] {
+		case "debug":
+			fmt.Print(eng.PrintDebug(Version))
+		case "primary":
+			fmt.Print(eng.PrintPrimary())
+		case "secondary":
+			fmt.Print(eng.PrintExtraPrompt(engine.Secondary))
+		case "transient":
+			fmt.Print(eng.PrintExtraPrompt(engine.Transient))
+		case "right":
+			fmt.Print(eng.PrintRPrompt())
+		case "tooltip":
+			fmt.Print(eng.PrintTooltip(command))
+		case "valid":
+			fmt.Print(eng.PrintExtraPrompt(engine.Valid))
+		case "error":
+			fmt.Print(eng.PrintExtraPrompt(engine.Error))
+		}
+	},
+}
+
+func init() { // nolint:gochecknoinits
+	printCmd.Flags().StringVar(&pwd, "pwd", "", "current working directory")
+	printCmd.Flags().StringVar(&pswd, "pswd", "", "current working directory (according to pwsh)")
+	printCmd.Flags().StringVar(&shell, "shell", "", "the shell to print for")
+	printCmd.Flags().IntVarP(&exitCode, "exit", "e", 0, "last exit code")
+	printCmd.Flags().Float64VarP(&timing, "timing", "t", 0, "timing of the last command")
+	printCmd.Flags().IntVarP(&stackCount, "stack-count", "s", 0, "number of locations on the stack")
+	printCmd.Flags().IntVarP(&terminalWidth, "terminal-width", "w", 0, "width of the terminal")
+	printCmd.Flags().StringVar(&command, "command", "", "tooltip command")
+	printCmd.Flags().BoolVarP(&plain, "plain", "p", false, "plain text output (no ANSI)")
+	printCmd.Flags().BoolVar(&eval, "eval", false, "output the prompt for eval")
+	promptCmd.AddCommand(printCmd)
+}
diff --git a/src/cmd/root.go b/src/cmd/root.go
new file mode 100644
index 00000000..06980f0e
--- /dev/null
+++ b/src/cmd/root.go
@@ -0,0 +1,49 @@
+package cmd
+
+import (
+	"fmt"
+	"os"
+
+	"github.com/spf13/cobra"
+)
+
+// Version number of oh-my-posh
+var (
+	Version = "development"
+	config  string
+)
+
+var rootCmd = &cobra.Command{
+	Use:   "oh-my-posh",
+	Short: "oh-my-posh is a tool to render your prompt",
+	Long: `oh-my-posh is a cross platform tool to render your prompt.
+It can use the same configuration everywhere to offer a consistent
+experience, regardless of where you are. For a detailed guide
+on getting started, have a look at the docs at https://ohmyposh.dev`,
+	Run: func(cmd *cobra.Command, args []string) {
+		if initialize {
+			runInit(shell)
+			return
+		}
+		fmt.Println(Version)
+	},
+}
+
+func Execute() {
+	if err := rootCmd.Execute(); err != nil {
+		fmt.Fprintln(os.Stderr, err)
+		os.Exit(1)
+	}
+}
+
+// Backwards compatibility
+var (
+	shell      string
+	initialize bool
+)
+
+func init() { // nolint:gochecknoinits
+	rootCmd.PersistentFlags().StringVarP(&config, "config", "c", "", "config (required)")
+	rootCmd.Flags().BoolVarP(&initialize, "init", "i", false, "init (deprecated)")
+	rootCmd.Flags().StringVarP(&shell, "shell", "s", "", "shell (deprecated)")
+}
diff --git a/src/cmd/version.go b/src/cmd/version.go
new file mode 100644
index 00000000..0a5dfa13
--- /dev/null
+++ b/src/cmd/version.go
@@ -0,0 +1,25 @@
+/*
+Copyright © 2022 NAME HERE <EMAIL ADDRESS>
+
+*/
+package cmd
+
+import (
+	"fmt"
+
+	"github.com/spf13/cobra"
+)
+
+// versionCmd represents the version command
+var versionCmd = &cobra.Command{
+	Use:   "version",
+	Short: "Print the version",
+	Long:  "Print oh-my-posh version and build information.",
+	Run: func(cmd *cobra.Command, args []string) {
+		fmt.Println(Version)
+	},
+}
+
+func init() { // nolint:gochecknoinits
+	rootCmd.AddCommand(versionCmd)
+}
diff --git a/src/engine/config.go b/src/engine/config.go
index 5cd53dde..d3535752 100644
--- a/src/engine/config.go
+++ b/src/engine/config.go
@@ -85,7 +85,7 @@ func (cfg *Config) exitWithError(err error) {
 func LoadConfig(env environment.Environment) *Config {
 	cfg := loadConfig(env)
 	// only migrate automatically when the switch isn't set
-	if !*env.Args().Migrate && cfg.Version != configVersion {
+	if !env.Flags().Migrate && cfg.Version != configVersion {
 		cfg.BackupAndMigrate(env)
 	}
 	return cfg
@@ -93,8 +93,7 @@ func LoadConfig(env environment.Environment) *Config {
 
 func loadConfig(env environment.Environment) *Config {
 	var cfg Config
-	configFile := *env.Args().Config
-	cfg.eval = *env.Args().Eval
+	configFile := env.Flags().Config
 	if configFile == "" {
 		return defaultConfig()
 	}
@@ -182,11 +181,11 @@ func (cfg *Config) Export(format string) string {
 func (cfg *Config) BackupAndMigrate(env environment.Environment) {
 	origin := cfg.backup()
 	cfg.Migrate(env)
-	cfg.write()
+	cfg.Write()
 	cfg.print(fmt.Sprintf("\nOh My Posh config migrated to version %d\nBackup config available at %s\n\n", cfg.Version, origin))
 }
 
-func (cfg *Config) write() {
+func (cfg *Config) Write() {
 	content := cfg.Export(cfg.format)
 	f, err := os.OpenFile(cfg.origin, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0755)
 	cfg.exitWithError(err)
diff --git a/src/engine/engine.go b/src/engine/engine.go
index 2f9f0567..307d0300 100644
--- a/src/engine/engine.go
+++ b/src/engine/engine.go
@@ -56,7 +56,7 @@ func (e *Engine) canWriteRPrompt() bool {
 	return canWrite
 }
 
-func (e *Engine) Render() string {
+func (e *Engine) PrintPrimary() string {
 	for _, block := range e.Config.Blocks {
 		e.renderBlock(block)
 	}
@@ -160,7 +160,7 @@ func (e *Engine) renderBlock(block *Block) {
 }
 
 // debug will loop through your config file and output the timings for each segments
-func (e *Engine) Debug(version string) string {
+func (e *Engine) PrintDebug(version string) string {
 	var segmentTimings []*SegmentTiming
 	largestSegmentNameLength := 0
 	e.write(fmt.Sprintf("\n\x1b[1mVersion:\x1b[0m %s\n", version))
@@ -204,7 +204,7 @@ func (e *Engine) Debug(version string) string {
 func (e *Engine) print() string {
 	switch e.Env.Shell() {
 	case zsh:
-		if !*e.Env.Args().Eval {
+		if !e.Env.Flags().Eval {
 			break
 		}
 		// escape double quotes contained in the prompt
@@ -224,7 +224,7 @@ func (e *Engine) print() string {
 	return e.string()
 }
 
-func (e *Engine) RenderTooltip(tip string) string {
+func (e *Engine) PrintTooltip(tip string) string {
 	tip = strings.Trim(tip, " ")
 	var tooltip *Segment
 	for _, tp := range e.Config.Tooltips {
@@ -275,7 +275,7 @@ const (
 	Secondary
 )
 
-func (e *Engine) RenderExtraPrompt(promptType ExtraPromptType) string {
+func (e *Engine) PrintExtraPrompt(promptType ExtraPromptType) string {
 	var prompt *ExtraPrompt
 	switch promptType {
 	case Transient:
@@ -331,7 +331,7 @@ func (e *Engine) RenderExtraPrompt(promptType ExtraPromptType) string {
 	return ""
 }
 
-func (e *Engine) RenderRPrompt() string {
+func (e *Engine) PrintRPrompt() string {
 	filterRPromptBlock := func(blocks []*Block) *Block {
 		for _, block := range blocks {
 			if block.Type == RPrompt {
diff --git a/src/engine/init.go b/src/engine/init.go
index 841cdadb..9afd54c0 100644
--- a/src/engine/init.go
+++ b/src/engine/init.go
@@ -46,7 +46,7 @@ func getExecutablePath(env environment.Environment) (string, error) {
 	// which uses unix style paths to resolve the executable's location.
 	// PowerShell knows how to resolve both, so we can swap this without any issue.
 	executable = strings.ReplaceAll(executable, "\\", "/")
-	switch *env.Args().Shell {
+	switch env.Flags().Shell {
 	case bash, zsh:
 		executable = strings.ReplaceAll(executable, " ", "\\ ")
 		executable = strings.ReplaceAll(executable, "(", "\\(")
@@ -60,10 +60,10 @@ func InitShell(env environment.Environment) string {
 	if err != nil {
 		return noExe
 	}
-	shell := *env.Args().Shell
+	shell := env.Flags().Shell
 	switch shell {
 	case pwsh, powershell5:
-		return fmt.Sprintf("(@(&\"%s\" --print-init --shell=%s --config=\"%s\") -join \"`n\") | Invoke-Expression", executable, shell, *env.Args().Config)
+		return fmt.Sprintf("(@(&\"%s\" prompt init %s --config=\"%s\" --print) -join \"`n\") | Invoke-Expression", executable, shell, env.Flags().Config)
 	case zsh, bash, fish, winCMD:
 		return PrintShellInit(env)
 	default:
@@ -76,8 +76,8 @@ func PrintShellInit(env environment.Environment) string {
 	if err != nil {
 		return noExe
 	}
-	shell := *env.Args().Shell
-	configFile := *env.Args().Config
+	shell := env.Flags().Shell
+	configFile := env.Flags().Config
 	switch shell {
 	case pwsh, powershell5:
 		return getShellInitScript(executable, configFile, pwshInit)
diff --git a/src/engine/init/omp.bash b/src/engine/init/omp.bash
index 0add537b..357f8f7d 100644
--- a/src/engine/init/omp.bash
+++ b/src/engine/init/omp.bash
@@ -10,9 +10,9 @@ if [[ ! -d "/tmp" ]]; then
 fi
 
 # start timer on command start
-PS0='$(::OMP:: --millis > "$TIMER_START")'
+PS0='$(::OMP:: config get millis > "$TIMER_START")'
 # set secondary prompt
-PS2="$(::OMP:: --config="$POSH_THEME" --shell=bash --print-secondary)"
+PS2="$(::OMP:: prompt print secondary --config="$POSH_THEME" --shell=bash)"
 
 function _omp_hook() {
     local ret=$?
@@ -20,12 +20,12 @@ function _omp_hook() {
     omp_stack_count=$((${#DIRSTACK[@]} - 1))
     omp_elapsed=-1
     if [[ -f "$TIMER_START" ]]; then
-        omp_now=$(::OMP:: --millis)
+        omp_now=$(::OMP:: config get millis)
         omp_start_time=$(cat "$TIMER_START")
         omp_elapsed=$((omp_now-omp_start_time))
         rm -f "$TIMER_START"
     fi
-    PS1="$(::OMP:: --config="$POSH_THEME" --shell=bash --error="$ret" --execution-time="$omp_elapsed" --stack-count="$omp_stack_count" | tr -d '\0')"
+    PS1="$(::OMP:: prompt print primary --config="$POSH_THEME" --shell=bash --exit="$ret" --timing="$omp_elapsed" --stack-count="$omp_stack_count" | tr -d '\0')"
 
     return $ret
 }
@@ -39,12 +39,3 @@ function _omp_runonexit() {
 }
 
 trap _omp_runonexit EXIT
-
-function export_poshconfig() {
-    [ $# -eq 0 ] && { echo "Usage: $0 \"filename\""; return; }
-    format=$2
-    if [ -z "$format" ]; then
-      format="json"
-    fi
-    ::OMP:: --config="$POSH_THEME" --print-config --format="$format" > $1
-}
diff --git a/src/engine/init/omp.fish b/src/engine/init/omp.fish
index 53d7dee3..1d0db7e9 100644
--- a/src/engine/init/omp.fish
+++ b/src/engine/init/omp.fish
@@ -18,11 +18,11 @@ function fish_prompt
       set -gx omp_last_status_generation $status_generation
     end
 
-    ::OMP:: --config $POSH_THEME --shell fish --error $omp_status_cache --execution-time $omp_duration --stack-count $omp_stack_count
+    ::OMP:: prompt print primary --config $POSH_THEME --shell fish --exit $omp_status_cache --timing $omp_duration --stack-count $omp_stack_count
 end
 
 function fish_right_prompt
-    ::OMP:: --config $POSH_THEME --shell fish --error $omp_status_cache --execution-time $omp_duration --stack-count $omp_stack_count --rprompt
+    ::OMP:: prompt print right --config $POSH_THEME --shell fish --exit $omp_status_cache --timing $omp_duration --stack-count $omp_stack_count
 end
 
 function postexec_omp --on-event fish_postexec
@@ -30,17 +30,3 @@ function postexec_omp --on-event fish_postexec
   # pre and postexec not fired for empty command in fish >=3.2
   set -gx omp_lastcommand $argv
 end
-
-
-function export_poshconfig
-  set -l file_name $argv[1]
-  set -l format $argv[2]
-  if not test -n "$file_name"
-    echo "Usage: export_poshconfig \"filename\""
-    return
-  end
-  if not test -n "$format"
-    set format "json"
-  end
-  ::OMP:: --config $POSH_THEME --print-config --format $format > $file_name
-end
diff --git a/src/engine/init/omp.lua b/src/engine/init/omp.lua
index 82c8a4d6..60bda7dc 100644
--- a/src/engine/init/omp.lua
+++ b/src/engine/init/omp.lua
@@ -37,7 +37,7 @@ local function os_clock_millis()
     if (clink.version_encoded or 0) >= 10020030 then
         return math.floor(os.clock() * 1000)
     else
-        local prompt_exe = string.format('%s --millis', omp_exe())
+        local prompt_exe = string.format('%s config get millis', omp_exe())
         return run_posh_command(prompt_exe)
     end
 end
@@ -61,25 +61,29 @@ end
 
 local function execution_time_option()
     if last_duration ~= nil then
-        return "--execution-time "..last_duration
+        return "--timing "..last_duration
     end
     return ""
 end
 
 local function error_level_option()
     if os.geterrorlevel ~= nil and settings.get("cmd.get_errorlevel") then
-        return "--error "..os.geterrorlevel()
+        return "--exit "..os.geterrorlevel()
     end
     return ""
 end
 
 local function get_posh_prompt(rprompt)
-    local prompt_exe = string.format('%s --shell=cmd --config=%s %s %s --rprompt=%s', omp_exe(), omp_config(), execution_time_option(), error_level_option(), rprompt)
+    local prompt = "primary"
+    if rprompt then
+        prompt = "right"
+    end
+    local prompt_exe = string.format('%s prompt print %s --shell=cmd --config=%s %s %s', omp_exe(), prompt, omp_config(), execution_time_option(), error_level_option(), rprompt)
     return run_posh_command(prompt_exe)
 end
 
 local function get_posh_tooltip(command)
-    local prompt_exe = string.format('%s --shell=cmd --config=%s --command="%s"', omp_exe(), omp_config(), command)
+    local prompt_exe = string.format('%s prompt print tooltip --shell=cmd --config=%s --command="%s"', omp_exe(), omp_config(), command)
     local tooltip = run_posh_command(prompt_exe)
     if tooltip == "" then
         -- If no tooltip, generate normal rprompt.
@@ -133,7 +137,7 @@ function p:rightfilter(prompt)
     return cached_prompt.right, false
 end
 function p:transientfilter(prompt)
-    local prompt_exe = string.format('%s --shell=cmd --config=%s --print-transient', omp_exe(), omp_config())
+    local prompt_exe = string.format('%s prompt print transient --config=%s', omp_exe(), omp_config())
     prompt = run_posh_command(prompt_exe)
     if prompt == "" then
         prompt = nil
diff --git a/src/engine/init/omp.ps1 b/src/engine/init/omp.ps1
index 4efd1c59..b588798b 100644
--- a/src/engine/init/omp.ps1
+++ b/src/engine/init/omp.ps1
@@ -32,7 +32,7 @@ Remove-Variable omp_value -Confirm:$false
 Remove-Variable omp_config -Confirm:$false
 
 # set secondary prompt
-$secondaryPrompt = @(&"::OMP::" --config="$Env:POSH_THEME" --print-secondary 2>&1) -join "`n"
+$secondaryPrompt = @(&"::OMP::" prompt print secondary --config="$Env:POSH_THEME" 2>&1) -join "`n"
 Set-PSReadLineOption -ContinuationPrompt $secondaryPrompt
 
 function global:Set-PoshContext {}
@@ -60,7 +60,7 @@ function global:Initialize-ModuleSupport {
     $omp = "::OMP::"
     $config, $cleanPWD, $cleanPSWD = Get-PoshContext
     if ($env:POSH_TRANSIENT -eq $true) {
-        $standardOut = @(&$omp --pwd="$cleanPWD" --pswd="$cleanPSWD" --config="$config" --print-transient 2>&1)
+        $standardOut = @(&$omp prompt print transient --pwd="$cleanPWD" --pswd="$cleanPSWD" --config="$config" 2>&1)
         $standardOut -join "`n"
         $env:POSH_TRANSIENT = $false
         return
@@ -94,7 +94,7 @@ function global:Initialize-ModuleSupport {
         $global:omp_lastHistoryId = $history.Id
     }
     $terminalWidth = $Host.UI.RawUI.WindowSize.Width
-    $standardOut = @(&$omp --error="$errorCode" --pwd="$cleanPWD" --pswd="$cleanPSWD" --execution-time="$executionTime" --stack-count="$stackCount" --config="$config" --terminal-width=$terminalWidth 2>&1)
+    $standardOut = @(&$omp prompt print primary --exit="$errorCode" --pwd="$cleanPWD" --pswd="$cleanPSWD" --timing="$executionTime" --stack-count="$stackCount" --config="$config" --terminal-width=$terminalWidth 2>&1)
     # make sure PSReadLine knows we have a multiline prompt
     $extraLines = $standardOut.Count - 1
     if ($extraLines -gt 0) {
@@ -112,7 +112,7 @@ Set-Item -Path Function:prompt -Value $Prompt -Force
 function global:Write-PoshDebug {
     $omp = "::OMP::"
     $config, $cleanPWD, $cleanPSWD = Get-PoshContext
-    $standardOut = @(&$omp --error=1337 --pwd="$cleanPWD" --pswd="$cleanPSWD" --execution-time=9001 --config="$config" --debug 2>&1)
+    $standardOut = @(&$omp prompt print debug --exit=1337 --pwd="$cleanPWD" --pswd="$cleanPSWD" --timing=9001 --config="$config" 2>&1)
     $standardOut -join "`n"
 }
 
@@ -149,7 +149,7 @@ function global:Export-PoshTheme {
 
     $config = $env:POSH_THEME
     $omp = "::OMP::"
-    $configString = @(&$omp --config="$config" --format="$Format" --print-config 2>&1)
+    $configString = @(&$omp config export --config="$config" --format="$Format" 2>&1)
     # if no path, copy to clipboard by default
     if ($FilePath -ne "") {
         #https://stackoverflow.com/questions/3038337/powershell-resolve-path-that-might-not-exist
@@ -162,35 +162,6 @@ function global:Export-PoshTheme {
     }
 }
 
-function global:Export-PoshImage {
-    param(
-        [Parameter(Mandatory = $false)]
-        [int]
-        $RPromptOffset = 40,
-        [Parameter(Mandatory = $false)]
-        [int]
-        $CursorPadding = 30,
-        [Parameter(Mandatory = $false)]
-        [string]
-        $Author,
-        [Parameter(Mandatory = $false)]
-        [string]
-        $BGColor
-    )
-
-    if ($Author) {
-        $Author = "--author=$Author"
-    }
-    if ($BGColor) {
-        $BGColor = "--bg-color=$BGColor"
-    }
-
-    $omp = "::OMP::"
-    $config, $cleanPWD, $cleanPSWD = Get-PoshContext
-    $standardOut = @(&$omp --shell=shell --config="$config" --pwd="$cleanPWD" --pswd="$cleanPSWD" --export-png --rprompt-offset="$RPromptOffset" --cursor-padding="$CursorPadding" $Author $BGColor 2>&1)
-    $standardOut -join "`n"
-}
-
 function global:Enable-PoshTooltips {
     Set-PSReadlineKeyHandler -Key SpaceBar -ScriptBlock {
         [Microsoft.PowerShell.PSConsoleReadLine]::Insert(' ')
@@ -200,7 +171,7 @@ function global:Enable-PoshTooltips {
         $command = $null
         $cursor = $null
         [Microsoft.PowerShell.PSConsoleReadLine]::GetBufferState([ref]$command, [ref]$cursor)
-        $standardOut = @(&$omp --pwd="$cleanPWD" --pswd="$cleanPSWD" --config="$config" --command="$command" 2>&1)
+        $standardOut = @(&$omp prompt print tooltip --pwd="$cleanPWD" --pswd="$cleanPSWD" --config="$config" --command="$command" 2>&1)
         Write-Host $standardOut -NoNewline
         $host.UI.RawUI.CursorPosition = $position
     }
@@ -216,8 +187,8 @@ function global:Enable-PoshTransientPrompt {
 
 function global:Enable-PoshLineError {
     $omp = "::OMP::"
-    $validLine = @(&$omp --config="$Env:POSH_THEME" --print-valid 2>&1) -join "`n"
-    $errorLine = @(&$omp --config="$Env:POSH_THEME" --print-error 2>&1) -join "`n"
+    $validLine = @(&$omp prompt print valid --config="$Env:POSH_THEME" 2>&1) -join "`n"
+    $errorLine = @(&$omp prompt print error --config="$Env:POSH_THEME" 2>&1) -join "`n"
     Set-PSReadLineOption -PromptText $validLine, $errorLine
 }
 
@@ -284,7 +255,7 @@ function global:Get-PoshThemes() {
         $themes | ForEach-Object -Process {
             Write-Host "Theme: $(Get-Hyperlink -uri $_.fullname -name $_.BaseName.Replace('.omp', ''))"
             Write-Host ""
-            & $omp -config $($_.FullName) -pwd $PWD -shell pwsh
+            & $omp prompt print primary --config $($_.FullName) --pwd $PWD --shell pwsh
             Write-Host ""
             Write-Host ""
         }
@@ -294,6 +265,6 @@ function global:Get-PoshThemes() {
     Write-Host ""
     Write-Host "To change your theme, adjust the init script in $PROFILE."
     Write-Host "Example:"
-    Write-Host "  oh-my-posh --init --shell pwsh --config $Path/jandedobbeleer.omp.json | Invoke-Expression"
+    Write-Host "  oh-my-posh prompt init pwsh --config $Path/jandedobbeleer.omp.json | Invoke-Expression"
     Write-Host ""
 }
diff --git a/src/engine/init/omp.zsh b/src/engine/init/omp.zsh
index 8a9bbfcf..12d595f5 100644
--- a/src/engine/init/omp.zsh
+++ b/src/engine/init/omp.zsh
@@ -3,10 +3,10 @@ export POWERLINE_COMMAND="oh-my-posh"
 export CONDA_PROMPT_MODIFIER=false
 
 # set secondary prompt
-PS2="$(::OMP:: --config="$POSH_THEME" --shell=zsh --print-secondary)"
+PS2="$(::OMP:: prompt print secondary --config="$POSH_THEME" --shell=zsh)"
 
 function prompt_ohmyposh_preexec() {
-  omp_start_time=$(::OMP:: --millis)
+  omp_start_time=$(::OMP:: config get millis)
 }
 
 function prompt_ohmyposh_precmd() {
@@ -14,10 +14,10 @@ function prompt_ohmyposh_precmd() {
   omp_stack_count=${#dirstack[@]}
   omp_elapsed=-1
   if [ $omp_start_time ]; then
-    omp_now=$(::OMP:: --millis)
+    omp_now=$(::OMP:: config get millis)
     omp_elapsed=$(($omp_now-$omp_start_time))
   fi
-  eval "$(::OMP:: --config="$POSH_THEME" --error="$omp_last_error" --execution-time="$omp_elapsed" --stack-count="$omp_stack_count" --eval --shell=zsh)"
+  eval "$(::OMP:: prompt print primary --config="$POSH_THEME" --exit="$omp_last_error" --timing="$omp_elapsed" --stack-count="$omp_stack_count" --eval --shell=zsh)"
   unset omp_start_time
   unset omp_now
   unset omp_elapsed
@@ -45,22 +45,13 @@ if [ "$TERM" != "linux" ]; then
   _install-omp-hooks
 fi
 
-function export_poshconfig() {
-    [ $# -eq 0 ] && { echo "Usage: $0 \"filename\""; return; }
-    format=$2
-    if [ -z "$format" ]; then
-      format="json"
-    fi
-    ::OMP:: --config="$POSH_THEME" --print-config --format="$format" > $1
-}
-
 function self-insert() {
   # ignore an empty buffer
   if [[ -z  "$BUFFER"  ]]; then
     zle .self-insert
     return
   fi
-  tooltip=$(::OMP:: --config="$POSH_THEME" --shell=zsh --command="$BUFFER")
+  tooltip=$(::OMP:: prompt print tooltip --config="$POSH_THEME" --shell=zsh --command="$BUFFER")
   # ignore an empty tooltip
   if [[ ! -z "$tooltip" ]]; then
     RPROMPT=$tooltip
@@ -82,7 +73,7 @@ _posh-zle-line-init() {
     local -i ret=$?
     (( $+zle_bracketed_paste )) && print -r -n - $zle_bracketed_paste[2]
 
-    eval "$(::OMP:: --config="$POSH_THEME" --print-transient --eval --shell=zsh)"
+    eval "$(::OMP:: prompt print transient --config="$POSH_THEME" --eval --shell=zsh)"
     zle .reset-prompt
 
     # If we received EOT, we exit the shell
diff --git a/src/engine/segment.go b/src/engine/segment.go
index 715ed2f9..e513b670 100644
--- a/src/engine/segment.go
+++ b/src/engine/segment.go
@@ -171,6 +171,9 @@ const (
 )
 
 func (segment *Segment) shouldIncludeFolder() bool {
+	if segment.env == nil {
+		return true
+	}
 	cwdIncluded := segment.cwdIncluded()
 	cwdExcluded := segment.cwdExcluded()
 	return cwdIncluded && !cwdExcluded
diff --git a/src/environment/shell.go b/src/environment/shell.go
index ea2a8657..96db3b77 100644
--- a/src/environment/shell.go
+++ b/src/environment/shell.go
@@ -29,39 +29,38 @@ const (
 	LinuxPlatform   = "linux"
 )
 
-type Args struct {
-	ErrorCode      *int
-	PrintInit      *bool
-	PrintConfig    *bool
-	PrintShell     *bool
-	PrintTransient *bool
-	PrintSecondary *bool
-	PrintValid     *bool
-	PrintError     *bool
-	Config         *string
-	ConfigFormat   *string
-	Shell          *string
-	PWD            *string
-	PSWD           *string
-	Version        *bool
-	Debug          *bool
-	ExecutionTime  *float64
-	Millis         *bool
-	Eval           *bool
-	Init           *bool
-	ExportPNG      *bool
-	Author         *string
-	CursorPadding  *int
-	RPromptOffset  *int
-	RPrompt        *bool
-	BGColor        *string
-	StackCount     *int
-	Command        *string
-	Plain          *bool
-	CachePath      *bool
-	Migrate        *bool
-	Write          *bool
-	TerminalWidth  *int
+type Flags struct {
+	ErrorCode     int
+	Config        string
+	Shell         string
+	PWD           string
+	PSWD          string
+	ExecutionTime float64
+	Eval          bool
+	StackCount    int
+	Migrate       bool
+	TerminalWidth int
+
+	// PrintInit      *bool
+	// PrintConfig    *bool
+	// PrintShell     *bool
+	// PrintTransient *bool
+	// PrintSecondary *bool
+	// PrintValid     *bool
+	// PrintError     *bool
+	// ConfigFormat   *string
+	// Version        *bool
+	// Millis         *bool
+	// Init           *bool
+	// ExportPNG      *bool
+	// Author         *string
+	// CursorPadding  *int
+	// RPromptOffset  *int
+	// RPrompt        *bool
+	// BGColor        *string
+	// Command        *string
+	// CachePath      *bool
+	// Write          *bool
 }
 
 type CommandError struct {
@@ -162,7 +161,7 @@ type Environment interface {
 	RunCommand(command string, args ...string) (string, error)
 	RunShellCommand(shell, command string) string
 	ExecutionTime() float64
-	Args() *Args
+	Flags() *Flags
 	BatteryInfo() ([]*battery.Battery, error)
 	QueryWindowTitles(processName, windowTitleRegex string) (string, error)
 	WindowsRegistryKeyValue(path string) (*WindowsRegistryValue, error)
@@ -207,7 +206,7 @@ const (
 )
 
 type ShellEnvironment struct {
-	args       *Args
+	CmdFlags   *Flags
 	cwd        string
 	cmdCache   *commandCache
 	fileCache  *fileCache
@@ -216,15 +215,20 @@ type ShellEnvironment struct {
 	debug      bool
 }
 
-func (env *ShellEnvironment) Init(args *Args) {
-	env.args = args
+func (env *ShellEnvironment) Init(debug bool) {
+	if env.CmdFlags == nil {
+		env.CmdFlags = &Flags{}
+	}
+	if len(env.CmdFlags.Config) == 0 {
+		env.CmdFlags.Config = env.Getenv("POSH_THEME")
+	}
 	env.fileCache = &fileCache{}
 	env.fileCache.Init(env.CachePath())
 	env.ResolveConfigPath()
 	env.cmdCache = &commandCache{
 		commands: newConcurrentMap(),
 	}
-	if env.args != nil && *env.args.Debug {
+	if debug {
 		env.debug = true
 		log.SetOutput(&env.logBuilder)
 	}
@@ -245,14 +249,14 @@ func (env *ShellEnvironment) getConfigPath(location string) {
 	if err != nil {
 		return
 	}
-	env.args.Config = &configPath
+	env.CmdFlags.Config = configPath
 }
 
 func (env *ShellEnvironment) ResolveConfigPath() {
-	if env.args == nil || env.args.Config == nil || len(*env.args.Config) == 0 {
+	if env.CmdFlags == nil || len(env.CmdFlags.Config) == 0 {
 		return
 	}
-	location, err := url.ParseRequestURI(*env.Args().Config)
+	location, err := url.ParseRequestURI(env.CmdFlags.Config)
 	if err == nil {
 		env.getConfigPath(location.String())
 		return
@@ -262,7 +266,7 @@ func (env *ShellEnvironment) ResolveConfigPath() {
 	if env.Platform() == WindowsPlatform && env.Shell() == "bash" {
 		return
 	}
-	configFile := *env.args.Config
+	configFile := env.CmdFlags.Config
 	if strings.HasPrefix(configFile, "~") {
 		configFile = strings.TrimPrefix(configFile, "~")
 		configFile = filepath.Join(env.Home(), configFile)
@@ -272,7 +276,7 @@ func (env *ShellEnvironment) ResolveConfigPath() {
 			configFile = absConfigFile
 		}
 	}
-	*env.args.Config = filepath.Clean(configFile)
+	env.CmdFlags.Config = filepath.Clean(configFile)
 }
 
 func (env *ShellEnvironment) trace(start time.Time, function string, args ...string) {
@@ -312,8 +316,8 @@ func (env *ShellEnvironment) Pwd() string {
 		driveLetter := regex.GetCompiledRegex(`^[a-z]:`)
 		return driveLetter.ReplaceAllStringFunc(pwd, strings.ToUpper)
 	}
-	if env.args != nil && *env.args.PWD != "" {
-		env.cwd = correctPath(*env.args.PWD)
+	if env.CmdFlags != nil && env.CmdFlags.PWD != "" {
+		env.cwd = correctPath(env.CmdFlags.PWD)
 		return env.cwd
 	}
 	dir, err := os.Getwd()
@@ -484,20 +488,20 @@ func (env *ShellEnvironment) HasCommand(command string) bool {
 
 func (env *ShellEnvironment) ErrorCode() int {
 	defer env.trace(time.Now(), "ErrorCode")
-	return *env.args.ErrorCode
+	return env.CmdFlags.ErrorCode
 }
 
 func (env *ShellEnvironment) ExecutionTime() float64 {
 	defer env.trace(time.Now(), "ExecutionTime")
-	if *env.args.ExecutionTime < 0 {
+	if env.CmdFlags.ExecutionTime < 0 {
 		return 0
 	}
-	return *env.args.ExecutionTime
+	return env.CmdFlags.ExecutionTime
 }
 
-func (env *ShellEnvironment) Args() *Args {
-	defer env.trace(time.Now(), "Args")
-	return env.args
+func (env *ShellEnvironment) Flags() *Flags {
+	defer env.trace(time.Now(), "Flags")
+	return env.CmdFlags
 }
 
 func (env *ShellEnvironment) BatteryInfo() ([]*battery.Battery, error) {
@@ -562,8 +566,8 @@ func (env *ShellEnvironment) BatteryInfo() ([]*battery.Battery, error) {
 
 func (env *ShellEnvironment) Shell() string {
 	defer env.trace(time.Now(), "Shell")
-	if *env.args.Shell != "" {
-		return *env.args.Shell
+	if env.CmdFlags.Shell != "" {
+		return env.CmdFlags.Shell
 	}
 	pid := os.Getppid()
 	p, _ := process.NewProcess(int32(pid))
@@ -581,8 +585,8 @@ func (env *ShellEnvironment) Shell() string {
 		return Unknown
 	}
 	// Cache the shell value to speed things up.
-	*env.args.Shell = strings.Trim(strings.Replace(name, ".exe", "", 1), " ")
-	return *env.args.Shell
+	env.CmdFlags.Shell = strings.Trim(strings.Replace(name, ".exe", "", 1), " ")
+	return env.CmdFlags.Shell
 }
 
 func (env *ShellEnvironment) HTTPRequest(targetURL string, timeout int, requestModifiers ...HTTPRequestModifier) ([]byte, error) {
@@ -637,10 +641,10 @@ func (env *ShellEnvironment) HasParentFilePath(path string) (*FileInfo, error) {
 
 func (env *ShellEnvironment) StackCount() int {
 	defer env.trace(time.Now(), "StackCount")
-	if *env.args.StackCount < 0 {
+	if env.CmdFlags.StackCount < 0 {
 		return 0
 	}
-	return *env.args.StackCount
+	return env.CmdFlags.StackCount
 }
 
 func (env *ShellEnvironment) Cache() Cache {
diff --git a/src/environment/unix.go b/src/environment/unix.go
index 30cdf4b5..01fe5994 100644
--- a/src/environment/unix.go
+++ b/src/environment/unix.go
@@ -45,8 +45,8 @@ func (env *ShellEnvironment) IsWsl2() bool {
 
 func (env *ShellEnvironment) TerminalWidth() (int, error) {
 	defer env.trace(time.Now(), "TerminalWidth")
-	if *env.args.TerminalWidth != 0 {
-		return *env.args.TerminalWidth, nil
+	if env.CmdFlags.TerminalWidth != 0 {
+		return env.CmdFlags.TerminalWidth, nil
 	}
 	width, err := terminal.Width()
 	if err != nil {
diff --git a/src/environment/windows.go b/src/environment/windows.go
index c5807dfe..bec633ba 100644
--- a/src/environment/windows.go
+++ b/src/environment/windows.go
@@ -87,8 +87,8 @@ func (env *ShellEnvironment) IsWsl2() bool {
 
 func (env *ShellEnvironment) TerminalWidth() (int, error) {
 	defer env.trace(time.Now(), "TerminalWidth")
-	if *env.args.TerminalWidth != 0 {
-		return *env.args.TerminalWidth, nil
+	if env.CmdFlags.TerminalWidth != 0 {
+		return env.CmdFlags.TerminalWidth, nil
 	}
 	handle, err := syscall.Open("CONOUT$", syscall.O_RDWR, 0)
 	if err != nil {
diff --git a/src/go.mod b/src/go.mod
index 0c8e70f0..ce089d50 100644
--- a/src/go.mod
+++ b/src/go.mod
@@ -34,6 +34,7 @@ require (
 
 require (
 	github.com/hashicorp/hcl/v2 v2.11.1
+	github.com/spf13/cobra v1.4.0
 	golang.org/x/mod v0.5.1
 	gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b
 )
@@ -66,9 +67,11 @@ require (
 	github.com/agext/levenshtein v1.2.3 // indirect
 	github.com/apparentlymart/go-textseg/v13 v13.0.0 // indirect
 	github.com/google/go-cmp v0.5.7 // indirect
+	github.com/inconshreveable/mousetrap v1.0.0 // indirect
 	github.com/mitchellh/go-wordwrap v1.0.1 // indirect
 	github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect
 	github.com/shopspring/decimal v1.3.1 // indirect
+	github.com/spf13/pflag v1.0.5 // indirect
 	github.com/yusufpapurcu/wmi v1.2.2 // indirect
 	github.com/zclconf/go-cty v1.10.0 // indirect
 )
diff --git a/src/go.sum b/src/go.sum
index 09a172a2..34199569 100644
--- a/src/go.sum
+++ b/src/go.sum
@@ -25,6 +25,7 @@ github.com/apparentlymart/go-textseg v1.0.0 h1:rRmlIsPEEhUTIKQb7T++Nz/A5Q6C9IuX2
 github.com/apparentlymart/go-textseg v1.0.0/go.mod h1:z96Txxhf3xSFMPmb5X/1W05FF/Nj9VFpLOpjS5yuumk=
 github.com/apparentlymart/go-textseg/v13 v13.0.0 h1:Y+KvPE1NYz0xl601PVImeQfFyEy6iT90AvPUL1NNfNw=
 github.com/apparentlymart/go-textseg/v13 v13.0.0/go.mod h1:ZK2fH7c4NqDTLtiYLvIkEghdlcqw7yxLeM89kiTRPUo=
+github.com/cpuguy83/go-md2man/v2 v2.0.1/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
 github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
 github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
 github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
@@ -71,6 +72,8 @@ github.com/huandu/xstrings v1.3.2/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq
 github.com/imdario/mergo v0.3.11/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA=
 github.com/imdario/mergo v0.3.12 h1:b6R2BslTbIEToALKP7LxUvijTsNI9TAe80pLWN2g/HU=
 github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA=
+github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM=
+github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
 github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
 github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
 github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
@@ -114,6 +117,7 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb
 github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
 github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c h1:ncq/mPwQF4JjgDlrVEn3C11VoGHZN7m8qihwgMEtzYw=
 github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE=
+github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
 github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
 github.com/sergi/go-diff v1.2.0 h1:XU+rvMAioB0UC3q1MFrIQy4Vo5/4VsRDQQXHsEya6xQ=
 github.com/sergi/go-diff v1.2.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM=
@@ -125,7 +129,11 @@ github.com/shopspring/decimal v1.3.1/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFR
 github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
 github.com/spf13/cast v1.4.1 h1:s0hze+J0196ZfEMTs80N7UlFt0BDuQ7Q+JDnHiMWKdA=
 github.com/spf13/cast v1.4.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
+github.com/spf13/cobra v1.4.0 h1:y+wJpx64xcgO1V+RcnwW0LEHxTKRi2ZDPSBjWnrg88Q=
+github.com/spf13/cobra v1.4.0/go.mod h1:Wo4iy3BUC+X2Fybo0PDqwJIv3dNRiZLHQymsfxlB84g=
 github.com/spf13/pflag v1.0.2/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
+github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
+github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
 github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
 github.com/stretchr/objx v0.3.0 h1:NGXK3lHquSN08v5vWalVI/L8XU9hdzE/G6xsrze47As=
 github.com/stretchr/objx v0.3.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
diff --git a/src/main.go b/src/main.go
index 94f2d74f..3a8e7708 100644
--- a/src/main.go
+++ b/src/main.go
@@ -1,266 +1,7 @@
 package main
 
-import (
-	"flag"
-	"fmt"
-	"oh-my-posh/color"
-	"oh-my-posh/console"
-	"oh-my-posh/engine"
-	"oh-my-posh/environment"
-	"time"
-
-	"github.com/gookit/config/v2"
-)
-
-// Version number of oh-my-posh
-var Version = "development"
+import "oh-my-posh/cmd"
 
 func main() {
-	args := &environment.Args{
-		ErrorCode: flag.Int(
-			"error",
-			0,
-			"Error code of previously executed command"),
-		PrintConfig: flag.Bool(
-			"print-config",
-			false,
-			"Print the current config in json format"),
-		ConfigFormat: flag.String(
-			"format",
-			config.JSON,
-			"The format to print the config in. Valid options are:\n- json\n- yaml\n- toml\n"),
-		PrintShell: flag.Bool(
-			"print-shell",
-			false,
-			"Print the current shell name"),
-		Config: flag.String(
-			"config",
-			"",
-			"Add the path to a configuration you wish to load"),
-		Shell: flag.String(
-			"shell",
-			"",
-			"Override the shell you are working in"),
-		PWD: flag.String(
-			"pwd",
-			"",
-			"the path you are working in"),
-		PSWD: flag.String(
-			"pswd",
-			"",
-			"the powershell path you are working in, useful when working with drives"),
-		Version: flag.Bool(
-			"version",
-			false,
-			"Print the current version of the binary"),
-		Debug: flag.Bool(
-			"debug",
-			false,
-			"Print debug information"),
-		ExecutionTime: flag.Float64(
-			"execution-time",
-			0,
-			"Execution time of the previously executed command"),
-		Millis: flag.Bool(
-			"millis",
-			false,
-			"Get the current time in milliseconds"),
-		Eval: flag.Bool(
-			"eval",
-			false,
-			"Run in eval mode"),
-		Init: flag.Bool(
-			"init",
-			false,
-			"Initialize the shell"),
-		PrintInit: flag.Bool(
-			"print-init",
-			false,
-			"Print the shell initialization script"),
-		ExportPNG: flag.Bool(
-			"export-png",
-			false,
-			"Create an image based on the current configuration"),
-		Author: flag.String(
-			"author",
-			"",
-			"Add the author to the exported image using --export-img"),
-		CursorPadding: flag.Int(
-			"cursor-padding",
-			30,
-			"Pad the cursor with x when using --export-img"),
-		RPromptOffset: flag.Int(
-			"rprompt-offset",
-			40,
-			"Offset the right prompt with x when using --export-img"),
-		RPrompt: flag.Bool(
-			"rprompt",
-			false,
-			"Only print the rprompt block"),
-		BGColor: flag.String(
-			"bg-color",
-			"#151515",
-			"Set the background color when using --export-img"),
-		StackCount: flag.Int(
-			"stack-count",
-			0,
-			"The current location stack count"),
-		Command: flag.String(
-			"command",
-			"",
-			"Render a tooltip based on the command value"),
-		PrintTransient: flag.Bool(
-			"print-transient",
-			false,
-			"Print the transient prompt"),
-		Plain: flag.Bool(
-			"plain",
-			false,
-			"Print a plain prompt without ANSI"),
-		CachePath: flag.Bool(
-			"cache-path",
-			false,
-			"Print the location of the cache"),
-		Migrate: flag.Bool(
-			"migrate",
-			false,
-			"Migrate the config to the latest version"),
-		Write: flag.Bool(
-			"write",
-			false,
-			"Write the config to the file"),
-		TerminalWidth: flag.Int(
-			"terminal-width",
-			0,
-			"The width of the terminal"),
-		PrintValid: flag.Bool(
-			"print-valid",
-			false,
-			"Print the valid prompt"),
-		PrintError: flag.Bool(
-			"print-error",
-			false,
-			"Print the failed prompt"),
-		PrintSecondary: flag.Bool(
-			"print-secondary",
-			false,
-			"Print the secondary prompt"),
-	}
-	flag.Parse()
-	if *args.Version {
-		fmt.Println(Version)
-		return
-	}
-	env := &environment.ShellEnvironment{}
-	env.Init(args)
-	defer env.Close()
-	if *args.PrintShell {
-		fmt.Println(env.Shell())
-		return
-	}
-	if *args.Millis {
-		fmt.Print(time.Now().UnixNano() / 1000000)
-		return
-	}
-	if *args.CachePath {
-		fmt.Print(env.CachePath())
-		return
-	}
-	if *args.Init {
-		init := engine.InitShell(env)
-		fmt.Print(init)
-		return
-	}
-	if *args.PrintInit {
-		init := engine.PrintShellInit(env)
-		fmt.Print(init)
-		return
-	}
-	cfg := engine.LoadConfig(env)
-	if *args.PrintConfig {
-		fmt.Print(cfg.Export(*args.ConfigFormat))
-		return
-	}
-	if *args.Migrate {
-		if *args.Write {
-			cfg.BackupAndMigrate(env)
-			return
-		}
-		cfg.Migrate(env)
-		fmt.Print(cfg.Export(*args.ConfigFormat))
-		return
-	}
-	ansi := &color.Ansi{}
-	ansi.Init(env.Shell())
-	var writer color.Writer
-	if *args.Plain {
-		writer = &color.PlainWriter{}
-	} else {
-		writerColors := cfg.MakeColors(env)
-		writer = &color.AnsiWriter{
-			Ansi:               ansi,
-			TerminalBackground: engine.GetConsoleBackgroundColor(env, cfg.TerminalBackground),
-			AnsiColors:         writerColors,
-		}
-	}
-	consoleTitle := &console.Title{
-		Env:      env,
-		Ansi:     ansi,
-		Template: cfg.ConsoleTitleTemplate,
-		Style:    cfg.ConsoleTitleStyle,
-	}
-	eng := &engine.Engine{
-		Config:       cfg,
-		Env:          env,
-		Writer:       writer,
-		ConsoleTitle: consoleTitle,
-		Ansi:         ansi,
-		Plain:        *args.Plain,
-	}
-	if *args.Debug {
-		fmt.Print(eng.Debug(Version))
-		return
-	}
-	if *args.PrintTransient {
-		fmt.Print(eng.RenderExtraPrompt(engine.Transient))
-		return
-	}
-	if *args.PrintValid {
-		fmt.Print(eng.RenderExtraPrompt(engine.Valid))
-		return
-	}
-	if *args.PrintError {
-		fmt.Print(eng.RenderExtraPrompt(engine.Error))
-		return
-	}
-	if *args.PrintSecondary {
-		fmt.Print(eng.RenderExtraPrompt(engine.Secondary))
-		return
-	}
-	if len(*args.Command) != 0 {
-		fmt.Print(eng.RenderTooltip(*args.Command))
-		return
-	}
-	if *args.RPrompt {
-		fmt.Print(eng.RenderRPrompt())
-		return
-	}
-	prompt := eng.Render()
-	if !*args.ExportPNG {
-		fmt.Print(prompt)
-		return
-	}
-	imageCreator := &engine.ImageRenderer{
-		AnsiString:    prompt,
-		Author:        *args.Author,
-		CursorPadding: *args.CursorPadding,
-		RPromptOffset: *args.RPromptOffset,
-		BgColor:       *args.BGColor,
-		Ansi:          ansi,
-	}
-	imageCreator.Init(*args.Config)
-	err := imageCreator.SavePNG()
-	if err != nil {
-		fmt.Print(err.Error())
-	}
+	cmd.Execute()
 }
diff --git a/src/mock/environment.go b/src/mock/environment.go
index c9eb270f..0d188736 100644
--- a/src/mock/environment.go
+++ b/src/mock/environment.go
@@ -106,9 +106,9 @@ func (env *MockedEnvironment) Root() bool {
 	return args.Bool(0)
 }
 
-func (env *MockedEnvironment) Args() *environment.Args {
+func (env *MockedEnvironment) Flags() *environment.Flags {
 	arguments := env.Called()
-	return arguments.Get(0).(*environment.Args)
+	return arguments.Get(0).(*environment.Flags)
 }
 
 func (env *MockedEnvironment) BatteryInfo() ([]*battery.Battery, error) {
diff --git a/src/segments/path.go b/src/segments/path.go
index ada7d03e..b418f52a 100644
--- a/src/segments/path.go
+++ b/src/segments/path.go
@@ -291,7 +291,7 @@ func (pt *Path) getFolderPath() string {
 }
 
 func (pt *Path) getPwd() string {
-	pwd := *pt.env.Args().PSWD
+	pwd := pt.env.Flags().PSWD
 	if pwd == "" {
 		pwd = pt.env.Pwd()
 	}


Test patch:
diff --git a/src/engine/engine_test.go b/src/engine/engine_test.go
index 3b28b089..6f4b1858 100644
--- a/src/engine/engine_test.go
+++ b/src/engine/engine_test.go
@@ -6,8 +6,6 @@ import (
 	"oh-my-posh/console"
 	"oh-my-posh/environment"
 	"oh-my-posh/mock"
-	"os"
-	"path/filepath"
 	"testing"
 
 	"github.com/stretchr/testify/assert"
@@ -45,48 +43,14 @@ func TestCanWriteRPrompt(t *testing.T) {
 }
 
 func BenchmarkEngineRender(b *testing.B) {
-	var err error
 	for i := 0; i < b.N; i++ {
-		err = engineRender("jandedobbeleer.omp.json")
-		if err != nil {
-			b.Fatal(err)
-		}
+		engineRender()
 	}
 }
 
-func engineRender(configPath string) error {
-	testDir, err := os.Getwd()
-	if err != nil {
-		return err
-	}
-
-	configPath = filepath.Join(testDir, "test", configPath)
-
-	var (
-		debug    = false
-		eval     = false
-		shell    = "pwsh"
-		plain    = false
-		pwd      = ""
-		pswd     = ""
-		code     = 2
-		execTime = 917.0
-	)
-
-	args := &environment.Args{
-		Debug:         &debug,
-		Config:        &configPath,
-		Eval:          &eval,
-		Shell:         &shell,
-		Plain:         &plain,
-		PWD:           &pwd,
-		PSWD:          &pswd,
-		ErrorCode:     &code,
-		ExecutionTime: &execTime,
-	}
-
+func engineRender() {
 	env := &environment.ShellEnvironment{}
-	env.Init(args)
+	env.Init(false)
 	defer env.Close()
 
 	cfg := LoadConfig(env)
@@ -112,20 +76,13 @@ func engineRender(configPath string) error {
 		Writer:       writer,
 		ConsoleTitle: consoleTitle,
 		Ansi:         ansi,
-		Plain:        *args.Plain,
 	}
 
-	engine.Render()
-
-	return nil
+	engine.PrintPrimary()
 }
 
 func BenchmarkEngineRenderPalette(b *testing.B) {
-	var err error
 	for i := 0; i < b.N; i++ {
-		err = engineRender("jandedobbeleer-palette.omp.json")
-		if err != nil {
-			b.Fatal(err)
-		}
+		engineRender()
 	}
 }
diff --git a/src/environment/shell_test.go b/src/environment/shell_test.go
index 8a653eb5..e2e019cd 100644
--- a/src/environment/shell_test.go
+++ b/src/environment/shell_test.go
@@ -37,8 +37,8 @@ func TestWindowsPathWithDriveLetter(t *testing.T) {
 	}
 	for _, tc := range cases {
 		env := &ShellEnvironment{
-			args: &Args{
-				PWD: &tc.CWD,
+			CmdFlags: &Flags{
+				PWD: tc.CWD,
 			},
 		}
 		assert.Equal(t, env.Pwd(), tc.Expected)
diff --git a/src/segments/path_test.go b/src/segments/path_test.go
index 0ebe43c1..cab42b7b 100644
--- a/src/segments/path_test.go
+++ b/src/segments/path_test.go
@@ -92,10 +92,10 @@ func TestRootLocationHome(t *testing.T) {
 		env := new(mock.MockedEnvironment)
 		env.On("Home").Return(tc.HomePath)
 		env.On("Pwd").Return(tc.Pwd)
-		args := &environment.Args{
-			PSWD: &tc.Pswd,
+		args := &environment.Flags{
+			PSWD: tc.Pswd,
 		}
-		env.On("Args").Return(args)
+		env.On("Flags").Return(args)
 		env.On("PathSeparator").Return(tc.PathSeparator)
 		env.On("GOOS").Return("")
 		path := &Path{
@@ -276,10 +276,10 @@ func TestAgnosterPathStyles(t *testing.T) {
 		env.On("GOOS").Return(tc.GOOS)
 		env.On("StackCount").Return(0)
 		env.On("IsWsl").Return(false)
-		args := &environment.Args{
-			PSWD: &tc.Pswd,
+		args := &environment.Flags{
+			PSWD: tc.Pswd,
 		}
-		env.On("Args").Return(args)
+		env.On("Flags").Return(args)
 		path := &Path{
 			env: env,
 			props: properties.Map{
@@ -398,10 +398,10 @@ func TestGetFullPath(t *testing.T) {
 		env.On("GOOS").Return(tc.GOOS)
 		env.On("StackCount").Return(tc.StackCount)
 		env.On("IsWsl").Return(false)
-		args := &environment.Args{
-			PSWD: &tc.Pswd,
+		args := &environment.Flags{
+			PSWD: tc.Pswd,
 		}
-		env.On("Args").Return(args)
+		env.On("Flags").Return(args)
 		if len(tc.Template) == 0 {
 			tc.Template = "{{ if gt .StackCount 0 }}{{ .StackCount }} {{ end }}{{ .Path }}"
 		}
@@ -447,10 +447,10 @@ func TestGetFullPathCustomMappedLocations(t *testing.T) {
 		env.On("Home").Return("/usr/home")
 		env.On("Pwd").Return(tc.Pwd)
 		env.On("GOOS").Return("")
-		args := &environment.Args{
-			PSWD: &tc.Pwd,
+		args := &environment.Flags{
+			PSWD: tc.Pwd,
 		}
-		env.On("Args").Return(args)
+		env.On("Flags").Return(args)
 		path := &Path{
 			env: env,
 			props: properties.Map{
@@ -498,10 +498,10 @@ func TestGetFolderPathCustomMappedLocations(t *testing.T) {
 	env.On("Home").Return("/usr/home")
 	env.On("Pwd").Return(pwd)
 	env.On("GOOS").Return("")
-	args := &environment.Args{
-		PSWD: &pwd,
+	args := &environment.Flags{
+		PSWD: pwd,
 	}
-	env.On("Args").Return(args)
+	env.On("Flags").Return(args)
 	path := &Path{
 		env: env,
 		props: properties.Map{
@@ -545,10 +545,10 @@ func TestAgnosterPath(t *testing.T) { // nolint:dupl
 		env.On("PathSeparator").Return(tc.PathSeparator)
 		env.On("Pwd").Return(tc.PWD)
 		env.On("GOOS").Return("")
-		args := &environment.Args{
-			PSWD: &tc.PWD,
+		args := &environment.Flags{
+			PSWD: tc.PWD,
 		}
-		env.On("Args").Return(args)
+		env.On("Flags").Return(args)
 		path := &Path{
 			env: env,
 			props: properties.Map{
@@ -593,10 +593,10 @@ func TestAgnosterLeftPath(t *testing.T) { // nolint:dupl
 		env.On("PathSeparator").Return(tc.PathSeparator)
 		env.On("Pwd").Return(tc.PWD)
 		env.On("GOOS").Return("")
-		args := &environment.Args{
-			PSWD: &tc.PWD,
+		args := &environment.Flags{
+			PSWD: tc.PWD,
 		}
-		env.On("Args").Return(args)
+		env.On("Flags").Return(args)
 		path := &Path{
 			env: env,
 			props: properties.Map{
@@ -641,10 +641,10 @@ func TestGetPwd(t *testing.T) {
 		env.On("Home").Return("/usr/home")
 		env.On("Pwd").Return(tc.Pwd)
 		env.On("GOOS").Return("")
-		args := &environment.Args{
-			PSWD: &tc.Pswd,
+		args := &environment.Flags{
+			PSWD: tc.Pswd,
 		}
-		env.On("Args").Return(args)
+		env.On("Flags").Return(args)
 		path := &Path{
 			env: env,
 			props: properties.Map{