agentsh.fish

  1# SPDX-FileCopyrightText: Amolith <amolith@secluded.site>
  2#
  3# SPDX-License-Identifier: Unlicense
  4
  5# Fallback to the configured agent command (Crush by default) when a command is not found.
  6
  7# Configure the prefix that marks commands for routing to the agent.
  8if not set -q __AGENTSH_PREFIX_CHAR
  9    set -g __AGENTSH_PREFIX_CHAR "✨"
 10end
 11
 12if not set -q __AGENTSH_PREFIX
 13    set -g __AGENTSH_PREFIX "$__AGENTSH_PREFIX_CHAR "
 14end
 15
 16if not set -q __AGENTSH_RUNNER
 17    set -g __AGENTSH_RUNNER crush run --quiet
 18end
 19
 20set -g __AGENTSH_PREFIX_ACTIVE 0
 21
 22# Preserve any previously defined handler so we can delegate if needed
 23if functions -q fish_command_not_found
 24    functions -c fish_command_not_found __agentsh_prev_fish_command_not_found
 25end
 26
 27# Command not found handler
 28function fish_command_not_found
 29    set -l missing_command $argv[1]
 30    set -l remaining_args $argv[2..-1]
 31
 32    # Nothing to do without a command name
 33    if test -z "$missing_command"
 34        echo "fish: command not found" >&2
 35        return 127
 36    end
 37
 38    set -l prefix_char $__AGENTSH_PREFIX_CHAR
 39    set -l handled false
 40    set -l effective_cmd
 41
 42    # Check if command starts with prefix
 43    if test "$missing_command" = "$prefix_char"
 44        set handled true
 45        set effective_cmd $remaining_args
 46    else if string match -q "$prefix_char*" -- "$missing_command"
 47        set handled true
 48        set -l stripped (string replace -- "$prefix_char" "" -- "$missing_command")
 49        if test -n "$stripped"
 50            set effective_cmd $stripped $remaining_args
 51        else
 52            set effective_cmd $remaining_args
 53        end
 54    end
 55
 56    if test "$handled" != true
 57        if functions -q __agentsh_prev_fish_command_not_found
 58            __agentsh_prev_fish_command_not_found $argv
 59            return $status
 60        end
 61        echo "fish: command not found: $missing_command" >&2
 62        return 127
 63    end
 64
 65    if test (count $effective_cmd) -eq 0
 66        echo "agentsh: nothing to run after '$prefix_char'." >&2
 67        return 127
 68    end
 69
 70    if not set -q __AGENTSH_RUNNER[1]
 71        echo "agentsh: no runner configured; set __AGENTSH_RUNNER before sourcing agentsh." >&2
 72        return 127
 73    end
 74
 75    set -l runner $__AGENTSH_RUNNER
 76    set -l runner_exe $runner[1]
 77
 78    if not type -q -- $runner_exe
 79        if functions -q __agentsh_prev_fish_command_not_found
 80            __agentsh_prev_fish_command_not_found $argv
 81            return $status
 82        end
 83        echo "agentsh: $runner_exe: command not found; unable to handle '$effective_cmd[1]'." >&2
 84        return 127
 85    end
 86
 87    # Build command string as single argument
 88    set -l full_cmd (string join ' ' -- $effective_cmd)
 89
 90    $runner $full_cmd
 91    return $status
 92end
 93
 94# Toggle prefix function
 95function __agentsh_toggle_prefix
 96    set -l prefix $__AGENTSH_PREFIX
 97    set -l prefix_len (string length -- $prefix)
 98    set -l buffer (commandline)
 99    set -l cursor_pos (commandline -C)
100
101    if string match -q "$prefix*" -- "$buffer"
102        # Remove prefix
103        set -l new_buffer (string sub --start (math $prefix_len + 1) -- $buffer)
104        commandline -r -- $new_buffer
105
106        if test $cursor_pos -gt $prefix_len
107            commandline -C (math $cursor_pos - $prefix_len)
108        else
109            commandline -C 0
110        end
111        set -g __AGENTSH_PREFIX_ACTIVE 0
112    else
113        # Add prefix
114        commandline -r -- "$prefix$buffer"
115        commandline -C (math $cursor_pos + $prefix_len)
116        set -g __AGENTSH_PREFIX_ACTIVE 1
117    end
118end
119
120# Hook to persist prefix across prompts
121function __agentsh_postexec --on-event fish_postexec
122    if test $__AGENTSH_PREFIX_ACTIVE -eq 1
123        commandline -f repaint
124    end
125end
126
127# Guard backward deletion to prevent removing prefix
128function __agentsh_guard_backward_delete
129    if test $__AGENTSH_PREFIX_ACTIVE -eq 0
130        commandline -f backward-delete-char
131        return
132    end
133
134    set -l prefix $__AGENTSH_PREFIX
135    set -l prefix_len (string length -- $prefix)
136    set -l buffer (commandline)
137    set -l cursor_pos (commandline -C)
138
139    # Don't delete if we're at or before the prefix boundary
140    if string match -q "$prefix*" -- "$buffer"; and test $cursor_pos -le $prefix_len
141        return
142    end
143
144    commandline -f backward-delete-char
145end
146
147function __agentsh_guard_backward_kill_word
148    if test $__AGENTSH_PREFIX_ACTIVE -eq 0
149        commandline -f backward-kill-word
150        return
151    end
152
153    set -l prefix $__AGENTSH_PREFIX
154    set -l prefix_len (string length -- $prefix)
155    set -l buffer (commandline)
156    set -l cursor_pos (commandline -C)
157
158    if string match -q "$prefix*" -- "$buffer"; and test $cursor_pos -le $prefix_len
159        return
160    end
161
162    commandline -f backward-kill-word
163end
164
165# Set up key bindings
166if status is-interactive
167    # Bind Ctrl+X to toggle prefix
168    bind \cx __agentsh_toggle_prefix
169
170    # Bind multiple backspace variants
171    bind \b __agentsh_guard_backward_delete
172    bind \x7f __agentsh_guard_backward_delete
173    bind backspace __agentsh_guard_backward_delete
174    
175    # Bind Alt+Backspace variants
176    bind \eb __agentsh_guard_backward_kill_word
177    bind \e\x7f __agentsh_guard_backward_kill_word
178    
179    # Bind Ctrl+W
180    bind \cw __agentsh_guard_backward_kill_word
181    
182    # Bind in insert mode too
183    bind -M insert \b __agentsh_guard_backward_delete
184    bind -M insert \x7f __agentsh_guard_backward_delete
185    bind -M insert \eb __agentsh_guard_backward_kill_word
186    bind -M insert \e\x7f __agentsh_guard_backward_kill_word
187    bind -M insert \cw __agentsh_guard_backward_kill_word
188end
189