feat(shell): add cmdline_url to the FTCS command executed mark

Extend the OSC 133;C emission with the just-submitted command line,
percent-encoded as kitty's cmdline_url= shell-integration extension.
Terminals and multiplexers no longer need process-tree inspection to
know what command a pane runs - a primitive Windows lacks entirely.
Encoding happens in-shell with builtins only, so no extra process
spawn per prompt; terminals that ignore the parameter still see the
bare mark.

Closes #7536

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Entire-Checkpoint: 7f155fd83428
This commit is contained in:
Jan De Dobbeleer
2026-07-14 11:36:46 +02:00
committed by Jan De Dobbeleer
co-authored by Claude Fable 5
parent cbcc61be82
commit ba1b9eb125
7 changed files with 121 additions and 13 deletions
+40 -2
View File
@@ -65,10 +65,48 @@ function _omp_milliseconds() {
"$_omp_executable" get millis
}
# percent-encode $1, byte-wise, keeping RFC 3986 unreserved characters literal
function _omp_urlencode() {
local LC_ALL=C
local str=$1 encoded='' ch i
for ((i = 0; i < ${#str}; i++)); do
ch=${str:i:1}
case $ch in
[A-Za-z0-9._~-])
encoded+=$ch
;;
*)
printf -v ch '%%%02X' "'$ch"
encoded+=$ch
;;
esac
done
printf '%s' "$encoded"
}
function _omp_ftcs_command_start() {
if [[ $_omp_ftcs_marks == 1 ]]; then
printf '\e]133;C\a'
if [[ $_omp_ftcs_marks != 1 ]]; then
return
fi
# the command comes from history: format is " 501 command", or
# " 501* command" when the entry was modified; commands are missing
# entirely when history is off or HISTCONTROL ignores them
local cmd=''
if [[ -o history ]]; then
cmd=$(HISTTIMEFORMAT='' builtin history 1)
cmd=${cmd#"${cmd%%[![:space:]]*}"} # strip the leading padding
cmd=${cmd#"${cmd%%[!0-9]*}"} # strip the history number
cmd=${cmd#??} # strip the modified flag and separator
fi
if [[ -n $cmd ]]; then
# advertise the command line via kitty's cmdline_url= extension
printf '\e]133;C;cmdline_url=%s\a' "$(_omp_urlencode "$cmd")"
return
fi
printf '\e]133;C\a'
}
# template function for context loading
+10 -2
View File
@@ -578,9 +578,17 @@ function _omp_postexec --on-event fish_postexec
end
function _omp_preexec --on-event fish_preexec
if test $_omp_ftcs_marks = 1
echo -ne "\e]133;C\a"
if test $_omp_ftcs_marks != 1
return
end
if test -n "$argv"
# advertise the command line via kitty's cmdline_url= extension
echo -ne "\e]133;C;cmdline_url="(string escape --style=url -- "$argv")"\a"
return
end
echo -ne "\e]133;C\a"
end
# perform cleanup so a new initialization in current session works
+18 -2
View File
@@ -391,13 +391,29 @@ local function display_cached_prompt()
cached_prompt.only_use_cache = nil
end
local function url_encode(str)
-- percent-encode everything but RFC 3986 unreserved characters
return (string.gsub(str, '[^%w%-%._~]', function(ch)
return string.format('%%%02X', string.byte(ch))
end))
end
local function command_executed_mark(input)
if string.gsub(input, '^%s*(.-)%s*$', '%1') ~= '' then
no_exit_code = false
end
if ftcs_marks_enabled then
clink.print('\x1b]133;C\007', NONL)
if not ftcs_marks_enabled then
return
end
-- advertise the command line via kitty's cmdline_url= extension
local cmdline = ''
if input and input ~= '' then
cmdline = ';cmdline_url=' .. url_encode(input)
end
clink.print('\x1b]133;C' .. cmdline .. '\007', NONL)
end
-- set priority lower than z.lua
+11 -3
View File
@@ -1107,8 +1107,9 @@ New-Module -Name "oh-my-posh-core" -ScriptBlock {
return {
try {
$Streaming.State = 'NEW'
$ast = $null
$parseErrors = $null
[Microsoft.PowerShell.PSConsoleReadLine]::GetBufferState([ref]$null, [ref]$null, [ref]$parseErrors, [ref]$null)
[Microsoft.PowerShell.PSConsoleReadLine]::GetBufferState([ref]$ast, [ref]$null, [ref]$parseErrors, [ref]$null)
$executingCommand = $parseErrors.Count -eq 0
if ($global:_ompTransientPrompt -and $executingCommand) {
Set-TransientPrompt
@@ -1117,8 +1118,15 @@ New-Module -Name "oh-my-posh-core" -ScriptBlock {
finally {
& $AcceptLineFunction
if ($global:_ompFTCSMarks -and $executingCommand) {
# Write FTCS_COMMAND_EXECUTED after accepting the input - it should still happen before execution
Write-Host "$([char]27)]133;C$([char]7)" -NoNewline
# Write FTCS_COMMAND_EXECUTED after accepting the input - it should still happen before execution.
# The command line rides along as kitty's cmdline_url= extension, percent-encoded.
# Windows PowerShell's Uri.EscapeDataString throws beyond 32766 characters, hence the length cap.
$cmdline = ''
$command = $ast.Extent.Text
if ($command -and $command.Length -lt 32000) {
$cmdline = ";cmdline_url=$([Uri]::EscapeDataString($command))"
}
Write-Host "$([char]27)]133;C$cmdline$([char]7)" -NoNewline
}
}
}.GetNewClosure()
+15 -2
View File
@@ -18,10 +18,23 @@ _omp_executable=::OMP::
# switches to enable/disable features
_omp_ftcs_marks=0
# percent-encode every byte of $1; over-encoding is still valid URL encoding
_omp_urlencode() {
printf '%s' "$1" | od -v -An -tx1 | tr -d ' \n' | sed 's/../%&/g'
}
_omp_ftcs_command_start() {
if [ "$_omp_ftcs_marks" = 1 ]; then
printf '\033]133;C\007'
if [ "$_omp_ftcs_marks" != 1 ]; then
return
fi
if [ -n "$COMMAND" ]; then
# advertise the command line via kitty's cmdline_url= extension
printf '\033]133;C;cmdline_url=%s\007' "$(_omp_urlencode "$COMMAND")"
return
fi
printf '\033]133;C\007'
}
# template function for context loading
+26 -1
View File
@@ -389,9 +389,34 @@ function _omp_milliseconds() {
_omp_millis=$($_omp_executable get millis)
}
# percent-encode $1 into REPLY, byte-wise, keeping RFC 3986 unreserved characters literal
function _omp_urlencode() {
emulate -L zsh
setopt no_multibyte
local str=$1 ch
local -i i
REPLY=''
for (( i = 1; i <= ${#str}; i++ )); do
ch=$str[i]
if [[ $ch == [A-Za-z0-9._~-] ]]; then
REPLY+=$ch
continue
fi
printf -v ch '%%%02X' "'$ch"
REPLY+=$ch
done
}
function _omp_preexec() {
if [[ $_omp_ftcs_marks == 1 ]]; then
printf '\033]133;C\007'
if [[ -n $1 ]]; then
# advertise the command line via kitty's cmdline_url= extension
local REPLY
_omp_urlencode "$1"
printf '\033]133;C;cmdline_url=%s\007' "$REPLY"
else
printf '\033]133;C\007'
fi
fi
_omp_milliseconds
+1 -1
View File
@@ -133,7 +133,7 @@ For example, the following is a valid `--config` flag:
| `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 |
| `shell_integration` | `boolean` | `false` | enable shell integration using FinalTerm's OSC sequences. Works in bash, cmd (Clink v1.14.25+), fish, powershell and zsh |
| `shell_integration` | `boolean` | `false` | enable shell integration using FinalTerm's OSC sequences. Works in bash, cmd (Clink v1.14.25+), fish, powershell and zsh. The `FTCS_COMMAND_EXECUTED` mark carries the command line as kitty's `cmdline_url=` parameter |
| `enable_cursor_positioning` | `boolean` | `false` | enable fetching the cursor position in bash, zsh, and fish 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] |