kitty-integration 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462
  1. #!/bin/zsh
  2. #
  3. # Enables integration between zsh and kitty based on KITTY_SHELL_INTEGRATION.
  4. # The latter is set by kitty based on kitty.conf.
  5. #
  6. # This is an autoloadable function. It's invoked automatically in shells
  7. # directly spawned by kitty but not in any other shells. For example, running
  8. # `exec zsh`, `sudo -E zsh`, `tmux`, or plain `zsh` will create a shell where
  9. # kitty-integration won't automatically run. Zsh users who want integration with
  10. # kitty in all shells should add the following lines to their .zshrc:
  11. #
  12. # if [[ -n "$KITTY_INSTALLATION_DIR" ]]; then
  13. # export KITTY_SHELL_INTEGRATION="enabled"
  14. # autoload -Uz -- "$KITTY_INSTALLATION_DIR"/shell-integration/zsh/kitty-integration
  15. # kitty-integration
  16. # unfunction kitty-integration
  17. # fi
  18. #
  19. # Implementation note: We can assume that alias expansion is disabled in this
  20. # file, so no need to quote defensively. We still have to defensively prefix all
  21. # builtins with `builtin` to avoid accidentally invoking user-defined functions.
  22. # We avoid `function` reserved word as an additional defensive measure.
  23. builtin emulate -L zsh -o no_warn_create_global -o no_aliases
  24. [[ -o interactive ]] || builtin return 0 # non-interactive shell
  25. [[ -n "$KITTY_SHELL_INTEGRATION" ]] || builtin return 0 # integration disabled
  26. (( ! $+_ksi_state )) || builtin return 0 # already initialized
  27. # 0: no OSC 133 [AC] marks have been written yet.
  28. # 1: the last written OSC 133 C has not been closed with D yet.
  29. # 2: none of the above.
  30. builtin typeset -gi _ksi_state
  31. # Attempt to create a writable file descriptor to the TTY so that we can print
  32. # to the TTY later even when STDOUT is redirected. This code is fairly subtle.
  33. #
  34. # - It's tempting to do `[[ -t 1 ]] && exec {_ksi_state}>&1` but we cannot do this
  35. # because it'll create a file descriptor >= 10 without O_CLOEXEC. This file
  36. # descriptor will leak to child processes.
  37. # - If we do `exec {3}>&1`, the file descriptor won't leak to the child processes
  38. # but it'll still leak if the current process is replaced with another. In
  39. # addition, it'll break user code that relies on fd 3 being available.
  40. # - Zsh doesn't expose dup3, which would have allowed us to copy STDOUT with
  41. # O_CLOEXEC. The only way to create a file descriptor with O_CLOEXEC is via
  42. # sysopen.
  43. # - `zmodload zsh/system` and `sysopen -o cloexec -wu _ksi_fd -- /dev/tty` can
  44. # fail with an error message to STDERR (the latter can happen even if /dev/tty
  45. # is writable), hence the redirection of STDERR. We do it for the whole block
  46. # for performance reasons (redirections are slow).
  47. # - We must open the file descriptor right here rather than in _ksi_deferred_init
  48. # because there are broken zsh plugins out there that run `exec {fd}< <(cmd)`
  49. # and then close the file descriptor more than once while suppressing errors.
  50. # This could end up closing our file descriptor if we opened it in
  51. # _ksi_deferred_init.
  52. typeset -gi _ksi_fd
  53. {
  54. builtin zmodload zsh/system && (( $+builtins[sysopen] )) && {
  55. { [[ -w $TTY ]] && builtin sysopen -o cloexec -wu _ksi_fd -- $TTY } ||
  56. { [[ -w /dev/tty ]] && builtin sysopen -o cloexec -wu _ksi_fd -- /dev/tty }
  57. }
  58. } 2>/dev/null || (( _ksi_fd = 1 ))
  59. # Asks kitty to print $@ to its STDERR. This is for debugging.
  60. _ksi_debug_print() {
  61. builtin local data
  62. data=$(builtin command base64 <<<"${(j: :)@}") || builtin return
  63. # Removing all spaces rather than just \n allows this code to
  64. # work on broken systems where base64 outputs \r\n.
  65. builtin print -nu "$_ksi_fd" '\eP@kitty-print|'"${data//[[:space:]]}"'\e\\'
  66. }
  67. # We defer initialization until precmd for several reasons:
  68. #
  69. # - Oh My Zsh and many other configs remove zle-line-init and
  70. # zle-line-finish hooks when they initialize.
  71. # - By deferring initialization we allow user rc files to opt out from some
  72. # parts of integration. For example, if a zshrc theme prints OSC 133
  73. # marks, it can append " no-prompt-mark" to KITTY_SHELL_INTEGRATION during
  74. # initialization to avoid redundant marks from our code.
  75. builtin typeset -ag precmd_functions
  76. precmd_functions+=(_ksi_deferred_init)
  77. _ksi_deferred_init() {
  78. builtin emulate -L zsh -o no_warn_create_global -o no_aliases
  79. # Recognized options: no-cursor, no-title, no-prompt-mark, no-complete, no-cwd, no-sudo.
  80. builtin local -a opt
  81. opt=(${(s: :)KITTY_SHELL_INTEGRATION})
  82. builtin unset KITTY_SHELL_INTEGRATION
  83. # The directory where kitty-integration is located: /.../shell-integration/zsh.
  84. builtin local self_dir="${functions_source[_ksi_deferred_init]:A:h}"
  85. # The directory with _kitty. We store it in a directory of its own rather than
  86. # in $self_dir because we are adding it to fpath and we don't want any other
  87. # files to be accidentally autoloadable.
  88. builtin local comp_dir="$self_dir/completions"
  89. # Enable completions for `kitty` command.
  90. if (( ! opt[(Ie)no-complete] )) && [[ -r $comp_dir/_kitty ]]; then
  91. if (( $+functions[compdef] )); then
  92. # If compdef is defined, then either compinit has already run or it's
  93. # a shim that records all calls for the purpose of replaying them after
  94. # compinit. Either way we clobber the existing completion for kitty and
  95. # install our own.
  96. builtin unset "functions[_kitty]"
  97. builtin autoload -Uz -- $comp_dir/_kitty
  98. compdef _kitty kitty
  99. compdef _kitty clone-in-kitty
  100. compdef _kitty kitten
  101. fi
  102. # If compdef is not set, compinit has not run yet. In this case we must
  103. # add our completions directory to fpath so that _kitty gets picked up by
  104. # compinit.
  105. #
  106. # We extend fpath even if compinit has run because it might run again.
  107. # Without our completions directory in fpath compinit would our _comp
  108. # mapping.
  109. builtin typeset -ga fpath
  110. fpath=($comp_dir ${fpath:#$comp_dir})
  111. fi
  112. # Enable semantic markup with OSC 133.
  113. if (( ! opt[(Ie)no-prompt-mark] )); then
  114. _ksi_precmd() {
  115. builtin local -i cmd_status=$?
  116. builtin emulate -L zsh -o no_warn_create_global -o no_aliases
  117. # Don't write OSC 133 D when our precmd handler is invoked from zle.
  118. # Some plugins do that to update prompt on cd.
  119. if ! builtin zle; then
  120. # This code works incorrectly in the presence of a precmd or chpwd
  121. # hook that prints. For example, sindresorhus/pure prints an empty
  122. # line on precmd and marlonrichert/zsh-snap prints $PWD on chpwd.
  123. # We'll end up writing our OSC 133 D mark too late.
  124. #
  125. # Another failure mode is when the output of a command doesn't end
  126. # with LF and prompst_sp is set (it is by default). In this case
  127. # we'll incorrectly state that '%' from prompt_sp is a part of the
  128. # command's output.
  129. if (( _ksi_state == 1 )); then
  130. # The last written OSC 133 C has not been closed with D yet.
  131. # Close it and supply status.
  132. builtin print -nu $_ksi_fd '\e]133;D;'$cmd_status'\a'
  133. (( _ksi_state = 2 ))
  134. elif (( _ksi_state == 2 )); then
  135. # There might be an unclosed OSC 133 C. Close that.
  136. builtin print -nu $_ksi_fd '\e]133;D\a'
  137. fi
  138. fi
  139. builtin local mark1=$'%{\e]133;A\a%}'
  140. if [[ -o prompt_percent ]]; then
  141. builtin typeset -g precmd_functions
  142. if [[ ${precmd_functions[-1]} == _ksi_precmd ]]; then
  143. # This is the best case for us: we can add our marks to PS1 and
  144. # PS2. This way our marks will be printed whenever zsh
  145. # redisplays prompt: on reset-prompt, on SIGWINCH, and on
  146. # SIGCHLD if notify is set. Themes that update prompt
  147. # asynchronously from a `zle -F` handler might still remove our
  148. # marks. Oh well.
  149. builtin local mark2=$'%{\e]133;A;k=s\a%}'
  150. # Add marks conditionally to avoid a situation where we have
  151. # several marks in place. These conditions can have false
  152. # positives and false negatives though.
  153. #
  154. # - False positive (with prompt_percent): PS1="%(?.$mark1.)"
  155. # - False negative (with prompt_subst): PS1='$mark1'
  156. [[ $PS1 == *$mark1* ]] || PS1=${mark1}${PS1}
  157. # PS2 mark is needed when clearing the prompt on resize
  158. [[ $PS2 == *$mark2* ]] || PS2=${mark2}${PS2}
  159. (( _ksi_state = 2 ))
  160. else
  161. # If our precmd hook is not the last, we cannot rely on prompt
  162. # changes to stick, so we don't even try. At least we can move
  163. # our hook to the end to have better luck next time. If there is
  164. # another piece of code that wants to take this privileged
  165. # position, this won't work well. We'll break them as much as
  166. # they are breaking us.
  167. precmd_functions=(${precmd_functions:#_ksi_precmd} _ksi_precmd)
  168. # Plugins that invoke precmd hooks from zle do that before zle
  169. # is trashed. This means that the cursor is in the middle of
  170. # BUFFER and we cannot print our mark there. Prompt might
  171. # already have a mark, so the following reset-prompt will write
  172. # it. If it doesn't, there is nothing we can do.
  173. if ! builtin zle; then
  174. builtin print -rnu $_ksi_fd -- $mark1[3,-3]
  175. (( _ksi_state = 2 ))
  176. fi
  177. fi
  178. elif ! builtin zle; then
  179. # Without prompt_percent we cannot patch prompt. Just print the
  180. # mark, except when we are invoked from zle. In the latter case we
  181. # cannot do anything.
  182. builtin print -rnu $_ksi_fd -- $mark1[3,-3]
  183. (( _ksi_state = 2 ))
  184. fi
  185. }
  186. _ksi_preexec() {
  187. builtin emulate -L zsh -o no_warn_create_global -o no_aliases
  188. # This can potentially break user prompt. Oh well. The robustness of
  189. # this code can be improved in the case prompt_subst is set because
  190. # it'll allow us distinguish (not perfectly but close enough) between
  191. # our own prompt, user prompt, and our own prompt with user additions on
  192. # top. We cannot force prompt_subst on the user though, so we would
  193. # still need this code for the no_prompt_subst case.
  194. PS1=${PS1//$'%{\e]133;A\a%}'}
  195. PS2=${PS2//$'%{\e]133;A;k=s\a%}'}
  196. # This will work incorrectly in the presence of a preexec hook that
  197. # prints. For example, if MichaelAquilina/zsh-you-should-use installs
  198. # its preexec hook before us, we'll incorrectly mark its output as
  199. # belonging to the command (as if the user typed it into zle) rather
  200. # than command output.
  201. builtin print -nu "$_ksi_fd" -f '\e]133;C;cmdline=%q\a' "$1"
  202. (( _ksi_state = 1 ))
  203. }
  204. # the following two lines are commented out as currently kitty doesn't use B prompt marking
  205. # and hooking zle widgets in ZSH is a total minefield, see https://github.com/kovidgoyal/kitty/issues/4428
  206. # so we can at least tell users to use no-cursor and with that avoid hooking ZLE widgets at all
  207. # functions[_ksi_zle_line_init]+='
  208. # builtin print -nu "$_ksi_fd" "\\e]133;B\\a"'
  209. fi
  210. # Enable reporting current working dir to terminal
  211. if (( ! opt[(Ie)no-cwd] )); then
  212. _ksi_report_pwd() { builtin print -nu $_ksi_fd '\e]7;kitty-shell-cwd://'"$HOST""$PWD"'\a'; }
  213. chpwd_functions=(${chpwd_functions[@]} "_ksi_report_pwd")
  214. # An executed program could change cwd and report the changed cwd, so also report cwd at each new prompt
  215. # as in this case chpwd_functions is insufficient. chpwd_functions is still needed for things like: cd x && something
  216. functions[_ksi_precmd]+="
  217. _ksi_report_pwd"
  218. _ksi_report_pwd
  219. fi
  220. # Enable terminal title changes.
  221. if (( ! opt[(Ie)no-title] )); then
  222. # We don't use `print -P` because it depends on prompt options, which
  223. # we don't control and cannot change.
  224. #
  225. # We use (V) in preexec to convert control characters to something visible
  226. # (LF becomes \n, etc.). This isn't necessary in precmd because (%) does it
  227. # for us.
  228. builtin local is_ssh_session="n"
  229. if [[ -n "$KITTY_PID" ]]; then
  230. # kitty running locally
  231. elif [[ -n "$SSH_TTY" || -n "$SSH2_TTY$KITTY_WINDOW_ID" ]]; then
  232. # connected to most SSH servers
  233. # or use ssh kitten to connected to some SSH servers that do not set SSH_TTY
  234. is_ssh_session="y"
  235. elif [[ -n "$(builtin command -v who)" ]]; then
  236. # the shell integration script is installed manually on the remote system
  237. # the environment variables are cleared after sudo
  238. # OpenSSH's sshd creates entries in utmp for every login so use those
  239. [[ "$(builtin command who -m 2> /dev/null)" =~ "\([a-fA-F.:0-9]+\)$" ]] && is_ssh_session="y"
  240. fi
  241. if [[ "$is_ssh_session" == "y" ]]; then
  242. # show the hostname via %m for SSH sessions
  243. functions[_ksi_precmd]+="
  244. builtin print -Prnu $_ksi_fd \$'\\e]2;'\"%m: \${(%):-%(4~|…/%3~|%~)}\"\$'\\a'"
  245. functions[_ksi_preexec]+="
  246. builtin print -Prnu $_ksi_fd \$'\\e]2;'\"%m: \${(V)1}\"\$'\\a'"
  247. else
  248. functions[_ksi_precmd]+="
  249. builtin print -rnu $_ksi_fd \$'\\e]2;'\"\${(%):-%(4~|…/%3~|%~)}\"\$'\\a'"
  250. functions[_ksi_preexec]+="
  251. builtin print -rnu $_ksi_fd \$'\\e]2;'\"\${(V)1}\"\$'\\a'"
  252. fi
  253. fi
  254. # Enable cursor shape changes depending on the current keymap.
  255. if (( ! opt[(Ie)no-cursor] )); then
  256. # This implementation leaks blinking block cursor into external commands
  257. # executed from zle. For example, users of fzf-based widgets may find
  258. # themselves with a blinking block cursor within fzf.
  259. _ksi_zle_line_init _ksi_zle_line_finish _ksi_zle_keymap_select() {
  260. case ${KEYMAP-} in
  261. # Blinking block cursor.
  262. vicmd|visual) builtin print -nu "$_ksi_fd" '\e[1 q';;
  263. # Blinking bar cursor.
  264. *) builtin print -nu "$_ksi_fd" '\e[5 q';;
  265. esac
  266. }
  267. # Restore the blinking default shape before executing an external command
  268. functions[_ksi_preexec]+="
  269. builtin print -rnu $_ksi_fd \$'\\e[0 q'"
  270. fi
  271. # Some zsh users manually run `source ~/.zshrc` in order to apply rc file
  272. # changes to the current shell. This is a terrible practice that breaks many
  273. # things, including our shell integration. For example, Oh My Zsh and Prezto
  274. # (both very popular among zsh users) will remove zle-line-init and
  275. # zle-line-finish hooks if .zshrc is manually sourced. Prezto will also remove
  276. # zle-keymap-select.
  277. #
  278. # Another common (and much more robust) way to apply rc file changes to the
  279. # current shell is `exec zsh`. This will remove our integration from the shell
  280. # unless it's explicitly invoked from .zshrc. This is not an issue with
  281. # `exec zsh` but rather with our implementation of automatic shell integration.
  282. # In the ideal world we would use add-zle-hook-widget to hook zle-line-init
  283. # and similar widget. This breaks user configs though, so we have do this
  284. # horrible thing instead.
  285. builtin local hook func widget orig_widget flag
  286. for hook in line-init line-finish keymap-select; do
  287. func=_ksi_zle_${hook/-/_}
  288. (( $+functions[$func] )) || builtin continue
  289. widget=zle-$hook
  290. if [[ $widgets[$widget] == user:azhw:* &&
  291. $+functions[add-zle-hook-widget] -eq 1 ]]; then
  292. # If the widget is already hooked by add-zle-hook-widget at the top
  293. # level, add our hook at the end. We MUST do it this way. We cannot
  294. # just wrap the widget ourselves in this case because it would
  295. # trigger bugs in add-zle-hook-widget.
  296. add-zle-hook-widget $hook $func
  297. else
  298. if (( $+widgets[$widget] )); then
  299. # There is a widget but it's not from add-zle-hook-widget. We
  300. # can rename the original widget, install our own and invoke
  301. # the original when we are called.
  302. #
  303. # Note: The leading dot is to work around bugs in
  304. # zsh-syntax-highlighting.
  305. orig_widget=._ksi_orig_$widget
  306. builtin zle -A $widget $orig_widget
  307. if [[ $widgets[$widget] == user:* ]]; then
  308. # No -w here to preserve $WIDGET within the original widget.
  309. flag=
  310. else
  311. flag=w
  312. fi
  313. functions[$func]+="
  314. builtin zle $orig_widget -N$flag -- \"\$@\""
  315. fi
  316. builtin zle -N $widget $func
  317. fi
  318. done
  319. if (( $+functions[_ksi_preexec] )); then
  320. builtin typeset -ag preexec_functions
  321. preexec_functions+=(_ksi_preexec)
  322. fi
  323. builtin typeset -ag precmd_functions
  324. if (( $+functions[_ksi_precmd] )); then
  325. precmd_functions=(${precmd_functions:/_ksi_deferred_init/_ksi_precmd})
  326. _ksi_precmd
  327. else
  328. precmd_functions=(${precmd_functions:#_ksi_deferred_init})
  329. fi
  330. if [ -n "${KITTY_IS_CLONE_LAUNCH}" ]; then
  331. builtin local orig_conda_env="$CONDA_DEFAULT_ENV"
  332. builtin eval "${KITTY_IS_CLONE_LAUNCH}"
  333. builtin hash -r 2> /dev/null 1> /dev/null
  334. builtin local venv="${VIRTUAL_ENV}/bin/activate"
  335. builtin local sourced=""
  336. _ksi_s_is_ok() {
  337. [[ -z "$sourced" && "$KITTY_CLONE_SOURCE_STRATEGIES" == *",$1,"* ]] && builtin return 0
  338. builtin return 1
  339. }
  340. if _ksi_s_is_ok "venv" && [[ -n "${VIRTUAL_ENV}" && -r "$venv" ]]; then
  341. sourced="y"
  342. builtin unset VIRTUAL_ENV
  343. builtin source "$venv"
  344. fi; if _ksi_s_is_ok "conda" && [[ -n "${CONDA_DEFAULT_ENV}" && (( $+commands[conda] )) && "${CONDA_DEFAULT_ENV}" != "$orig_conda_env" ]]; then
  345. sourced="y"
  346. conda activate "${CONDA_DEFAULT_ENV}"
  347. fi; if _ksi_s_is_ok "env_var" && [[ -n "${KITTY_CLONE_SOURCE_CODE}" ]]; then
  348. sourced="y"
  349. builtin eval "${KITTY_CLONE_SOURCE_CODE}"
  350. fi; if _ksi_s_is_ok "path" && [[ -r "${KITTY_CLONE_SOURCE_PATH}" ]]; then
  351. sourced="y"
  352. builtin source "${KITTY_CLONE_SOURCE_PATH}"
  353. fi
  354. builtin unfunction _ksi_s_is_ok
  355. # Ensure PATH has no duplicate entries
  356. builtin typeset -gxU PATH="$PATH"
  357. fi
  358. builtin unset KITTY_IS_CLONE_LAUNCH KITTY_CLONE_SOURCE_STRATEGIES
  359. builtin alias edit-in-kitty="kitten edit-in-kitty"
  360. if (( ! opt[(Ie)no-sudo] )) ; then
  361. if [[ -n "$TERMINFO" && ! ( -r "/usr/share/terminfo/x/xterm-kitty" || -r "/usr/share/terminfo/78/xterm-kitty" ) ]]; then
  362. sudo() {
  363. # Ensure terminfo is available in sudo
  364. builtin local is_sudoedit="n"
  365. for arg; do
  366. if [[ "$arg" == "-e" || $arg == "--edit" ]]; then
  367. is_sudoedit="y"
  368. builtin break;
  369. fi
  370. [[ "$arg" != -* && "$arg" != *=* ]] && builtin break # command found
  371. done
  372. if [[ "$is_sudoedit" == "y" ]]; then
  373. builtin command sudo "$@";
  374. else
  375. builtin command sudo TERMINFO="$TERMINFO" "$@";
  376. fi
  377. }
  378. fi
  379. fi
  380. # Map alt+left/right to move by word if not already mapped. This is expected behavior on macOS and I am tired
  381. # of answering questions about it.
  382. [[ $(builtin bindkey "^[[1;3C") == *" undefined-key" ]] && builtin bindkey "^[[1;3C" "forward-word"
  383. [[ $(builtin bindkey "^[[1;3D") == *" undefined-key" ]] && builtin bindkey "^[[1;3D" "backward-word"
  384. # Unfunction _ksi_deferred_init to save memory. Don't unfunction
  385. # kitty-integration though because decent public functions aren't supposed to
  386. # to unfunction themselves when invoked. Unfunctioning is done by calling code.
  387. builtin unfunction _ksi_deferred_init
  388. }
  389. _ksi_transmit_data() {
  390. builtin local data="${1//[[:space:]]}"
  391. builtin local pos=0
  392. builtin local chunk_num=0
  393. while [ $pos -lt ${#data} ]; do
  394. builtin local chunk="${data:$pos:2048}"
  395. pos=$(($pos+2048))
  396. builtin print -nu "$_ksi_fd" -f '\eP@kitty-%s|%s:%s\e\\' "${2}" "${chunk_num}" "${chunk}"
  397. chunk_num=$(($chunk_num+1))
  398. done
  399. # save history so it is available in new shell
  400. [ "$3" = "save_history" ] && builtin fc -AI
  401. builtin print -nu "$_ksi_fd" -f '\eP@kitty-%s|\e\\' "${2}"
  402. }
  403. clone-in-kitty() {
  404. builtin local data="shell=zsh,pid=$$,cwd=$(builtin printf "%s" "$PWD" | builtin command base64)"
  405. while :; do
  406. case "$1" in
  407. "") break;;
  408. -h|--help)
  409. builtin printf "%s\n\n%s\n" "Clone the current zsh session into a new kitty window." "For usage instructions see: https://sw.kovidgoyal.net/kitty/shell-integration/#clone-shell"
  410. builtin return
  411. ;;
  412. *) data="$data,a=$(builtin printf "%s" "$1" | builtin command base64)";;
  413. esac
  414. shift
  415. done
  416. builtin local env
  417. builtin local env_vars
  418. builtin local varname
  419. env_vars=(${(f)"$(builtin export)"})
  420. for i in $env_vars; do
  421. varname="${i%%=*}"
  422. env="${env}$(builtin printf "%s=%s\0" "$varname" "${(P)varname}")"
  423. done
  424. data="$data,env=$(builtin printf "%s" "$env" | builtin command base64)"
  425. _ksi_transmit_data "$data" "clone" "save_history"
  426. }