diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..29c01fa --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,56 @@ +name: Release Binaries + +on: + push: + tags: + - "v*" + +jobs: + build: + name: Build and Release + runs-on: ubuntu-latest + permissions: + contents: write + steps: + - name: Checkout code + uses: actions/checkout@v3 + + - name: Set up Go + uses: actions/setup-go@v4 + with: + go-version: "1.21" + + - name: Set release version + run: echo "RELEASE_VERSION=${GITHUB_REF#refs/tags/}" >> $GITHUB_ENV + + - name: Build binaries + run: | + # Create a directory for binaries + mkdir -p ./dist + + # Build for Windows + GOOS=windows GOARCH=amd64 go build -o ./dist/shell-ai-${{ env.RELEASE_VERSION }}-windows-amd64.exe main.go + + # Build for macOS + GOOS=darwin GOARCH=amd64 go build -o ./dist/shell-ai-${{ env.RELEASE_VERSION }}-darwin-amd64 main.go + GOOS=darwin GOARCH=arm64 go build -o ./dist/shell-ai-${{ env.RELEASE_VERSION }}-darwin-arm64 main.go + + # Build for Linux + GOOS=linux GOARCH=amd64 go build -o ./dist/shell-ai-${{ env.RELEASE_VERSION }}-linux-amd64 main.go + GOOS=linux GOARCH=arm64 go build -o ./dist/shell-ai-${{ env.RELEASE_VERSION }}-linux-arm64 main.go + + # Make binaries executable + chmod +x ./dist/* + + # Create checksums + cd ./dist && sha256sum * > checksums.txt + + - name: Create Release + uses: softprops/action-gh-release@v1 + with: + name: "ShellAI ${{ env.RELEASE_VERSION }}" + draft: false + prerelease: false + generate_release_notes: true + files: | + ./dist/* diff --git a/README.md b/README.md index 2b8025b..705cf81 100644 --- a/README.md +++ b/README.md @@ -16,7 +16,7 @@ A delightfully minimal, yet remarkably powerful AI Shell Assistant. # About -For developers, referencing things online is inevitable – but one can only look up "how to do [X] in git" so many times before losing your mind. +For developers, referencing things online is inevitable – but one can only look up "how to do [X] in git" so many times before losing your mind.
@@ -31,14 +31,93 @@ _New: ShellAI now supports local models! See [Custom Model Configuration](#custo
### Homebrew
```bash
-brew tap ibigio/tap
+brew tap erfannf/tap
brew install shell-ai
```
-### Linux
+### Linux/macOS
```bash
-curl https://raw.githubusercontent.com/ibigio/shell-ai/main/install.sh | bash
+curl https://raw.githubusercontent.com/erfannf/shell-ai/main/install.sh | bash
+```
+
+### Windows
+
+```powershell
+Invoke-RestMethod -Uri https://raw.githubusercontent.com/erfannf/shell-ai/main/install.ps1 | powershell -Command -
+```
+
+### SSH Session Requirements
+
+If you're using ShellAI over an SSH session, you'll need to install clipboard utilities for the copy functionality to work:
+
+- **On Debian/Ubuntu-based systems**:
+ ```bash
+ sudo apt-get install xsel xclip
+ ```
+
+- **On Fedora/RHEL-based systems**:
+ ```bash
+ sudo dnf install xsel xclip
+ ```
+
+- **On Arch Linux**:
+ ```bash
+ sudo pacman -S xsel xclip
+ ```
+
+- **For Wayland users**:
+ ```bash
+ sudo apt-get install wl-clipboard # Debian/Ubuntu
+ sudo dnf install wl-clipboard # Fedora
+ sudo pacman -S wl-clipboard # Arch
+ ```
+
+- **For Termux users**:
+ ```bash
+ pkg install termux-api
+ ```
+
+Without these utilities, you'll see an error when attempting to copy text to clipboard.
+
+#### TTY Session Type Compatibility
+
+Clipboard functionality depends on your terminal session type:
+
+- **Local terminal sessions**: Clipboard should work without additional configuration.
+- **X11 forwarding**: Use `ssh -X` or `ssh -Y` to connect to your server to enable clipboard sharing between the server and your local machine.
+ ```bash
+ ssh -X user@host
+ ```
+- **SSH without X forwarding**: Clipboard functionality will be limited to the remote server's clipboard only, not your local machine.
+- **TTY sessions (no GUI)**: Clipboard functionality won't work as these sessions don't have access to a GUI clipboard.
+- **tmux/screen sessions**: May require additional configuration:
+ ```bash
+ # For tmux, ensure this in your ~/.tmux.conf
+ set -g set-clipboard on
+ ```
+
+If you're accessing a remote server and need to copy command output to your local machine, consider piping to a file and using `scp` or `rsync` to transfer it.
+
+# Uninstall
+
+### Homebrew
+
+```bash
+brew uninstall shell-ai
+brew untap erfannf/tap
+```
+
+### Linux/macOS
+
+```bash
+rm -f /usr/local/bin/q
+```
+
+### Windows
+
+```powershell
+Remove-Item -Path "$env:LOCALAPPDATA\Programs\shell-ai\q.exe" -Force
```
# Usage
@@ -58,13 +137,43 @@ Type `q` followed by a description of a shell command, code snippet, or general
### Configuration
-Set your [OpenAI API key](https://platform.openai.com/account/api-keys).
+#### Setting up API keys
+
+Set your [OpenAI API key](https://platform.openai.com/account/api-keys):
```bash
-export OPENAI_API_KEY=[your key]
+export OPENAI_API_KEY="your-api-key-here"
```
-For more options (like setting the default model), run:
+For persistent configuration, add the export command to your shell profile:
+
+- **Bash users** - Add to `~/.bashrc` or `~/.bash_profile`:
+ ```bash
+ echo 'export OPENAI_API_KEY="your-api-key-here"' >> ~/.bashrc
+ source ~/.bashrc
+ ```
+
+- **Zsh users** - Add to `~/.zshrc`:
+ ```bash
+ echo 'export OPENAI_API_KEY="your-api-key-here"' >> ~/.zshrc
+ source ~/.zshrc
+ ```
+
+- **Fish users** - Add to `~/.config/fish/config.fish`:
+ ```fish
+ set -Ux OPENAI_API_KEY "your-api-key-here"
+ ```
+
+- **Windows PowerShell users** - Add to your PowerShell profile:
+ ```powershell
+ [Environment]::SetEnvironmentVariable("OPENAI_API_KEY", "your-api-key-here", "User")
+ ```
+
+> **Important:** Never include the square brackets when setting your API key. Use the format `export OPENAI_API_KEY="sk-abcd1234"` not `export OPENAI_API_KEY=[sk-abcd1234]`.
+
+#### Changing default model
+
+For more configuration options (like setting the default model), run:
```bash
q config
@@ -118,7 +227,7 @@ You can now configure model prompts and even add your own model setups in the `~
### Config File Syntax
-````yaml
+```yaml
preferences:
default_model: gpt-4-1106-preview
@@ -139,7 +248,7 @@ models:
# other models ...
config_format_version: "1"
-````
+```
**Note:** The `auth_env_var` is set to `OPENAI_API_KEY` verbatim, not the key itself, so as to not keep sensitive information in the config file.
@@ -159,7 +268,7 @@ Here's what I did:
4. Finally I added the new `model` config to my `~/.shell-ai/config.yaml`, and wrestled with the prompt until it worked – bet you can do better. (As you can see, YAML is [flexible](https://docs.ansible.com/ansible/latest/reference_appendices/YAMLSyntax.html).)
-````yaml
+```yaml
models:
- name: stablelm-zephyr-3b.Q8_0
endpoint: http://127.0.0.1:8080/v1/chat/completions
@@ -191,7 +300,7 @@ models:
```
# other models ...
-````
+```
and also updated the default model (which you can also do from `q config`):
@@ -204,6 +313,37 @@ And huzzah! You can now use ShellAI on a plane.
(Fun fact, I implemented a good bit of the initial config TUI on a plane using this exact local model.)
+### Setting Up DeepSeek Models
+
+ShellAI now supports DeepSeek's powerful LLMs via their API. To use DeepSeek models:
+
+1. Create an account at [DeepSeek Platform](https://platform.deepseek.com)
+2. Generate an API key at https://platform.deepseek.com/api_keys
+3. Set your DeepSeek API key as an environment variable:
+
+```bash
+# For Linux/macOS (temporary)
+export DEEPSEEK_API_KEY="your-api-key-here"
+
+# For permanent setup on Linux/macOS
+echo 'export DEEPSEEK_API_KEY="your-api-key-here"' >> ~/.bashrc # for bash
+# or
+echo 'export DEEPSEEK_API_KEY="your-api-key-here"' >> ~/.zshrc # for zsh
+
+# For Windows (PowerShell)
+$env:DEEPSEEK_API_KEY = "your-api-key-here" # temporary
+# For permanent setup on Windows
+[Environment]::SetEnvironmentVariable("DEEPSEEK_API_KEY", "your-api-key-here", "User")
+```
+
+> **Important:** Do not include square brackets around your API key. The correct format is `export DEEPSEEK_API_KEY="your-key"`, not `export DEEPSEEK_API_KEY=[your-key]`.
+
+4. Run `q config` and select "Change Default Model" to switch to one of the DeepSeek models:
+ - `deepseek-chat`: General-purpose conversational model
+ - `deepseek-coder`: Specialized for code and programming tasks
+
+The DeepSeek models are already configured in ShellAI, so you don't need to modify your config file manually.
+
### Setting Up Azure OpenAI endpoint
Define `AZURE_OPENAI_API_KEY` environment variable and make few changes to the config file.
diff --git a/build.ps1 b/build.ps1
new file mode 100644
index 0000000..4948e06
--- /dev/null
+++ b/build.ps1
@@ -0,0 +1,67 @@
+$VERSION = "v1.2.3"
+$PLATFORMS = @(
+ @{GOOS = "windows"; GOARCH = "amd64"; ARCH_NAME = "x86_64"; Ext = ".exe" },
+ @{GOOS = "windows"; GOARCH = "386"; ARCH_NAME = "x86"; Ext = ".exe" },
+ @{GOOS = "linux"; GOARCH = "amd64"; ARCH_NAME = "x86_64"; Ext = "" },
+ @{GOOS = "linux"; GOARCH = "386"; ARCH_NAME = "x86"; Ext = "" },
+ @{GOOS = "linux"; GOARCH = "arm64"; ARCH_NAME = "arm64"; Ext = "" },
+ @{GOOS = "darwin"; GOARCH = "amd64"; ARCH_NAME = "x86_64"; Ext = "" },
+ @{GOOS = "darwin"; GOARCH = "arm64"; ARCH_NAME = "arm64"; Ext = "" }
+)
+
+# Create dist directory if it doesn't exist
+if (-not (Test-Path -Path "dist")) {
+ New-Item -ItemType Directory -Path "dist" | Out-Null
+}
+
+foreach ($platform in $PLATFORMS) {
+ $env:GOOS = $platform.GOOS
+ $env:GOARCH = $platform.GOARCH
+
+ # Create the filename compatible with install scripts
+ $outFile = "dist/shell-ai_$($platform.GOOS)_$($platform.ARCH_NAME)$($platform.Ext)"
+
+ Write-Host "Building for $($platform.GOOS)/$($platform.GOARCH)..."
+ go build -o $outFile main.go
+
+ if ($LASTEXITCODE -eq 0) {
+ Write-Host " Success: $outFile"
+
+ # Create tar.gz archive for non-Windows platforms
+ if ($platform.GOOS -ne "windows") {
+ $tarName = "shell-ai_$($platform.GOOS)_$($platform.ARCH_NAME).tar.gz"
+
+ # The commands below will work on Windows with tar installed
+ # Create a temporary directory for the binary
+ $tempDir = "dist/temp_$($platform.GOOS)_$($platform.ARCH_NAME)"
+ New-Item -ItemType Directory -Path $tempDir -Force | Out-Null
+
+ # Copy the binary to the temp directory with the simple name
+ Copy-Item -Path $outFile -Destination "$tempDir/shell-ai"
+
+ # Create tar.gz archive
+ Set-Location $tempDir
+ tar -czf "../$tarName" "shell-ai"
+ Set-Location "../.."
+
+ # Cleanup temp directory
+ Remove-Item -Path $tempDir -Recurse -Force
+
+ Write-Host " Created archive: dist/$tarName"
+ }
+ }
+ else {
+ Write-Host " Failed to build for $($platform.GOOS)/$($platform.GOARCH)" -ForegroundColor Red
+ }
+}
+
+# Create checksums file
+Set-Location dist
+$checksums = Get-ChildItem -File | Where-Object { $_.Name -ne "checksums.txt" } | ForEach-Object {
+ $hash = Get-FileHash -Path $_.FullName -Algorithm SHA256
+ "$($hash.Hash.ToLower()) $($_.Name)"
+}
+$checksums | Out-File -FilePath "checksums.txt" -Encoding utf8
+
+Write-Host "`nBuild completed. Binaries are in the dist/ directory."
+Write-Host "SHA256 checksums have been saved to dist/checksums.txt"
\ No newline at end of file
diff --git a/cli/cli.go b/cli/cli.go
index ea4a744..2d402e6 100644
--- a/cli/cli.go
+++ b/cli/cli.go
@@ -129,18 +129,23 @@ func (m model) getConnectionError(err error) string {
styleRed := lipgloss.NewStyle().Foreground(lipgloss.Color("9"))
styleGreen := lipgloss.NewStyle().Foreground(lipgloss.Color("2"))
styleDim := lipgloss.NewStyle().Faint(true).Width(m.maxWidth).PaddingLeft(2)
+
+ // Create a more generic error message
+ errorMsg := "Error: Failed to connect to the API service."
+
message := fmt.Sprintf("\n %v\n\n%v\n",
- styleRed.Render("Error: Failed to connect to OpenAI."),
+ styleRed.Render(errorMsg),
styleDim.Render(err.Error()))
+
+ // Add information for rate limit errors
if util.IsLikelyBillingError(err.Error()) {
- message = fmt.Sprintf("%v\n %v %v\n\n %v%v\n\n",
+ message = fmt.Sprintf("%v\n %v %v\n\n",
message,
styleGreen.Render("Hint:"),
- "You may need to set up billing. You can do so here:",
- styleGreen.Render("->"),
- styleDim.Render("https://platform.openai.com/account/billing"),
+ "You may have reached your rate limit. Check your API usage and billing status.",
)
}
+
return message
}
@@ -322,6 +327,24 @@ func printAPIKeyNotSetMessage(modelConfig ModelConfig) {
%s
4. (Recommended) Add that ^ line to your %s file.`, shellSyntax, profileScriptName)
+ msg2, _ := r.Render(message_string)
+ fmt.Printf("\n %v%v\n", msg1, msg2)
+ case "DEEPSEEK_API_KEY":
+ msg1 := styleRed.Render("DEEPSEEK_API_KEY environment variable not set.")
+
+ deepseekSyntax := shellSyntax
+ if runtime.GOOS == "windows" {
+ deepseekSyntax = "\n```powershell\n$env:DEEPSEEK_API_KEY = \"[your key]\"\n```"
+ } else {
+ deepseekSyntax = "\n```bash\nexport DEEPSEEK_API_KEY=[your key]\n```"
+ }
+
+ message_string := fmt.Sprintf(`
+ 1. Generate your API key at https://platform.deepseek.com/api_keys
+ 2. Set your key by running:
+ %s
+ 3. (Recommended) Add that ^ line to your %s file.`, deepseekSyntax, profileScriptName)
+
msg2, _ := r.Render(message_string)
fmt.Printf("\n %v%v\n", msg1, msg2)
default:
diff --git a/config/config.yaml b/config/config.yaml
index 0494480..f7e611e 100644
--- a/config/config.yaml
+++ b/config/config.yaml
@@ -29,5 +29,27 @@ models:
content: print hi
- role: assistant
content: "```bash\necho \"hi\"\n```"
+
+ - name: deepseek-chat
+ endpoint: https://api.deepseek.com/v1/chat/completions
+ auth_env_var: DEEPSEEK_API_KEY
+ prompt:
+ - role: system
+ content: You are a terminal assistant. Turn the natural language instructions into a terminal command. By default always only output code, and in a code block. However, if the user is clearly asking a question then answer it very briefly and well. Consider when the user request references a previous request.
+ - role: user
+ content: print hi
+ - role: assistant
+ content: "```bash\necho \"hi\"\n```"
+
+ - name: deepseek-coder
+ endpoint: https://api.deepseek.com/v1/chat/completions
+ auth_env_var: DEEPSEEK_API_KEY
+ prompt:
+ - role: system
+ content: You are a coding assistant. Always output code in a code block with the appropriate language tag. Provide concise solutions to coding problems. If explaining code, be brief. Consider when the user request references a previous request.
+ - role: user
+ content: write a recursive fibonacci function in python
+ - role: assistant
+ content: "```python\ndef fibonacci(n):\n if n <= 1:\n return n\n else:\n return fibonacci(n-1) + fibonacci(n-2)\n```"
config_format_version: "1"
diff --git a/install.ps1 b/install.ps1
index e6f6ab3..a6e7ded 100644
--- a/install.ps1
+++ b/install.ps1
@@ -1,5 +1,5 @@
param(
- [string]$repoowner = "ibigio",
+ [string]$repoowner = "erfannf",
[string]$reponame = "shell-ai",
[string]$toolname = "shell-ai",
[string]$toolsymlink = "q",
@@ -12,7 +12,7 @@ if ($help) {
Write-Host " shell-ai -help