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