mirror of
https://github.com/Myzel394/zsh-copilot.git
synced 2025-06-18 20:55:26 +02:00
Compare commits
8 Commits
0bc635926b
...
bcea2d2033
Author | SHA1 | Date | |
---|---|---|---|
![]() |
bcea2d2033 | ||
c1ce6cd97f | |||
6c1ada57ab | |||
411e9329e9 | |||
3c64992348 | |||
60de616c35 | |||
035c9a0566 | |||
75e47a9beb |
28
README.md
28
README.md
@ -1,5 +1,6 @@
|
|||||||
# zsh-copilot
|
**NOTICE**: I'm slowly migrating my repositories to my own Git server. Please visit this repository at [https://git.myzel394.app/Myzel394/zsh-copilot](https://git.myzel394.app/Myzel394/zsh-copilot) for the latest updates.
|
||||||
|
|
||||||
|
# zsh-copilot
|
||||||
|
|
||||||
Get suggestions **truly** in your shell. No `suggest` bullshit. Just press `CTRL + Z` and get your suggestion.
|
Get suggestions **truly** in your shell. No `suggest` bullshit. Just press `CTRL + Z` and get your suggestion.
|
||||||
|
|
||||||
@ -15,12 +16,31 @@ Please make sure you have the following dependencies installed:
|
|||||||
* [jq](https://github.com/jqlang/jq)
|
* [jq](https://github.com/jqlang/jq)
|
||||||
* [curl](https://github.com/curl/curl)
|
* [curl](https://github.com/curl/curl)
|
||||||
|
|
||||||
|
### Oh My Zsh
|
||||||
|
|
||||||
|
1. Clone `zsh-copilot` into `$ZSH_CUSTOM/plugins` (by default ~/.config/oh-my-zsh/custom/plugins)
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
git clone https://github.com/Myzel394/zsh-copilot.git ~/.zsh-copilot
|
git clone https://git.myzel394.app/Myzel394/zsh-copilot ${ZSH_CUSTOM:-~/.config/oh-my-zsh/custom}/plugins/zsh-copilot
|
||||||
echo "source ~/.zsh-copilot/zsh-copilot.plugin.zsh" >> ~/.zshrc
|
|
||||||
```
|
```
|
||||||
|
|
||||||
### Configuration
|
2. Add `zsh-copilot` to the plugins array in your `.zshrc` file:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
plugins=(
|
||||||
|
# your other plugins...
|
||||||
|
zsh-autosuggestions
|
||||||
|
)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Manual Installation
|
||||||
|
|
||||||
|
```sh
|
||||||
|
git clone https://git.myzel394.app/Myzel394/zsh-copilot ~/.config/zsh-copilot
|
||||||
|
echo "source ~/.config/zsh-copilot/zsh-copilot.plugin.zsh" >> ~/.zshrc
|
||||||
|
```
|
||||||
|
|
||||||
|
## Configuration
|
||||||
|
|
||||||
You need to have an API key for either OpenAI or Anthropic to use this plugin. Expose this via the appropriate environment variable:
|
You need to have an API key for either OpenAI or Anthropic to use this plugin. Expose this via the appropriate environment variable:
|
||||||
|
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
#!/usr/bin/env zsh
|
||||||
|
|
||||||
# Default key binding
|
# Default key binding
|
||||||
(( ! ${+ZSH_COPILOT_KEY} )) &&
|
(( ! ${+ZSH_COPILOT_KEY} )) &&
|
||||||
typeset -g ZSH_COPILOT_KEY='^z'
|
typeset -g ZSH_COPILOT_KEY='^z'
|
||||||
@ -10,63 +12,82 @@
|
|||||||
typeset -g ZSH_COPILOT_DEBUG=false
|
typeset -g ZSH_COPILOT_DEBUG=false
|
||||||
|
|
||||||
# New option to select AI provider
|
# New option to select AI provider
|
||||||
(( ! ${+ZSH_COPILOT_AI_PROVIDER} )) &&
|
if [[ -z "$ZSH_COPILOT_AI_PROVIDER" ]]; then
|
||||||
|
if [[ -n "$OPENAI_API_KEY" ]]; then
|
||||||
typeset -g ZSH_COPILOT_AI_PROVIDER="openai"
|
typeset -g ZSH_COPILOT_AI_PROVIDER="openai"
|
||||||
|
elif [[ -n "$ANTHROPIC_API_KEY" ]]; then
|
||||||
|
typeset -g ZSH_COPILOT_AI_PROVIDER="anthropic"
|
||||||
|
else
|
||||||
|
echo "No AI provider selected. Please set either OPENAI_API_KEY or ANTHROPIC_API_KEY."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
# System prompt
|
# System prompt
|
||||||
read -r -d '' SYSTEM_PROMPT <<- EOM
|
if [[ -z "$ZSH_COPILOT_SYSTEM_PROMPT" ]]; then
|
||||||
|
read -r -d '' ZSH_COPILOT_SYSTEM_PROMPT <<- EOM
|
||||||
You will be given the raw input of a shell command.
|
You will be given the raw input of a shell command.
|
||||||
Your task is to either complete the command or provide a new command that you think the user is trying to type.
|
Your task is to either complete the command or provide a new command that you think the user is trying to type.
|
||||||
If you return a completely new command for the user, prefix is with an equal sign (=).
|
If you return a completely new command for the user, prefix is with an equal sign (=).
|
||||||
If you return a completion for the user's command, prefix it with a plus sign (+).
|
If you return a completion for the user's command, prefix it with a plus sign (+).
|
||||||
MAKE SURE TO ONLY INCLUDE THE REST OF THE COMPLETION!!!
|
MAKE SURE TO ONLY INCLUDE THE REST OF THE COMPLETION!!!
|
||||||
Do not write any leading or trailing characters except if required for the completion to work.
|
Do not write any leading or trailing characters except if required for the completion to work.
|
||||||
|
|
||||||
Only respond with either a completion or a new command, not both.
|
Only respond with either a completion or a new command, not both.
|
||||||
Your response may only start with either a plus sign or an equal sign.
|
Your response may only start with either a plus sign or an equal sign.
|
||||||
Your response MAY NOT start with both! This means that your response IS NOT ALLOWED to start with '+=' or '=+'.
|
Your response MAY NOT start with both! This means that your response IS NOT ALLOWED to start with '+=' or '=+'.
|
||||||
You MAY explain the command by writing a short line after the comment symbol (#).
|
|
||||||
|
Your response MAY NOT contain any newlines!
|
||||||
|
Do NOT add any additional text, comments, or explanations to your response.
|
||||||
Do not ask for more information, you won't receive it.
|
Do not ask for more information, you won't receive it.
|
||||||
|
|
||||||
Your response will be run in the user's shell.
|
Your response will be run in the user's shell.
|
||||||
Make sure input is escaped correctly if needed so.
|
Make sure input is escaped correctly if needed so.
|
||||||
Your input should be able to run without any modifications to it.
|
Your input should be able to run without any modifications to it.
|
||||||
Don't you dare to return anything else other than a shell command!!!
|
|
||||||
DO NOT INTERACT WITH THE USER IN NATURAL LANGUAGE! If you do, you will be banned from the system.
|
DO NOT INTERACT WITH THE USER IN NATURAL LANGUAGE! If you do, you will be banned from the system.
|
||||||
Note that the double quote sign is escaped. Keep this in mind when you create quotes.
|
Note that the double quote sign is escaped. Keep this in mind when you create quotes.
|
||||||
Here are two examples:
|
Here are two examples:
|
||||||
* User input: 'list files in current directory'; Your response: '=ls' (ls is the builtin command for listing files)
|
* User input: 'list files in current directory'; Your response: '=ls' (ls is the builtin command for listing files)
|
||||||
* User input: 'cd /tm'; Your response: '+p' (/tmp is the standard temp folder on linux and mac).
|
* User input: 'cd /tm'; Your response: '+p' (/tmp is the standard temp folder on linux and mac).
|
||||||
EOM
|
EOM
|
||||||
|
|
||||||
if [[ "$OSTYPE" == "darwin"* ]]; then
|
|
||||||
SYSTEM="Your system is ${$(sw_vers | xargs | sed 's/ /./g')}."
|
|
||||||
else
|
|
||||||
SYSTEM="Your system is ${$(cat /etc/*-release | xargs | sed 's/ /,/g')}."
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
function _suggest_ai() {
|
function _suggest_ai() {
|
||||||
local OPENAI_API_URL=${OPENAI_API_URL:-"api.openai.com"}
|
#### Prepare environment
|
||||||
local ANTHROPIC_API_URL=${ANTHROPIC_API_URL:-"api.anthropic.com"}
|
local openai_api_url=${OPENAI_API_URL:-"api.openai.com"}
|
||||||
|
local anthropic_api_url=${ANTHROPIC_API_URL:-"api.anthropic.com"}
|
||||||
|
|
||||||
local context_info=""
|
local context_info=""
|
||||||
if [[ "$ZSH_COPILOT_SEND_CONTEXT" == 'true' ]]; then
|
if [[ "$ZSH_COPILOT_SEND_CONTEXT" == 'true' ]]; then
|
||||||
context_info="Context: You are user $(whoami) with id $(id) in directory $(pwd).
|
local system
|
||||||
Your shell is $(echo $SHELL) and your terminal is $(echo $TERM) running on $(uname -a).
|
|
||||||
$SYSTEM"
|
if [[ "$OSTYPE" == "darwin"* ]]; then
|
||||||
|
system="Your system is ${$(sw_vers | xargs | sed 's/ /./g')}."
|
||||||
|
else
|
||||||
|
system="Your system is ${$(cat /etc/*-release | xargs | sed 's/ /,/g')}."
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Get input
|
context_info="Context: You are user $(whoami) with id $(id) in directory $(pwd).
|
||||||
|
Your shell is $(echo $SHELL) and your terminal is $(echo $TERM) running on $(uname -a).
|
||||||
|
$system"
|
||||||
|
fi
|
||||||
|
|
||||||
|
##### Get input
|
||||||
local input=$(echo "${BUFFER:0:$CURSOR}" | tr '\n' ';')
|
local input=$(echo "${BUFFER:0:$CURSOR}" | tr '\n' ';')
|
||||||
input=$(echo "$input" | sed 's/"/\\"/g')
|
input=$(echo "$input" | sed 's/"/\\"/g')
|
||||||
|
|
||||||
_zsh_autosuggest_clear
|
_zsh_autosuggest_clear
|
||||||
zle -R "Thinking..."
|
zle -R "Thinking..."
|
||||||
|
|
||||||
local full_prompt=$(echo "$SYSTEM_PROMPT $context_info" | tr -d '\n')
|
local full_prompt=$(echo "$ZSH_COPILOT_SYSTEM_PROMPT $context_info" | tr -d '\n')
|
||||||
|
|
||||||
|
##### Fetch message
|
||||||
local data
|
local data
|
||||||
local response
|
local response
|
||||||
|
local message
|
||||||
|
|
||||||
if [[ "$ZSH_COPILOT_AI_PROVIDER" == "openai" ]]; then
|
if [[ "$ZSH_COPILOT_AI_PROVIDER" == "openai" ]]; then
|
||||||
|
# OpenAI's API payload
|
||||||
data="{
|
data="{
|
||||||
\"model\": \"gpt-4o-mini\",
|
\"model\": \"gpt-4o-mini\",
|
||||||
\"messages\": [
|
\"messages\": [
|
||||||
@ -80,15 +101,17 @@ function _suggest_ai() {
|
|||||||
}
|
}
|
||||||
]
|
]
|
||||||
}"
|
}"
|
||||||
response=$(curl "https://${OPENAI_API_URL}/v1/chat/completions" \
|
response=$(curl "https://${openai_api_url}/v1/chat/completions" \
|
||||||
--silent \
|
--silent \
|
||||||
-H "Content-Type: application/json" \
|
-H "Content-Type: application/json" \
|
||||||
-H "Authorization: Bearer $OPENAI_API_KEY" \
|
-H "Authorization: Bearer $OPENAI_API_KEY" \
|
||||||
-d "$data")
|
-d "$data")
|
||||||
local message=$(echo "$response" | jq -r '.choices[0].message.content')
|
|
||||||
|
message=$(echo "$response" | tr -d '\n' | jq -r '.choices[0].message.content')
|
||||||
elif [[ "$ZSH_COPILOT_AI_PROVIDER" == "anthropic" ]]; then
|
elif [[ "$ZSH_COPILOT_AI_PROVIDER" == "anthropic" ]]; then
|
||||||
|
# Anthropic's API payload
|
||||||
data="{
|
data="{
|
||||||
\"model\": \"claude-3-5-sonnet-20240620\",
|
\"model\": \"claude-3-5-sonnet-latest\",
|
||||||
\"max_tokens\": 1000,
|
\"max_tokens\": 1000,
|
||||||
\"system\": \"$full_prompt\",
|
\"system\": \"$full_prompt\",
|
||||||
\"messages\": [
|
\"messages\": [
|
||||||
@ -98,18 +121,21 @@ function _suggest_ai() {
|
|||||||
}
|
}
|
||||||
]
|
]
|
||||||
}"
|
}"
|
||||||
response=$(curl "https://${ANTHROPIC_API_URL}/v1/messages" \
|
response=$(curl "https://${anthropic_api_url}/v1/messages" \
|
||||||
--silent \
|
--silent \
|
||||||
-H "Content-Type: application/json" \
|
-H "Content-Type: application/json" \
|
||||||
-H "x-api-key: $ANTHROPIC_API_KEY" \
|
-H "x-api-key: $ANTHROPIC_API_KEY" \
|
||||||
-H "anthropic-version: 2023-06-01" \
|
-H "anthropic-version: 2023-06-01" \
|
||||||
-d "$data")
|
-d "$data")
|
||||||
local message=$(echo "$response" | jq -r '.content[0].text')
|
|
||||||
|
message=$(echo "$response" | tr -d '\n' | jq -r '.content[0].text')
|
||||||
else
|
else
|
||||||
echo "Invalid AI provider selected. Please choose 'openai' or 'anthropic'."
|
echo "Invalid AI provider selected. Please choose 'openai' or 'anthropic'."
|
||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
##### Process response
|
||||||
|
|
||||||
local first_char=${message:0:1}
|
local first_char=${message:0:1}
|
||||||
local suggestion=${message:1:${#message}}
|
local suggestion=${message:1:${#message}}
|
||||||
|
|
||||||
@ -118,6 +144,8 @@ function _suggest_ai() {
|
|||||||
echo "$(date);INPUT:$input;RESPONSE:$response;FIRST_CHAR:$first_char;SUGGESTION:$suggestion:DATA:$data" >> /tmp/zsh-copilot.log
|
echo "$(date);INPUT:$input;RESPONSE:$response;FIRST_CHAR:$first_char;SUGGESTION:$suggestion:DATA:$data" >> /tmp/zsh-copilot.log
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
##### And now, let's actually show the suggestion to the user!
|
||||||
|
|
||||||
if [[ "$first_char" == '=' ]]; then
|
if [[ "$first_char" == '=' ]]; then
|
||||||
# Reset user input
|
# Reset user input
|
||||||
BUFFER=""
|
BUFFER=""
|
||||||
@ -135,8 +163,10 @@ function zsh-copilot() {
|
|||||||
echo "Configurations:"
|
echo "Configurations:"
|
||||||
echo " - ZSH_COPILOT_KEY: Key to press to get suggestions (default: ^z, value: $ZSH_COPILOT_KEY)."
|
echo " - ZSH_COPILOT_KEY: Key to press to get suggestions (default: ^z, value: $ZSH_COPILOT_KEY)."
|
||||||
echo " - ZSH_COPILOT_SEND_CONTEXT: If \`true\`, zsh-copilot will send context information (whoami, shell, pwd, etc.) to the AI model (default: true, value: $ZSH_COPILOT_SEND_CONTEXT)."
|
echo " - ZSH_COPILOT_SEND_CONTEXT: If \`true\`, zsh-copilot will send context information (whoami, shell, pwd, etc.) to the AI model (default: true, value: $ZSH_COPILOT_SEND_CONTEXT)."
|
||||||
echo " - ZSH_COPILOT_AI_PROVIDER: AI provider to use ('openai' or 'anthropic', default: openai, value: $ZSH_COPILOT_AI_PROVIDER)."
|
echo " - ZSH_COPILOT_AI_PROVIDER: AI provider to use ('openai' or 'anthropic', value: $ZSH_COPILOT_AI_PROVIDER)."
|
||||||
|
echo " - ZSH_COPILOT_SYSTEM_PROMPT: System prompt to use for the AI model (uses a built-in prompt by default)."
|
||||||
}
|
}
|
||||||
|
|
||||||
zle -N _suggest_ai
|
zle -N _suggest_ai
|
||||||
bindkey $ZSH_COPILOT_KEY _suggest_ai
|
bindkey "$ZSH_COPILOT_KEY" _suggest_ai
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user