# a-agent Fish integration. Records command metadata only; no output is captured.
# The Rust runtime injects recent records from this Fish session into agent requests.
if not set -q __a_fish_session_key
set -g __a_fish_session_key (string join '' a_fish_ $fish_pid _ (date +%s) _ (random) _ (random))
end
if not set -q __a_ai_turn_mode
set -g __a_ai_turn_mode once
end
function __a_preexec --on-event fish_preexec
set -g __a_command $argv[1]
set -g __a_cwd $PWD
set -g __a_started_at (date +%s%3N)
end
function __a_postexec --on-event fish_postexec
set -l execution_snapshot $status $pipestatus
if not set -q __a_started_at
return
end
# __a_ai_turn is the AI turn itself, not a command the user ran, so it never
# enters the recorded shell context.
if test "$__a_command" = __a_ai_turn
return
end
set -l exit_code $execution_snapshot[1]
set -l pipe_status (string join ' ' $execution_snapshot[2..])
set -l finished_at (date +%s%3N)
set -l duration_ms (math "$finished_at - $__a_started_at")
command a __record-shell \
--cwd "$__a_cwd" \
--command "$__a_command" \
--exit-code "$exit_code" \
--pipe-status "$pipe_status" \
--fish-session-key "$__a_fish_session_key" \
--started-at "$__a_started_at" \
--duration-ms "$duration_ms" >/dev/null 2>&1
end
# Silences the prompt for one command so the row Fish echoes for it has a known
# height. Counting the theme's rows instead does not work: a transient prompt
# renders a different number of rows than the one that was counted, which is
# enough to erase the user's own input line.
function __a_silence_prompt
functions -q fish_prompt && functions --copy fish_prompt __a_saved_fish_prompt
functions -q fish_right_prompt &&
functions --copy fish_right_prompt __a_saved_fish_right_prompt
# Still run the theme's own prompt so its bookkeeping happens, such as
# clearing a one-shot transient-prompt flag, but discard the output so the
# echoed command line occupies exactly one row.
function fish_prompt
functions -q __a_saved_fish_prompt && __a_saved_fish_prompt >/dev/null 2>&1
return 0
end
function fish_right_prompt
functions -q __a_saved_fish_right_prompt &&
__a_saved_fish_right_prompt >/dev/null 2>&1
return 0
end
end
function __a_restore_prompt
functions -e fish_prompt fish_right_prompt
if functions -q __a_saved_fish_prompt
functions --copy __a_saved_fish_prompt fish_prompt
functions -e __a_saved_fish_prompt
end
if functions -q __a_saved_fish_right_prompt
functions --copy __a_saved_fish_right_prompt fish_right_prompt
functions -e __a_saved_fish_right_prompt
end
end
# Fish saves and restores $status around key bindings and event handlers, and
# $status itself is read-only, so a turn started from inside the Ctrl+G binding
# can never set the shell's exit code. Running the turn as a real command line
# is the only way, and then $status is simply the agent's exit code. Fish echoes
# the command line it runs and adds it to history, so this removes itself from
# history and erases the one echoed row before the agent writes anything.
function __a_ai_turn
history delete --exact --case-sensitive -- __a_ai_turn >/dev/null 2>&1
printf '\e[1A\r\e[J'
__a_restore_prompt
set -lx A_FISH_AI_PROMPT "$__a_ai_pending_prompt"
set -e __a_ai_pending_prompt
command a --fish-ai --fish-session-key "$__a_fish_session_key" --one-turn
end
function __a_render_ai_prompt
set_color --bold cyan
printf 'a> '
set_color normal
end
function __a_render_ai_right_prompt
if test "$__a_ai_turn_mode" = multi
set_color --bold magenta
printf 'multi · tab'
else
set_color brblack
printf 'once · tab'
end
set_color normal
end
function __a_toggle_turn_mode
if test "$__a_ai_turn_mode" = multi
set -g __a_ai_turn_mode once
else
set -g __a_ai_turn_mode multi
end
commandline -f repaint
end
function __a_handle_tab
if set -q __a_ai_prompt_active
__a_toggle_turn_mode
else
commandline -f complete
end
end
function __a_ai_prompt
if set -q __a_ai_prompt_active
set -g __a_ai_toggle_text (commandline)
set -g __a_ai_toggle_requested 1
commandline -f execute
return
end
set -l initial (commandline)
set -l shell_prompt_rows (count (fish_prompt))
commandline -r ''
commandline -f repaint
while true
set -g __a_ai_prompt_active 1
set -l prompt
read --local --line --command "$initial" --prompt __a_render_ai_prompt --right-prompt __a_render_ai_right_prompt prompt
set -l read_status $status
set -e __a_ai_prompt_active
if set -q __a_ai_toggle_requested
set -l shell_text "$__a_ai_toggle_text"
set -e __a_ai_toggle_requested
set -e __a_ai_toggle_text
set -l columns (tput cols 2>/dev/null)
if not string match --quiet --regex '^[1-9][0-9]*$' -- "$columns"
set columns 80
end
set -l text_width (string length --visible -- "$shell_text")
set -l ai_rows (math "max(1, ceil((3 + $text_width) / $columns))")
set -l clear_rows (math "max(1, $shell_prompt_rows + $ai_rows - 1)")
printf '\e[%dA\r\e[J' $clear_rows
commandline -r "$shell_text"
commandline -f repaint
return
end
if test $read_status -ne 0
commandline -f repaint
return
end
if test -n (string trim -- "$prompt")
if test "$__a_ai_turn_mode" = once
set -g __a_ai_pending_prompt "$prompt"
__a_silence_prompt
commandline -r __a_ai_turn
commandline -f execute
return
end
set -lx A_FISH_AI_PROMPT "$prompt"
command a --fish-ai --fish-session-key "$__a_fish_session_key" --one-turn
set shell_prompt_rows 1
else if test "$__a_ai_turn_mode" = once
commandline -f repaint
return
end
set initial ''
end
end
function __a_bind_keys
bind -M default \cg __a_ai_prompt
bind -M insert \cg __a_ai_prompt
bind -M default \t __a_handle_tab
bind -M insert \t __a_handle_tab
end
function __a_refresh_bindings --on-event fish_prompt
__a_bind_keys
end
__a_bind_keys