From 75e47a9beb4ca038aafa062e9ccc28bfd87bdd16 Mon Sep 17 00:00:00 2001 From: Myzel394 Date: Sat, 7 Jun 2025 23:10:28 +0200 Subject: [PATCH 1/7] feat: Add instructions for oh my zsh --- README.md | 25 ++++++++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index ebf0655..ce15abe 100644 --- a/README.md +++ b/README.md @@ -15,12 +15,31 @@ Please make sure you have the following dependencies installed: * [jq](https://github.com/jqlang/jq) * [curl](https://github.com/curl/curl) +### Oh My Zsh + +1. Clone `zsh-copilot` into $ZSH_CUSTOM/plugins (by default ~/.oh-my-zsh/custom/plugins) + ```sh -git clone https://github.com/Myzel394/zsh-copilot.git ~/.zsh-copilot -echo "source ~/.zsh-copilot/zsh-copilot.plugin.zsh" >> ~/.zshrc +git clone https://git.myzel394.app/Myzel394/zsh-copilot ${ZSH_CUSTOM:-~/.oh-my-zsh/custom}/plugins/zsh-autosuggestions ``` -### Configuration +2. Add `zsh-copilot` to the plugins array in your `.zshrc` file: + +```sh +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: From 035c9a0566b1475be4fb78794e2c95d53bca2a97 Mon Sep 17 00:00:00 2001 From: Myzel394 Date: Sat, 7 Jun 2025 23:12:22 +0200 Subject: [PATCH 2/7] fix: Fix oh my zsh paths --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index ce15abe..d109220 100644 --- a/README.md +++ b/README.md @@ -17,10 +17,10 @@ Please make sure you have the following dependencies installed: ### Oh My Zsh -1. Clone `zsh-copilot` into $ZSH_CUSTOM/plugins (by default ~/.oh-my-zsh/custom/plugins) +1. Clone `zsh-copilot` into $ZSH_CUSTOM/plugins (by default ~/.config/oh-my-zsh/custom/plugins) ```sh -git clone https://git.myzel394.app/Myzel394/zsh-copilot ${ZSH_CUSTOM:-~/.oh-my-zsh/custom}/plugins/zsh-autosuggestions +git clone https://git.myzel394.app/Myzel394/zsh-copilot ${ZSH_CUSTOM:-~/.config/oh-my-zsh/custom}/plugins/zsh-autosuggestions ``` 2. Add `zsh-copilot` to the plugins array in your `.zshrc` file: From 60de616c353ef4563de50302ccface0e6c62c32a Mon Sep 17 00:00:00 2001 From: Myzel394 Date: Sat, 7 Jun 2025 23:13:33 +0200 Subject: [PATCH 3/7] feat: Add git info --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index d109220..5343ffe 100644 --- a/README.md +++ b/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. From 3c649923489e3ccab128b446588a7755eaee2039 Mon Sep 17 00:00:00 2001 From: Myzel394 Date: Sat, 7 Jun 2025 23:15:51 +0200 Subject: [PATCH 4/7] fix: Improve README.md --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 5343ffe..ee40078 100644 --- a/README.md +++ b/README.md @@ -18,15 +18,15 @@ Please make sure you have the following dependencies installed: ### Oh My Zsh -1. Clone `zsh-copilot` into $ZSH_CUSTOM/plugins (by default ~/.config/oh-my-zsh/custom/plugins) +1. Clone `zsh-copilot` into `$ZSH_CUSTOM/plugins` (by default ~/.config/oh-my-zsh/custom/plugins) ```sh -git clone https://git.myzel394.app/Myzel394/zsh-copilot ${ZSH_CUSTOM:-~/.config/oh-my-zsh/custom}/plugins/zsh-autosuggestions +git clone https://git.myzel394.app/Myzel394/zsh-copilot ${ZSH_CUSTOM:-~/.config/oh-my-zsh/custom}/plugins/zsh-copilot ``` 2. Add `zsh-copilot` to the plugins array in your `.zshrc` file: -```sh +```bash plugins=( # your other plugins... zsh-autosuggestions From 411e9329e9751343c1230f59bcbb0b694a49dcc8 Mon Sep 17 00:00:00 2001 From: Myzel394 Date: Sat, 7 Jun 2025 23:20:43 +0200 Subject: [PATCH 5/7] fix: Overall improvements --- zsh-copilot.plugin.zsh | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/zsh-copilot.plugin.zsh b/zsh-copilot.plugin.zsh index e73637a..6a8eaae 100644 --- a/zsh-copilot.plugin.zsh +++ b/zsh-copilot.plugin.zsh @@ -10,11 +10,20 @@ typeset -g ZSH_COPILOT_DEBUG=false # New option to select AI provider -(( ! ${+ZSH_COPILOT_AI_PROVIDER} )) && - typeset -g ZSH_COPILOT_AI_PROVIDER="openai" +if [[ -z "$ZSH_COPILOT_AI_PROVIDER" ]]; then + if [[ -n "$OPENAI_API_KEY" ]]; then + 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 -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. 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 (=). @@ -36,6 +45,7 @@ read -r -d '' SYSTEM_PROMPT <<- EOM * 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). EOM +fi if [[ "$OSTYPE" == "darwin"* ]]; then SYSTEM="Your system is ${$(sw_vers | xargs | sed 's/ /./g')}." @@ -61,7 +71,7 @@ function _suggest_ai() { _zsh_autosuggest_clear 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') local data local response @@ -135,7 +145,8 @@ function zsh-copilot() { echo "Configurations:" 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_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 From 6c1ada57ab3f1aa83aef082753aded516c537591 Mon Sep 17 00:00:00 2001 From: Myzel394 Date: Sat, 7 Jun 2025 23:31:21 +0200 Subject: [PATCH 6/7] fix: Overall improvements --- zsh-copilot.plugin.zsh | 44 ++++++++++++++++++++++++++++-------------- 1 file changed, 29 insertions(+), 15 deletions(-) diff --git a/zsh-copilot.plugin.zsh b/zsh-copilot.plugin.zsh index 6a8eaae..71e0426 100644 --- a/zsh-copilot.plugin.zsh +++ b/zsh-copilot.plugin.zsh @@ -1,3 +1,5 @@ +#!/usr/bin/env zsh + # Default key binding (( ! ${+ZSH_COPILOT_KEY} )) && typeset -g ZSH_COPILOT_KEY='^z' @@ -47,24 +49,27 @@ read -r -d '' ZSH_COPILOT_SYSTEM_PROMPT <<- EOM EOM fi -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 - function _suggest_ai() { - local OPENAI_API_URL=${OPENAI_API_URL:-"api.openai.com"} - local ANTHROPIC_API_URL=${ANTHROPIC_API_URL:-"api.anthropic.com"} + #### Prepare environment + local openai_api_url=${OPENAI_API_URL:-"api.openai.com"} + local anthropic_api_url=${ANTHROPIC_API_URL:-"api.anthropic.com"} local context_info="" if [[ "$ZSH_COPILOT_SEND_CONTEXT" == 'true' ]]; then + local 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 + 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" + $system" fi - # Get input + ##### Get input local input=$(echo "${BUFFER:0:$CURSOR}" | tr '\n' ';') input=$(echo "$input" | sed 's/"/\\"/g') @@ -73,8 +78,10 @@ function _suggest_ai() { local full_prompt=$(echo "$ZSH_COPILOT_SYSTEM_PROMPT $context_info" | tr -d '\n') + ##### Fetch message local data local response + local message if [[ "$ZSH_COPILOT_AI_PROVIDER" == "openai" ]]; then data="{ @@ -90,12 +97,13 @@ function _suggest_ai() { } ] }" - response=$(curl "https://${OPENAI_API_URL}/v1/chat/completions" \ + response=$(curl "https://${openai_api_url}/v1/chat/completions" \ --silent \ -H "Content-Type: application/json" \ -H "Authorization: Bearer $OPENAI_API_KEY" \ -d "$data") - local message=$(echo "$response" | jq -r '.choices[0].message.content') + + message=$(echo "$response" | jq -r '.choices[0].message.content') elif [[ "$ZSH_COPILOT_AI_PROVIDER" == "anthropic" ]]; then data="{ \"model\": \"claude-3-5-sonnet-20240620\", @@ -108,18 +116,21 @@ function _suggest_ai() { } ] }" - response=$(curl "https://${ANTHROPIC_API_URL}/v1/messages" \ + response=$(curl "https://${anthropic_api_url}/v1/messages" \ --silent \ -H "Content-Type: application/json" \ -H "x-api-key: $ANTHROPIC_API_KEY" \ -H "anthropic-version: 2023-06-01" \ -d "$data") - local message=$(echo "$response" | jq -r '.content[0].text') + + message=$(echo "$response" | jq -r '.content[0].text') else echo "Invalid AI provider selected. Please choose 'openai' or 'anthropic'." return 1 fi + ##### Process response + local first_char=${message:0:1} local suggestion=${message:1:${#message}} @@ -128,6 +139,8 @@ function _suggest_ai() { echo "$(date);INPUT:$input;RESPONSE:$response;FIRST_CHAR:$first_char;SUGGESTION:$suggestion:DATA:$data" >> /tmp/zsh-copilot.log fi + ##### And now, let's actually show the suggestion to the user! + if [[ "$first_char" == '=' ]]; then # Reset user input BUFFER="" @@ -150,4 +163,5 @@ function zsh-copilot() { } zle -N _suggest_ai -bindkey $ZSH_COPILOT_KEY _suggest_ai +bindkey "$ZSH_COPILOT_KEY" _suggest_ai + From c1ce6cd97fd033dd4f4a9973f5f92c6f18f6b588 Mon Sep 17 00:00:00 2001 From: Myzel394 Date: Sat, 7 Jun 2025 23:47:19 +0200 Subject: [PATCH 7/7] feat: Improve prompt --- zsh-copilot.plugin.zsh | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/zsh-copilot.plugin.zsh b/zsh-copilot.plugin.zsh index 71e0426..145e459 100644 --- a/zsh-copilot.plugin.zsh +++ b/zsh-copilot.plugin.zsh @@ -32,15 +32,18 @@ read -r -d '' ZSH_COPILOT_SYSTEM_PROMPT <<- EOM 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!!! 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. 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 '=+'. - 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. + Your response will be run in the user's shell. Make sure input is escaped correctly if needed so. 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. Note that the double quote sign is escaped. Keep this in mind when you create quotes. Here are two examples: @@ -84,6 +87,7 @@ function _suggest_ai() { local message if [[ "$ZSH_COPILOT_AI_PROVIDER" == "openai" ]]; then + # OpenAI's API payload data="{ \"model\": \"gpt-4o-mini\", \"messages\": [ @@ -103,10 +107,11 @@ function _suggest_ai() { -H "Authorization: Bearer $OPENAI_API_KEY" \ -d "$data") - 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 + # Anthropic's API payload data="{ - \"model\": \"claude-3-5-sonnet-20240620\", + \"model\": \"claude-3-5-sonnet-latest\", \"max_tokens\": 1000, \"system\": \"$full_prompt\", \"messages\": [ @@ -123,7 +128,7 @@ function _suggest_ai() { -H "anthropic-version: 2023-06-01" \ -d "$data") - message=$(echo "$response" | jq -r '.content[0].text') + message=$(echo "$response" | tr -d '\n' | jq -r '.content[0].text') else echo "Invalid AI provider selected. Please choose 'openai' or 'anthropic'." return 1