Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 1 addition & 5 deletions apm.lock
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
lockfile_version: '1'
generated_at: '2026-03-11T11:40:09.315428+00:00'
generated_at: '2026-03-11T19:43:11.877128+00:00'
apm_version: 0.7.7
dependencies:
- repo_url: JanDeDobbeleer/agentic
Expand All @@ -9,7 +9,6 @@ dependencies:
is_virtual: true
package_type: claude_skill
deployed_files:
- .claude/skills/conventional-commit
- .github/skills/conventional-commit
- repo_url: JanDeDobbeleer/agentic
host: github.com
Expand All @@ -18,7 +17,6 @@ dependencies:
is_virtual: true
package_type: claude_skill
deployed_files:
- .claude/skills/golang
- .github/skills/golang
- repo_url: JanDeDobbeleer/agentic
host: github.com
Expand All @@ -27,7 +25,6 @@ dependencies:
is_virtual: true
package_type: claude_skill
deployed_files:
- .claude/skills/markdown
- .github/skills/markdown
- repo_url: JanDeDobbeleer/agentic
host: github.com
Expand All @@ -36,5 +33,4 @@ dependencies:
is_virtual: true
package_type: claude_skill
deployed_files:
- .claude/skills/powershell
- .github/skills/powershell
4 changes: 3 additions & 1 deletion src/cli/upgrade.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,9 +71,11 @@ var upgradeCmd = &cobra.Command{
}

terminal.Init(sh)
fmt.Print(terminal.StartProgress())

cfg := config.Get(configFlag, false)
terminal.Features = cfg.TerminalFeatures

fmt.Print(terminal.StartProgress())

defer func() {
fmt.Print(terminal.StopProgress())
Expand Down
13 changes: 9 additions & 4 deletions src/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ type Config struct {
ToolTipsAction Action `json:"tooltips_action,omitempty" toml:"tooltips_action,omitempty" yaml:"tooltips_action,omitempty"`
Blocks []*Block `json:"blocks,omitempty" toml:"blocks,omitempty" yaml:"blocks,omitempty"`
Cycle color.Cycle `json:"cycle,omitempty" toml:"cycle,omitempty" yaml:"cycle,omitempty"`
ITermFeatures terminal.ITermFeatures `json:"iterm_features,omitempty" toml:"iterm_features,omitempty" yaml:"iterm_features,omitempty"`
TerminalFeatures map[string][]string `json:"terminal_features,omitempty" toml:"terminal_features,omitempty" yaml:"terminal_features,omitempty"`
Tooltips []*Segment `json:"tooltips,omitempty" toml:"tooltips,omitempty" yaml:"tooltips,omitempty"`
hash uint64
Version int `json:"version" toml:"version" yaml:"version"`
Expand Down Expand Up @@ -166,9 +166,14 @@ func (cfg *Config) Features(env runtime.Environment) shell.Features {
feats |= shell.Tooltips
}

if env.Shell() == shell.FISH && cfg.ITermFeatures != nil && cfg.ITermFeatures.Contains(terminal.PromptMark) {
log.Debug("prompt mark enabled")
feats |= shell.PromptMark
if env.Shell() == shell.FISH {
for _, features := range cfg.TerminalFeatures {
if slices.Contains(features, terminal.PromptMark) {
log.Debug("prompt mark enabled")
feats |= shell.PromptMark
break
}
}
}

for i, block := range cfg.Blocks {
Expand Down
1 change: 1 addition & 0 deletions src/prompt/engine.go
Original file line number Diff line number Diff line change
Expand Up @@ -566,6 +566,7 @@ func New(flags *runtime.Flags) *Engine {
}

terminal.Init(sh)
terminal.Features = cfg.TerminalFeatures
terminal.BackgroundColor = cfg.TerminalBackground.ResolveTemplate()
terminal.Colors = cfg.MakeColors(env)
terminal.Plain = flags.Plain
Expand Down
4 changes: 2 additions & 2 deletions src/prompt/primary.go
Original file line number Diff line number Diff line change
Expand Up @@ -115,9 +115,9 @@ func (e *Engine) writePrimaryPromptInternal(needsPrimaryRPrompt, fromCache bool)
e.currentLineLength++
}

if e.Config.ITermFeatures != nil && e.isIterm() {
if terminal.HasITermFeatures() {
host, _ := e.Env.Host()
e.write(terminal.RenderItermFeatures(e.Config.ITermFeatures, e.Env.Shell(), e.Env.Pwd(), e.Env.User(), host))
e.write(terminal.RenderItermFeatures(e.Env.Shell(), e.Env.Pwd(), e.Env.User(), host))
}

if e.Config.ShellIntegration {
Expand Down
46 changes: 17 additions & 29 deletions src/terminal/iterm.go
Original file line number Diff line number Diff line change
@@ -1,50 +1,38 @@
package terminal

import (
"encoding/gob"
"fmt"
"slices"

"github.com/jandedobbeleer/oh-my-posh/src/shell"
"github.com/jandedobbeleer/oh-my-posh/src/text"
)

func init() {
gob.Register(&ITermFeatures{})
}

type iTermFeature string

const (
PromptMark iTermFeature = "prompt_mark"
CurrentDir iTermFeature = "current_dir"
RemoteHost iTermFeature = "remote_host"
PromptMark = "prompt_mark"
CurrentDir = "current_dir"
RemoteHost = "remote_host"
)

type ITermFeatures []iTermFeature

func (f ITermFeatures) Contains(feature iTermFeature) bool {
return slices.Contains(f, feature)
func HasITermFeatures() bool {
return SupportsFeature(PromptMark) || SupportsFeature(CurrentDir) || SupportsFeature(RemoteHost)
}

func RenderItermFeatures(features ITermFeatures, sh, pwd, user, host string) string {
func RenderItermFeatures(sh, pwd, user, host string) string {
result := text.NewBuilder()

supportedShells := []string{shell.BASH, shell.ZSH}

result := text.NewBuilder()
if SupportsFeature(PromptMark) && slices.Contains(supportedShells, sh) {
result.WriteString(formats.ITermPromptMark)
}

if SupportsFeature(CurrentDir) {
result.WriteString(fmt.Sprintf(formats.ITermCurrentDir, pwd))
}

for _, feature := range features {
switch feature {
case PromptMark:
if !slices.Contains(supportedShells, sh) {
continue
}

result.WriteString(formats.ITermPromptMark)
case CurrentDir:
result.WriteString(fmt.Sprintf(formats.ITermCurrentDir, pwd))
case RemoteHost:
result.WriteString(fmt.Sprintf(formats.ITermRemoteHost, user, host))
}
if SupportsFeature(RemoteHost) {
result.WriteString(fmt.Sprintf(formats.ITermRemoteHost, user, host))
}

return result.String()
Expand Down
26 changes: 23 additions & 3 deletions src/terminal/writer.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package terminal
import (
"fmt"
"os"
"slices"
"strings"

"github.com/jandedobbeleer/oh-my-posh/src/color"
Expand Down Expand Up @@ -62,6 +63,11 @@ var (
Shell string
Program string

// Features maps terminal program names to the list of features they support.
// When set, SupportsFeature uses this map to resolve feature support.
// When unset, SupportsFeature falls back to the built-in defaults.
Features map[string][]string

formats *shell.Formats
)

Expand Down Expand Up @@ -93,6 +99,9 @@ const (
setProgress = "\x1b]9;4;4;%d\x07"
endProgress = "\x1b]9;4;0;0\x07"

// Progress is the feature name for OSC 9;4 taskbar progress sequences.
Progress = "progress"

WindowsTerminal = "Windows Terminal"
Warp = "WarpTerminal"
ITerm = "iTerm.app"
Expand Down Expand Up @@ -247,24 +256,35 @@ func LineBreak() string {
return cr + lf
}

// SupportsFeature reports whether the current terminal program supports the named feature.
// When Features contains an entry for the current Program, the configured feature list is used.
// Otherwise, the provided defaults (program names) are checked against the current Program.
func SupportsFeature(feature string, defaults ...string) bool {
if features, ok := Features[Program]; ok {
return slices.Contains(features, feature)
}

return slices.Contains(defaults, Program)
}

func StartProgress() string {
if Program != WindowsTerminal {
if !SupportsFeature(Progress, WindowsTerminal) {
return ""
}

return startProgress
}

func SetProgress(percentage int) string {
if Program != WindowsTerminal {
if !SupportsFeature(Progress, WindowsTerminal) {
return ""
}

return fmt.Sprintf(setProgress, percentage)
}

func StopProgress() string {
if Program != WindowsTerminal {
if !SupportsFeature(Progress, WindowsTerminal) {
return ""
}

Expand Down
94 changes: 94 additions & 0 deletions src/terminal/writer_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -294,3 +294,97 @@ func TestWriteLength(t *testing.T) {
assert.Equal(t, tc.Expected, got, tc.Case)
}
}

func TestSupportsFeature(t *testing.T) {
cases := []struct {
Case string
Program string
Features map[string][]string
Feature string
Defaults []string
Expected bool
}{
{
Case: "default match - Windows Terminal matches built-in default",
Program: WindowsTerminal,
Feature: Progress,
Defaults: []string{WindowsTerminal},
Expected: true,
},
{
Case: "default no match - Ghostty not in built-in defaults",
Program: "Ghostty",
Feature: Progress,
Defaults: []string{WindowsTerminal},
Expected: false,
},
{
Case: "configured list - Ghostty added by user",
Program: "Ghostty",
Feature: Progress,
Features: map[string][]string{
"Ghostty": {Progress},
},
Defaults: []string{WindowsTerminal},
Expected: true,
},
{
Case: "configured list - Windows Terminal explicitly cleared",
Program: WindowsTerminal,
Feature: Progress,
Features: map[string][]string{
WindowsTerminal: {},
},
Defaults: []string{WindowsTerminal},
Expected: false,
},
{
Case: "configured list - unlisted program falls back to defaults",
Program: WindowsTerminal,
Feature: Progress,
Features: map[string][]string{
"Ghostty": {Progress},
},
Defaults: []string{WindowsTerminal},
Expected: true,
},
{
Case: "nil Features map uses defaults",
Program: WindowsTerminal,
Feature: Progress,
Features: nil,
Defaults: []string{WindowsTerminal},
Expected: true,
},
{
Case: "iTerm prompt_mark configured for iTerm.app",
Program: ITerm,
Feature: PromptMark,
Features: map[string][]string{
ITerm: {PromptMark, CurrentDir, RemoteHost},
},
Expected: true,
},
{
Case: "iTerm prompt_mark not configured - no defaults",
Program: ITerm,
Feature: PromptMark,
Expected: false,
},
}

for _, tc := range cases {
t.Run(tc.Case, func(t *testing.T) {
Program = tc.Program
Features = tc.Features

got := SupportsFeature(tc.Feature, tc.Defaults...)

assert.Equal(t, tc.Expected, got, tc.Case)
})
}

// Reset global state
Features = nil
Program = Unknown
}
21 changes: 10 additions & 11 deletions themes/schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -2622,7 +2622,7 @@
},
"extensions": {
"default": [
"*.🔥",
"*.\ud83d\udd25",
"*.mojo",
"mojoproject.toml"
]
Expand Down Expand Up @@ -5202,16 +5202,15 @@
"title": "Accent color",
"$ref": "#/definitions/color"
},
"iterm_features": {
"type": "array",
"title": "The iTerm2 features to enable",
"items": {
"type": "string",
"enum": [
"prompt_mark",
"current_dir",
"remote_host"
]
"terminal_features": {
"type": "object",
"title": "Terminal feature support overrides",
"description": "Maps terminal program names to the list of features they support. Keys are program names matched against the $TERM_PROGRAM environment variable (or \"Windows Terminal\" when $WT_SESSION is set). Values are lists of feature names (e.g. \"progress\", \"prompt_mark\", \"current_dir\", \"remote_host\"). Programs not listed fall back to built-in defaults.",
"additionalProperties": {
"type": "array",
"items": {
"type": "string"
}
}
},
"var": {
Expand Down
4 changes: 2 additions & 2 deletions website/docs/configuration/general.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -137,8 +137,8 @@ For example, the following is a valid `--config` flag:
| `enable_cursor_positioning` | `boolean` | `false` | enable fetching the cursor position in bash and zsh to allow automatic hiding of leading newlines when at the top of the shell |
| `patch_pwsh_bleed` | `boolean` | `false` | patch a PowerShell bug where the background colors bleed into the next line at the end of the buffer (can be removed when [this][pwsh-bleed] is merged) |
| `upgrade` | `Upgrade` | | enable auto upgrade or the upgrade notice. See [Upgrade] |
| `iterm_features` | `[]string` | `false` | enable iTerm2 specific features:<ul><li>`prompt_mark`: add the `iterm2_prompt_mark` [function][iterm2-si] for supported shells</li><li>`current_dir`: expose the current directory for iTerm2</li><li>`remote_host`: expose the current remote and user for iTerm2</li></ul> |
| `maps` | [`Maps`](#maps) | | a list of custom text mappings |
| `terminal_features` | `map[string][]string` | | configure which features each terminal supports. Keys are terminal program names matched against the `$TERM_PROGRAM` environment variable (or `Windows Terminal` when `$WT_SESSION` is set). Values are lists of feature names: `progress` (OSC 9;4 taskbar progress), `prompt_mark` (iTerm2 prompt mark for bash/zsh), `current_dir` (iTerm2 current directory), `remote_host` (iTerm2 remote host). A program not listed falls back to built-in defaults (e.g. `progress` defaults to `Windows Terminal`). This replaces the old `iterm_features` config — iTerm2 features are now configured under the `iTerm.app` key. |
| `maps` | [`Maps`](#maps) | | a list of custom text mappings |
| `async` | `boolean` | `false` | load the prompt async. Will either load the standard prompt, or allow you to start typing right away. Supported for `pwsh`, `powershell`, `zsh`, `bash` and `fish` |
| `version` | `int` | `4` | the config version, currently at `4` |
| `extends` | `string` | | the configuration to [extend] from |
Expand Down