Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
246 changes: 246 additions & 0 deletions PLUGINS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,246 @@
# Creating Plugins for zsh-copilot

zsh-copilot supports three types of plugins:

| Type | Purpose | Function Pattern |
|------|---------|------------------|
| **Provider** | AI backend (API calls) | `_zsh_copilot_provider_<name>` |
| **Context** | Add info to prompts | `_zsh_copilot_context_<name>` |
| **Transform** | Process AI responses | `_zsh_copilot_transform_<name>` |

## Provider Plugins

Provider plugins handle communication with AI backends.

### Interface

```zsh
_zsh_copilot_provider_<name>() {
local input="$1" # User's input (escaped)
local system_prompt="$2" # System prompt + context

# Make API call, parse response
# Write result to /tmp/zsh_copilot_suggestion
# Write errors to /tmp/.zsh_copilot_error
# Return 0 on success, 1 on failure
}
```

### Example: Ollama Provider

```zsh
# ~/.config/zsh-copilot/plugins/providers/ollama.zsh

_zsh_copilot_provider_ollama() {
local input="$1"
local system_prompt="$2"
local model=${OLLAMA_MODEL:-"llama2"}
local url=${OLLAMA_URL:-"http://localhost:11434"}

local data="{
\"model\": \"$model\",
\"prompt\": \"$system_prompt\n\nUser: $input\",
\"stream\": false
}"

local response
response=$(curl "$url/api/generate" \
--silent \
-H "Content-Type: application/json" \
-d "$data")
local response_code=$?

if [[ $response_code -ne 0 ]]; then
echo "Error connecting to Ollama at $url" > /tmp/.zsh_copilot_error
return 1
fi

local message
message=$(echo "$response" | jq -r '.response')

if [[ -z "$message" || "$message" == "null" ]]; then
echo "Empty response from Ollama" > /tmp/.zsh_copilot_error
return 1
fi

echo "$message" > /tmp/zsh_copilot_suggestion
}
```

### Usage

```zsh
# In .zshrc
source ~/.config/zsh-copilot/plugins/providers/ollama.zsh
export ZSH_COPILOT_AI_PROVIDER="ollama"
```

---

## Context Plugins

Context plugins add information to the prompt sent to the AI. They chain together - each plugin receives the output of previous plugins.

### Interface

```zsh
_zsh_copilot_context_<name>() {
local context="$1" # Current context from previous plugins

local my_info="..."

# Append to existing context
if [[ -n "$context" ]]; then
echo "$context $my_info"
else
echo "$my_info"
fi
}
```

### Example: Git Context

```zsh
# ~/.config/zsh-copilot/plugins/context/git.zsh

_zsh_copilot_context_git() {
local context="$1"

local git_info=""
if git rev-parse --is-inside-work-tree &>/dev/null; then
local branch=$(git branch --show-current 2>/dev/null)
local status=$(git status --porcelain 2>/dev/null | wc -l | tr -d ' ')
git_info="Git repo on branch '$branch' with $status uncommitted changes."
fi

if [[ -n "$context" ]]; then
echo "$context $git_info"
else
echo "$git_info"
fi
}
```

### Example: Node.js Project Context

```zsh
# ~/.config/zsh-copilot/plugins/context/nodejs.zsh

_zsh_copilot_context_nodejs() {
local context="$1"

local node_info=""
if [[ -f "package.json" ]]; then
local name=$(jq -r '.name // empty' package.json 2>/dev/null)
local pm="npm"
[[ -f "yarn.lock" ]] && pm="yarn"
[[ -f "pnpm-lock.yaml" ]] && pm="pnpm"
[[ -f "bun.lockb" ]] && pm="bun"
node_info="Node.js project${name:+ '$name'} using $pm."
fi

if [[ -n "$context" ]]; then
echo "$context $node_info"
else
echo "$node_info"
fi
}
```

---

## Transform Plugins

Transform plugins process the AI response before displaying it. They chain together.

### Interface

```zsh
_zsh_copilot_transform_<name>() {
local message="$1" # Current message (possibly from previous transforms)

# Process message
echo "$processed_message"
}
```

### Example: Strip Explanations

```zsh
# ~/.config/zsh-copilot/plugins/transform/strip-explanation.zsh

_zsh_copilot_transform_strip_explanation() {
local message="$1"

# Remove common AI prefixes
message="${message#Here is the command: }"
message="${message#The command is: }"
message="${message#Try this: }"
message="${message#Run: }"

echo "$message"
}
```

### Example: Dangerous Command Warning

```zsh
# ~/.config/zsh-copilot/plugins/transform/warn-dangerous.zsh

_zsh_copilot_transform_warn_dangerous() {
local message="$1"

# Check for dangerous patterns
if [[ "$message" == *"rm -rf"* ]] || \
[[ "$message" == *"sudo rm"* ]] || \
[[ "$message" == *"> /dev/"* ]]; then
echo "# WARNING: Potentially dangerous command\n$message"
else
echo "$message"
fi
}
```

---

## Plugin Loading

Plugins are loaded by sourcing them in your `.zshrc`:

```zsh
# Source zsh-copilot first
source ~/.config/zsh-copilot/zsh-copilot.plugin.zsh

# Then source plugins
source ~/.config/zsh-copilot/plugins/providers/openai.zsh
source ~/.config/zsh-copilot/plugins/context/system.zsh
source ~/.config/zsh-copilot/plugins/context/git.zsh
source ~/.config/zsh-copilot/plugins/transform/normalize.zsh
```

### Custom Plugin Directory

You can keep your custom plugins anywhere. A common pattern:

```zsh
# Custom plugins in ~/.zsh-copilot/plugins/
for plugin in ~/.zsh-copilot/plugins/**/*.zsh(N); do
source "$plugin"
done
```

---

## Tips

1. **Keep plugins fast** - They run on every suggestion request
2. **Handle errors gracefully** - Don't break the shell if something fails
3. **Use `(N)` glob qualifier** - Prevents errors when no files match
4. **Test incrementally** - Source plugins manually before adding to `.zshrc`
5. **Check `$ZSH_COPILOT_DEBUG`** - Log debug info when enabled

```zsh
if [[ "$ZSH_COPILOT_DEBUG" == 'true' ]]; then
echo "{\"plugin\":\"my-plugin\",\"data\":\"...\"}" >> /tmp/zsh-copilot.log
fi
```
83 changes: 52 additions & 31 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,69 +10,90 @@ https://github.com/Myzel394/zsh-copilot/assets/50424412/ed2bc8ac-ce49-4012-ab73-

### Dependencies

Please make sure you have the following dependencies installed:

* [zsh-autosuggestions](https://github.com/zsh-users/zsh-autosuggestions)
* [jq](https://github.com/jqlang/jq)
* [curl](https://github.com/curl/curl)

### Oh My Zsh
### Recommended Installation

1. Clone `zsh-copilot` into `$ZSH_CUSTOM/plugins` (by default ~/.config/oh-my-zsh/custom/plugins)
It's highly recommended to git clone zsh-copilot into `~/.config/zsh-copilot`. zsh-copilot uses a plugin system and this makes it easy to load the built-in plugins.

```sh
git clone https://git.myzel394.app/Myzel394/zsh-copilot ${ZSH_CUSTOM:-~/.config/oh-my-zsh/custom}/plugins/zsh-copilot
git clone https://git.myzel394.app/Myzel394/zsh-copilot ~/.config/zsh-copilot
```

2. Add `zsh-copilot` to the plugins array in your `.zshrc` file:

```bash
plugins=(
# your other plugins...
zsh-autosuggestions
)
Add to your `.zshrc`:
```sh
source ~/.config/zsh-copilot/zsh-copilot.plugin.zsh
```

### Manual Installation
### Oh My Zsh

```sh
git clone https://git.myzel394.app/Myzel394/zsh-copilot ~/.config/zsh-copilot
echo "source ~/.config/zsh-copilot/zsh-copilot.plugin.zsh" >> ~/.zshrc
git clone https://git.myzel394.app/Myzel394/zsh-copilot ${ZSH_CUSTOM:-~/.oh-my-zsh/custom}/plugins/zsh-copilot
```

Add `zsh-copilot` to the plugins array in your `.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:
### Loading a Provider

You need to source a provider plugin. Add one of these to your `.zshrc` after sourcing zsh-copilot:

For OpenAI (default):
**OpenAI:**
```sh
export OPENAI_API_KEY=<your-openai-api-key>
export OPENAI_API_KEY=<your-api-key>
source ~/.config/zsh-copilot/plugins/providers/openai.zsh
```

For Anthropic:
**Anthropic:**
```sh
export ANTHROPIC_API_KEY=<your-anthropic-api-key>
export ANTHROPIC_API_KEY=<your-api-key>
source ~/.config/zsh-copilot/plugins/providers/anthropic.zsh
```

You can configure the AI provider using the `ZSH_COPILOT_AI_PROVIDER` variable:
### Loading Plugins

Source the plugins you want:

```sh
export ZSH_COPILOT_AI_PROVIDER="openai" # or "anthropic"
# Provider (required - pick one)
source ~/.config/zsh-copilot/plugins/providers/openai.zsh

# Context plugins (optional - adds info to prompts)
source ~/.config/zsh-copilot/plugins/context/system.zsh

# Transform plugins (optional - processes AI responses)
source ~/.config/zsh-copilot/plugins/transform/normalize.zsh
```

Other configuration options:
### Built-in Plugins

- `ZSH_COPILOT_KEY`: Key to press to get suggestions (default: ^z)
- `ZSH_COPILOT_SEND_CONTEXT`: If `true`, zsh-copilot will send context information to the AI model (default: true)
- `ZSH_COPILOT_DEBUG`: Enable debug logging (default: false)
Take a look at the [plugins directory](./plugins) for built-in plugins.

To see all available configurations and their current values, run:
| Type | Plugin | Description |
|------|--------|-------------|
| Provider | `openai` | OpenAI API (gpt-4o-mini) |
| Provider | `anthropic` | Anthropic API (claude-3-haiku) |
| Context | `system` | Adds user, shell, terminal, OS info |
| Context | `git` | Adds git repo, branch, and uncommitted changes info |
| Transform | `normalize` | Trims whitespace, removes quotes/code blocks |

```sh
zsh-copilot
```
### Options

| Variable | Default | Description |
|----------|---------|-------------|
| `ZSH_COPILOT_KEY` | `^z` | Key binding for suggestions |
| `ZSH_COPILOT_SEND_CONTEXT` | `true` | Send context info to AI |
| `ZSH_COPILOT_DEBUG` | `false` | Enable debug logging |

Run `zsh-copilot` to see current configuration.

## Usage

Type in your command or your message and press `CTRL + Z` to get your suggestion!
Type your command or message and press `CTRL + Z` to get a suggestion.

## Creating Custom Plugins

See [PLUGINS.md](PLUGINS.md) for documentation on creating your own providers, context plugins, and transform plugins.
5 changes: 5 additions & 0 deletions _debug.zsh
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
#!/usr/bin/env zsh

ZSH_COPILOT_DEBUG=true

source ./zsh-copilot.plugin.zsh && zsh-copilot
Loading