Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bdbf31f0b9 | ||
|
|
4e6560efb9 | ||
|
|
f2f6efdd35 | ||
|
|
b106787a50 | ||
|
|
e1b2ce483f | ||
|
|
c42d94c465 | ||
|
|
f879a94c95 | ||
|
|
6d05d5a7c3 | ||
|
|
2c5003e3fc | ||
|
|
1b22acbc58 | ||
|
|
7648a2d790 | ||
|
|
789ff3c59b | ||
|
|
259858df5f | ||
|
|
289f6af3de | ||
|
|
cded7ee9fd | ||
|
|
c02721ce6b | ||
|
|
5d7a9a98b3 | ||
|
|
ce8bf37f15 | ||
|
|
f33dff8772 |
@@ -0,0 +1,37 @@
|
|||||||
|
name: build
|
||||||
|
|
||||||
|
on:
|
||||||
|
workflow_dispatch:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- main
|
||||||
|
|
||||||
|
concurrency: ${{ github.workflow }}-${{ github.ref }}
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: write
|
||||||
|
packages: write
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
|
|
||||||
|
- run: git fetch --force --tags
|
||||||
|
|
||||||
|
- uses: actions/setup-go@v5
|
||||||
|
with:
|
||||||
|
go-version: ">=1.23.2"
|
||||||
|
cache: true
|
||||||
|
cache-dependency-path: go.sum
|
||||||
|
|
||||||
|
- run: go mod download
|
||||||
|
|
||||||
|
- uses: goreleaser/goreleaser-action@v6
|
||||||
|
with:
|
||||||
|
distribution: goreleaser
|
||||||
|
version: latest
|
||||||
|
args: build --snapshot --clean
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
name: release
|
||||||
|
|
||||||
|
on:
|
||||||
|
workflow_dispatch:
|
||||||
|
push:
|
||||||
|
tags:
|
||||||
|
- "*"
|
||||||
|
|
||||||
|
concurrency: ${{ github.workflow }}-${{ github.ref }}
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: write
|
||||||
|
packages: write
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
goreleaser:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
|
|
||||||
|
- run: git fetch --force --tags
|
||||||
|
|
||||||
|
- uses: actions/setup-go@v5
|
||||||
|
with:
|
||||||
|
go-version: ">=1.23.2"
|
||||||
|
cache: true
|
||||||
|
cache-dependency-path: go.sum
|
||||||
|
|
||||||
|
- run: go mod download
|
||||||
|
|
||||||
|
- uses: goreleaser/goreleaser-action@v6
|
||||||
|
with:
|
||||||
|
distribution: goreleaser
|
||||||
|
version: latest
|
||||||
|
args: release --clean
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.HOMEBREW_GITHUB_TOKEN }}
|
||||||
|
AUR_KEY: ${{ secrets.AUR_KEY }}
|
||||||
@@ -0,0 +1,78 @@
|
|||||||
|
version: 2
|
||||||
|
project_name: opencode
|
||||||
|
before:
|
||||||
|
hooks:
|
||||||
|
builds:
|
||||||
|
- env:
|
||||||
|
- CGO_ENABLED=1
|
||||||
|
goos:
|
||||||
|
- linux
|
||||||
|
- darwin
|
||||||
|
goarch:
|
||||||
|
- amd64
|
||||||
|
- arm64
|
||||||
|
ldflags:
|
||||||
|
- -s -w -X github.com/opencode-ai/opencode/internal/version.Version={{.Version}}
|
||||||
|
main: ./main.go
|
||||||
|
|
||||||
|
archives:
|
||||||
|
- format: tar.gz
|
||||||
|
# this name template makes the OS and Arch compatible with the results of uname.
|
||||||
|
name_template: >-
|
||||||
|
opencode-
|
||||||
|
{{- if eq .Os "darwin" }}mac-
|
||||||
|
{{- else if eq .Os "windows" }}windows-
|
||||||
|
{{- else if eq .Os "linux" }}linux-{{end}}
|
||||||
|
{{- if eq .Arch "amd64" }}x86_64
|
||||||
|
{{- else if eq .Arch "#86" }}i386
|
||||||
|
{{- else }}{{ .Arch }}{{ end }}
|
||||||
|
{{- if .Arm }}v{{ .Arm }}{{ end }}
|
||||||
|
# use zip for windows archives
|
||||||
|
format_overrides:
|
||||||
|
- goos: windows
|
||||||
|
format: zip
|
||||||
|
checksum:
|
||||||
|
name_template: "checksums.txt"
|
||||||
|
snapshot:
|
||||||
|
name_template: "0.0.0-{{ .Timestamp }}"
|
||||||
|
aurs:
|
||||||
|
- name: opencode
|
||||||
|
homepage: "https://github.com/opencode-ai/opencode"
|
||||||
|
description: "terminal based agent that can build anything"
|
||||||
|
maintainers:
|
||||||
|
- "kujtimiihoxha <kujtimii.h@gmail.com>"
|
||||||
|
license: "MIT"
|
||||||
|
private_key: "{{ .Env.AUR_KEY }}"
|
||||||
|
git_url: "ssh://aur@aur.archlinux.org/opencode-bin.git"
|
||||||
|
provides:
|
||||||
|
- opencode
|
||||||
|
conflicts:
|
||||||
|
- opencode
|
||||||
|
package: |-
|
||||||
|
install -Dm755 ./opencode "${pkgdir}/usr/bin/opencode"
|
||||||
|
brews:
|
||||||
|
- repository:
|
||||||
|
owner: opencode-ai
|
||||||
|
name: homebrew-tap
|
||||||
|
nfpms:
|
||||||
|
- maintainer: kujtimiihoxha
|
||||||
|
description: terminal based agent that can build anything
|
||||||
|
formats:
|
||||||
|
- deb
|
||||||
|
- rpm
|
||||||
|
file_name_template: >-
|
||||||
|
{{ .ProjectName }}-
|
||||||
|
{{- if eq .Os "darwin" }}mac
|
||||||
|
{{- else }}{{ .Os }}{{ end }}-{{ .Arch }}
|
||||||
|
|
||||||
|
changelog:
|
||||||
|
sort: asc
|
||||||
|
filters:
|
||||||
|
exclude:
|
||||||
|
- "^docs:"
|
||||||
|
- "^doc:"
|
||||||
|
- "^test:"
|
||||||
|
- "^ci:"
|
||||||
|
- "^ignore:"
|
||||||
|
- "^example:"
|
||||||
|
- "^wip:"
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
# OpenCode
|
# ⌬ OpenCode
|
||||||
|
|
||||||
> **⚠️ Early Development Notice:** This project is in early development and is not yet ready for production use. Features may change, break, or be incomplete. Use at your own risk.
|
> **⚠️ Early Development Notice:** This project is in early development and is not yet ready for production use. Features may change, break, or be incomplete. Use at your own risk.
|
||||||
|
|
||||||
@@ -24,7 +24,7 @@ OpenCode is a Go-based CLI application that brings AI assistance to your termina
|
|||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Coming soon
|
# Coming soon
|
||||||
go install github.com/kujtimiihoxha/opencode@latest
|
go install github.com/opencode-ai/opencode@latest
|
||||||
```
|
```
|
||||||
|
|
||||||
## Configuration
|
## Configuration
|
||||||
@@ -341,7 +341,7 @@ While the LSP client implementation supports the full LSP protocol (including co
|
|||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Clone the repository
|
# Clone the repository
|
||||||
git clone https://github.com/kujtimiihoxha/opencode.git
|
git clone https://github.com/opencode-ai/opencode.git
|
||||||
cd opencode
|
cd opencode
|
||||||
|
|
||||||
# Build
|
# Build
|
||||||
|
|||||||
+13
-7
@@ -8,13 +8,14 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
tea "github.com/charmbracelet/bubbletea"
|
tea "github.com/charmbracelet/bubbletea"
|
||||||
"github.com/kujtimiihoxha/opencode/internal/app"
|
"github.com/opencode-ai/opencode/internal/app"
|
||||||
"github.com/kujtimiihoxha/opencode/internal/config"
|
"github.com/opencode-ai/opencode/internal/config"
|
||||||
"github.com/kujtimiihoxha/opencode/internal/db"
|
"github.com/opencode-ai/opencode/internal/db"
|
||||||
"github.com/kujtimiihoxha/opencode/internal/llm/agent"
|
"github.com/opencode-ai/opencode/internal/llm/agent"
|
||||||
"github.com/kujtimiihoxha/opencode/internal/logging"
|
"github.com/opencode-ai/opencode/internal/logging"
|
||||||
"github.com/kujtimiihoxha/opencode/internal/pubsub"
|
"github.com/opencode-ai/opencode/internal/pubsub"
|
||||||
"github.com/kujtimiihoxha/opencode/internal/tui"
|
"github.com/opencode-ai/opencode/internal/tui"
|
||||||
|
"github.com/opencode-ai/opencode/internal/version"
|
||||||
zone "github.com/lrstanley/bubblezone"
|
zone "github.com/lrstanley/bubblezone"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
)
|
)
|
||||||
@@ -31,6 +32,10 @@ to assist developers in writing, debugging, and understanding code directly from
|
|||||||
cmd.Help()
|
cmd.Help()
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
if cmd.Flag("version").Changed {
|
||||||
|
fmt.Println(version.Version)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// Load the config
|
// Load the config
|
||||||
debug, _ := cmd.Flags().GetBool("debug")
|
debug, _ := cmd.Flags().GetBool("debug")
|
||||||
@@ -247,6 +252,7 @@ func Execute() {
|
|||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
rootCmd.Flags().BoolP("help", "h", false, "Help")
|
rootCmd.Flags().BoolP("help", "h", false, "Help")
|
||||||
|
rootCmd.Flags().BoolP("version", "v", false, "Version")
|
||||||
rootCmd.Flags().BoolP("debug", "d", false, "Debug")
|
rootCmd.Flags().BoolP("debug", "d", false, "Debug")
|
||||||
rootCmd.Flags().StringP("cwd", "c", "", "Current working directory")
|
rootCmd.Flags().StringP("cwd", "c", "", "Current working directory")
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-2
@@ -5,8 +5,8 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
"github.com/kujtimiihoxha/opencode/internal/config"
|
"github.com/opencode-ai/opencode/internal/config"
|
||||||
"github.com/kujtimiihoxha/opencode/internal/llm/models"
|
"github.com/opencode-ai/opencode/internal/llm/models"
|
||||||
)
|
)
|
||||||
|
|
||||||
// JSONSchemaType represents a JSON Schema type
|
// JSONSchemaType represents a JSON Schema type
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
module github.com/kujtimiihoxha/opencode
|
module github.com/opencode-ai/opencode
|
||||||
|
|
||||||
go 1.24.0
|
go 1.24.0
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,180 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -euo pipefail
|
||||||
|
APP=opencode
|
||||||
|
|
||||||
|
RED='\033[0;31m'
|
||||||
|
GREEN='\033[0;32m'
|
||||||
|
YELLOW='\033[1;33m'
|
||||||
|
ORANGE='\033[38;2;255;140;0m'
|
||||||
|
NC='\033[0m' # No Color
|
||||||
|
|
||||||
|
requested_version=${VERSION:-}
|
||||||
|
|
||||||
|
os=$(uname -s | tr '[:upper:]' '[:lower:]')
|
||||||
|
if [[ "$os" == "darwin" ]]; then
|
||||||
|
os="mac"
|
||||||
|
fi
|
||||||
|
arch=$(uname -m)
|
||||||
|
|
||||||
|
if [[ "$arch" == "aarch64" ]]; then
|
||||||
|
arch="arm64"
|
||||||
|
fi
|
||||||
|
|
||||||
|
filename="$APP-$os-$arch.tar.gz"
|
||||||
|
|
||||||
|
|
||||||
|
case "$filename" in
|
||||||
|
*"-linux-"*)
|
||||||
|
[[ "$arch" == "x86_64" || "$arch" == "arm64" || "$arch" == "i386" ]] || exit 1
|
||||||
|
;;
|
||||||
|
*"-mac-"*)
|
||||||
|
[[ "$arch" == "x86_64" || "$arch" == "arm64" ]] || exit 1
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
echo "${RED}Unsupported OS/Arch: $os/$arch${NC}"
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
INSTALL_DIR=$HOME/.opencode/bin
|
||||||
|
mkdir -p "$INSTALL_DIR"
|
||||||
|
|
||||||
|
if [ -z "$requested_version" ]; then
|
||||||
|
url="https://github.com/opencode-ai/opencode/releases/latest/download/$filename"
|
||||||
|
specific_version=$(curl -s https://api.github.com/repos/opencode-ai/opencode/releases/latest | awk -F'"' '/"tag_name": "/ {gsub(/^v/, "", $4); print $4}')
|
||||||
|
|
||||||
|
if [[ $? -ne 0 ]]; then
|
||||||
|
echo "${RED}Failed to fetch version information${NC}"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
url="https://github.com/opencode-ai/opencode/releases/download/v${requested_version}/$filename"
|
||||||
|
specific_version=$requested_version
|
||||||
|
fi
|
||||||
|
|
||||||
|
print_message() {
|
||||||
|
local level=$1
|
||||||
|
local message=$2
|
||||||
|
local color=""
|
||||||
|
|
||||||
|
case $level in
|
||||||
|
info) color="${GREEN}" ;;
|
||||||
|
warning) color="${YELLOW}" ;;
|
||||||
|
error) color="${RED}" ;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
echo -e "${color}${message}${NC}"
|
||||||
|
}
|
||||||
|
|
||||||
|
check_version() {
|
||||||
|
if command -v opencode >/dev/null 2>&1; then
|
||||||
|
opencode_path=$(which opencode)
|
||||||
|
|
||||||
|
|
||||||
|
## TODO: check if version is installed
|
||||||
|
# installed_version=$(opencode version)
|
||||||
|
installed_version="0.0.1"
|
||||||
|
installed_version=$(echo $installed_version | awk '{print $2}')
|
||||||
|
|
||||||
|
if [[ "$installed_version" != "$specific_version" ]]; then
|
||||||
|
print_message info "Installed version: ${YELLOW}$installed_version."
|
||||||
|
else
|
||||||
|
print_message info "Version ${YELLOW}$specific_version${GREEN} already installed"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
download_and_install() {
|
||||||
|
print_message info "Downloading ${ORANGE}opencode ${GREEN}version: ${YELLOW}$specific_version ${GREEN}..."
|
||||||
|
mkdir -p opencodetmp && cd opencodetmp
|
||||||
|
curl -# -L $url | tar xz
|
||||||
|
mv opencode $INSTALL_DIR
|
||||||
|
cd .. && rm -rf opencodetmp
|
||||||
|
}
|
||||||
|
|
||||||
|
check_version
|
||||||
|
download_and_install
|
||||||
|
|
||||||
|
|
||||||
|
add_to_path() {
|
||||||
|
local config_file=$1
|
||||||
|
local command=$2
|
||||||
|
|
||||||
|
if [[ -w $config_file ]]; then
|
||||||
|
echo -e "\n# opencode" >> "$config_file"
|
||||||
|
echo "$command" >> "$config_file"
|
||||||
|
print_message info "Successfully added ${ORANGE}opencode ${GREEN}to \$PATH in $config_file"
|
||||||
|
else
|
||||||
|
print_message warning "Manually add the directory to $config_file (or similar):"
|
||||||
|
print_message info " $command"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
XDG_CONFIG_HOME=${XDG_CONFIG_HOME:-$HOME/.config}
|
||||||
|
|
||||||
|
current_shell=$(basename "$SHELL")
|
||||||
|
case $current_shell in
|
||||||
|
fish)
|
||||||
|
config_files="$HOME/.config/fish/config.fish"
|
||||||
|
;;
|
||||||
|
zsh)
|
||||||
|
config_files="$HOME/.zshrc $HOME/.zshenv $XDG_CONFIG_HOME/zsh/.zshrc $XDG_CONFIG_HOME/zsh/.zshenv"
|
||||||
|
;;
|
||||||
|
bash)
|
||||||
|
config_files="$HOME/.bashrc $HOME/.bash_profile $HOME/.profile $XDG_CONFIG_HOME/bash/.bashrc $XDG_CONFIG_HOME/bash/.bash_profile"
|
||||||
|
;;
|
||||||
|
ash)
|
||||||
|
config_files="$HOME/.ashrc $HOME/.profile /etc/profile"
|
||||||
|
;;
|
||||||
|
sh)
|
||||||
|
config_files="$HOME/.ashrc $HOME/.profile /etc/profile"
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
# Default case if none of the above matches
|
||||||
|
config_files="$HOME/.bashrc $HOME/.bash_profile $XDG_CONFIG_HOME/bash/.bashrc $XDG_CONFIG_HOME/bash/.bash_profile"
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
config_file=""
|
||||||
|
for file in $config_files; do
|
||||||
|
if [[ -f $file ]]; then
|
||||||
|
config_file=$file
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
if [[ -z $config_file ]]; then
|
||||||
|
print_message error "No config file found for $current_shell. Checked files: ${config_files[@]}"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ ":$PATH:" != *":$INSTALL_DIR:"* ]]; then
|
||||||
|
case $current_shell in
|
||||||
|
fish)
|
||||||
|
add_to_path "$config_file" "fish_add_path $INSTALL_DIR"
|
||||||
|
;;
|
||||||
|
zsh)
|
||||||
|
add_to_path "$config_file" "export PATH=$INSTALL_DIR:\$PATH"
|
||||||
|
;;
|
||||||
|
bash)
|
||||||
|
add_to_path "$config_file" "export PATH=$INSTALL_DIR:\$PATH"
|
||||||
|
;;
|
||||||
|
ash)
|
||||||
|
add_to_path "$config_file" "export PATH=$INSTALL_DIR:\$PATH"
|
||||||
|
;;
|
||||||
|
sh)
|
||||||
|
add_to_path "$config_file" "export PATH=$INSTALL_DIR:\$PATH"
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
print_message warning "Manually add the directory to $config_file (or similar):"
|
||||||
|
print_message info " export PATH=$INSTALL_DIR:\$PATH"
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -n "${GITHUB_ACTIONS-}" ] && [ "${GITHUB_ACTIONS}" == "true" ]; then
|
||||||
|
echo "$INSTALL_DIR" >> $GITHUB_PATH
|
||||||
|
print_message info "Added $INSTALL_DIR to \$GITHUB_PATH"
|
||||||
|
fi
|
||||||
|
|
||||||
+9
-9
@@ -7,15 +7,15 @@ import (
|
|||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/kujtimiihoxha/opencode/internal/config"
|
"github.com/opencode-ai/opencode/internal/config"
|
||||||
"github.com/kujtimiihoxha/opencode/internal/db"
|
"github.com/opencode-ai/opencode/internal/db"
|
||||||
"github.com/kujtimiihoxha/opencode/internal/history"
|
"github.com/opencode-ai/opencode/internal/history"
|
||||||
"github.com/kujtimiihoxha/opencode/internal/llm/agent"
|
"github.com/opencode-ai/opencode/internal/llm/agent"
|
||||||
"github.com/kujtimiihoxha/opencode/internal/logging"
|
"github.com/opencode-ai/opencode/internal/logging"
|
||||||
"github.com/kujtimiihoxha/opencode/internal/lsp"
|
"github.com/opencode-ai/opencode/internal/lsp"
|
||||||
"github.com/kujtimiihoxha/opencode/internal/message"
|
"github.com/opencode-ai/opencode/internal/message"
|
||||||
"github.com/kujtimiihoxha/opencode/internal/permission"
|
"github.com/opencode-ai/opencode/internal/permission"
|
||||||
"github.com/kujtimiihoxha/opencode/internal/session"
|
"github.com/opencode-ai/opencode/internal/session"
|
||||||
)
|
)
|
||||||
|
|
||||||
type App struct {
|
type App struct {
|
||||||
|
|||||||
+4
-4
@@ -4,10 +4,10 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/kujtimiihoxha/opencode/internal/config"
|
"github.com/opencode-ai/opencode/internal/config"
|
||||||
"github.com/kujtimiihoxha/opencode/internal/logging"
|
"github.com/opencode-ai/opencode/internal/logging"
|
||||||
"github.com/kujtimiihoxha/opencode/internal/lsp"
|
"github.com/opencode-ai/opencode/internal/lsp"
|
||||||
"github.com/kujtimiihoxha/opencode/internal/lsp/watcher"
|
"github.com/opencode-ai/opencode/internal/lsp/watcher"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (app *App) initLSPClients(ctx context.Context) {
|
func (app *App) initLSPClients(ctx context.Context) {
|
||||||
|
|||||||
+31
-24
@@ -7,8 +7,8 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/kujtimiihoxha/opencode/internal/llm/models"
|
"github.com/opencode-ai/opencode/internal/llm/models"
|
||||||
"github.com/kujtimiihoxha/opencode/internal/logging"
|
"github.com/opencode-ai/opencode/internal/logging"
|
||||||
"github.com/spf13/viper"
|
"github.com/spf13/viper"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -177,6 +177,7 @@ func configureViper() {
|
|||||||
viper.SetConfigType("json")
|
viper.SetConfigType("json")
|
||||||
viper.AddConfigPath("$HOME")
|
viper.AddConfigPath("$HOME")
|
||||||
viper.AddConfigPath(fmt.Sprintf("$XDG_CONFIG_HOME/%s", appName))
|
viper.AddConfigPath(fmt.Sprintf("$XDG_CONFIG_HOME/%s", appName))
|
||||||
|
viper.AddConfigPath(fmt.Sprintf("$HOME/.config/%s", appName))
|
||||||
viper.SetEnvPrefix(strings.ToUpper(appName))
|
viper.SetEnvPrefix(strings.ToUpper(appName))
|
||||||
viper.AutomaticEnv()
|
viper.AutomaticEnv()
|
||||||
}
|
}
|
||||||
@@ -199,22 +200,16 @@ func setDefaults(debug bool) {
|
|||||||
// 1. Anthropic
|
// 1. Anthropic
|
||||||
// 2. OpenAI
|
// 2. OpenAI
|
||||||
// 3. Google Gemini
|
// 3. Google Gemini
|
||||||
// 4. AWS Bedrock
|
// 4. Groq
|
||||||
|
// 5. AWS Bedrock
|
||||||
func setProviderDefaults() {
|
func setProviderDefaults() {
|
||||||
// Groq configuration
|
// Anthropic configuration
|
||||||
if apiKey := os.Getenv("GROQ_API_KEY"); apiKey != "" {
|
if apiKey := os.Getenv("ANTHROPIC_API_KEY"); apiKey != "" {
|
||||||
viper.SetDefault("providers.groq.apiKey", apiKey)
|
viper.SetDefault("providers.anthropic.apiKey", apiKey)
|
||||||
viper.SetDefault("agents.coder.model", models.QWENQwq)
|
viper.SetDefault("agents.coder.model", models.Claude37Sonnet)
|
||||||
viper.SetDefault("agents.task.model", models.QWENQwq)
|
viper.SetDefault("agents.task.model", models.Claude37Sonnet)
|
||||||
viper.SetDefault("agents.title.model", models.QWENQwq)
|
viper.SetDefault("agents.title.model", models.Claude37Sonnet)
|
||||||
}
|
return
|
||||||
|
|
||||||
// Google Gemini configuration
|
|
||||||
if apiKey := os.Getenv("GEMINI_API_KEY"); apiKey != "" {
|
|
||||||
viper.SetDefault("providers.gemini.apiKey", apiKey)
|
|
||||||
viper.SetDefault("agents.coder.model", models.Gemini25)
|
|
||||||
viper.SetDefault("agents.task.model", models.Gemini25Flash)
|
|
||||||
viper.SetDefault("agents.title.model", models.Gemini25Flash)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// OpenAI configuration
|
// OpenAI configuration
|
||||||
@@ -223,21 +218,33 @@ func setProviderDefaults() {
|
|||||||
viper.SetDefault("agents.coder.model", models.GPT41)
|
viper.SetDefault("agents.coder.model", models.GPT41)
|
||||||
viper.SetDefault("agents.task.model", models.GPT41Mini)
|
viper.SetDefault("agents.task.model", models.GPT41Mini)
|
||||||
viper.SetDefault("agents.title.model", models.GPT41Mini)
|
viper.SetDefault("agents.title.model", models.GPT41Mini)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Anthropic configuration
|
// Google Gemini configuration
|
||||||
if apiKey := os.Getenv("ANTHROPIC_API_KEY"); apiKey != "" {
|
if apiKey := os.Getenv("GEMINI_API_KEY"); apiKey != "" {
|
||||||
viper.SetDefault("providers.anthropic.apiKey", apiKey)
|
viper.SetDefault("providers.gemini.apiKey", apiKey)
|
||||||
viper.SetDefault("agents.coder.model", models.Claude37Sonnet)
|
viper.SetDefault("agents.coder.model", models.Gemini25)
|
||||||
viper.SetDefault("agents.task.model", models.Claude37Sonnet)
|
viper.SetDefault("agents.task.model", models.Gemini25Flash)
|
||||||
viper.SetDefault("agents.title.model", models.Claude37Sonnet)
|
viper.SetDefault("agents.title.model", models.Gemini25Flash)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Groq configuration
|
||||||
|
if apiKey := os.Getenv("GROQ_API_KEY"); apiKey != "" {
|
||||||
|
viper.SetDefault("providers.groq.apiKey", apiKey)
|
||||||
|
viper.SetDefault("agents.coder.model", models.QWENQwq)
|
||||||
|
viper.SetDefault("agents.task.model", models.QWENQwq)
|
||||||
|
viper.SetDefault("agents.title.model", models.QWENQwq)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// AWS Bedrock configuration
|
||||||
if hasAWSCredentials() {
|
if hasAWSCredentials() {
|
||||||
viper.SetDefault("agents.coder.model", models.BedrockClaude37Sonnet)
|
viper.SetDefault("agents.coder.model", models.BedrockClaude37Sonnet)
|
||||||
viper.SetDefault("agents.task.model", models.BedrockClaude37Sonnet)
|
viper.SetDefault("agents.task.model", models.BedrockClaude37Sonnet)
|
||||||
viper.SetDefault("agents.title.model", models.BedrockClaude37Sonnet)
|
viper.SetDefault("agents.title.model", models.BedrockClaude37Sonnet)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -12,8 +12,8 @@ import (
|
|||||||
"github.com/golang-migrate/migrate/v4/database/sqlite3"
|
"github.com/golang-migrate/migrate/v4/database/sqlite3"
|
||||||
_ "github.com/mattn/go-sqlite3"
|
_ "github.com/mattn/go-sqlite3"
|
||||||
|
|
||||||
"github.com/kujtimiihoxha/opencode/internal/config"
|
"github.com/opencode-ai/opencode/internal/config"
|
||||||
"github.com/kujtimiihoxha/opencode/internal/logging"
|
"github.com/opencode-ai/opencode/internal/logging"
|
||||||
)
|
)
|
||||||
|
|
||||||
func Connect() (*sql.DB, error) {
|
func Connect() (*sql.DB, error) {
|
||||||
|
|||||||
@@ -19,8 +19,8 @@ import (
|
|||||||
"github.com/charmbracelet/x/ansi"
|
"github.com/charmbracelet/x/ansi"
|
||||||
"github.com/go-git/go-git/v5"
|
"github.com/go-git/go-git/v5"
|
||||||
"github.com/go-git/go-git/v5/plumbing/object"
|
"github.com/go-git/go-git/v5/plumbing/object"
|
||||||
"github.com/kujtimiihoxha/opencode/internal/config"
|
"github.com/opencode-ai/opencode/internal/config"
|
||||||
"github.com/kujtimiihoxha/opencode/internal/logging"
|
"github.com/opencode-ai/opencode/internal/logging"
|
||||||
"github.com/sergi/go-diff/diffmatchpatch"
|
"github.com/sergi/go-diff/diffmatchpatch"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -9,8 +9,8 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
"github.com/kujtimiihoxha/opencode/internal/db"
|
"github.com/opencode-ai/opencode/internal/db"
|
||||||
"github.com/kujtimiihoxha/opencode/internal/pubsub"
|
"github.com/opencode-ai/opencode/internal/pubsub"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
|||||||
@@ -5,11 +5,11 @@ import (
|
|||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/kujtimiihoxha/opencode/internal/config"
|
"github.com/opencode-ai/opencode/internal/config"
|
||||||
"github.com/kujtimiihoxha/opencode/internal/llm/tools"
|
"github.com/opencode-ai/opencode/internal/llm/tools"
|
||||||
"github.com/kujtimiihoxha/opencode/internal/lsp"
|
"github.com/opencode-ai/opencode/internal/lsp"
|
||||||
"github.com/kujtimiihoxha/opencode/internal/message"
|
"github.com/opencode-ai/opencode/internal/message"
|
||||||
"github.com/kujtimiihoxha/opencode/internal/session"
|
"github.com/opencode-ai/opencode/internal/session"
|
||||||
)
|
)
|
||||||
|
|
||||||
type agentTool struct {
|
type agentTool struct {
|
||||||
|
|||||||
@@ -7,15 +7,15 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"github.com/kujtimiihoxha/opencode/internal/config"
|
"github.com/opencode-ai/opencode/internal/config"
|
||||||
"github.com/kujtimiihoxha/opencode/internal/llm/models"
|
"github.com/opencode-ai/opencode/internal/llm/models"
|
||||||
"github.com/kujtimiihoxha/opencode/internal/llm/prompt"
|
"github.com/opencode-ai/opencode/internal/llm/prompt"
|
||||||
"github.com/kujtimiihoxha/opencode/internal/llm/provider"
|
"github.com/opencode-ai/opencode/internal/llm/provider"
|
||||||
"github.com/kujtimiihoxha/opencode/internal/llm/tools"
|
"github.com/opencode-ai/opencode/internal/llm/tools"
|
||||||
"github.com/kujtimiihoxha/opencode/internal/logging"
|
"github.com/opencode-ai/opencode/internal/logging"
|
||||||
"github.com/kujtimiihoxha/opencode/internal/message"
|
"github.com/opencode-ai/opencode/internal/message"
|
||||||
"github.com/kujtimiihoxha/opencode/internal/permission"
|
"github.com/opencode-ai/opencode/internal/permission"
|
||||||
"github.com/kujtimiihoxha/opencode/internal/session"
|
"github.com/opencode-ai/opencode/internal/session"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Common errors
|
// Common errors
|
||||||
|
|||||||
@@ -5,11 +5,11 @@ import (
|
|||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/kujtimiihoxha/opencode/internal/config"
|
"github.com/opencode-ai/opencode/internal/config"
|
||||||
"github.com/kujtimiihoxha/opencode/internal/llm/tools"
|
"github.com/opencode-ai/opencode/internal/llm/tools"
|
||||||
"github.com/kujtimiihoxha/opencode/internal/logging"
|
"github.com/opencode-ai/opencode/internal/logging"
|
||||||
"github.com/kujtimiihoxha/opencode/internal/permission"
|
"github.com/opencode-ai/opencode/internal/permission"
|
||||||
"github.com/kujtimiihoxha/opencode/internal/version"
|
"github.com/opencode-ai/opencode/internal/version"
|
||||||
|
|
||||||
"github.com/mark3labs/mcp-go/client"
|
"github.com/mark3labs/mcp-go/client"
|
||||||
"github.com/mark3labs/mcp-go/mcp"
|
"github.com/mark3labs/mcp-go/mcp"
|
||||||
|
|||||||
@@ -3,12 +3,12 @@ package agent
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
|
||||||
"github.com/kujtimiihoxha/opencode/internal/history"
|
"github.com/opencode-ai/opencode/internal/history"
|
||||||
"github.com/kujtimiihoxha/opencode/internal/llm/tools"
|
"github.com/opencode-ai/opencode/internal/llm/tools"
|
||||||
"github.com/kujtimiihoxha/opencode/internal/lsp"
|
"github.com/opencode-ai/opencode/internal/lsp"
|
||||||
"github.com/kujtimiihoxha/opencode/internal/message"
|
"github.com/opencode-ai/opencode/internal/message"
|
||||||
"github.com/kujtimiihoxha/opencode/internal/permission"
|
"github.com/opencode-ai/opencode/internal/permission"
|
||||||
"github.com/kujtimiihoxha/opencode/internal/session"
|
"github.com/opencode-ai/opencode/internal/session"
|
||||||
)
|
)
|
||||||
|
|
||||||
func CoderAgentTools(
|
func CoderAgentTools(
|
||||||
|
|||||||
@@ -8,9 +8,9 @@ import (
|
|||||||
"runtime"
|
"runtime"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/kujtimiihoxha/opencode/internal/config"
|
"github.com/opencode-ai/opencode/internal/config"
|
||||||
"github.com/kujtimiihoxha/opencode/internal/llm/models"
|
"github.com/opencode-ai/opencode/internal/llm/models"
|
||||||
"github.com/kujtimiihoxha/opencode/internal/llm/tools"
|
"github.com/opencode-ai/opencode/internal/llm/tools"
|
||||||
)
|
)
|
||||||
|
|
||||||
func CoderPrompt(provider models.ModelProvider) string {
|
func CoderPrompt(provider models.ModelProvider) string {
|
||||||
|
|||||||
@@ -5,8 +5,8 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
|
||||||
"github.com/kujtimiihoxha/opencode/internal/config"
|
"github.com/opencode-ai/opencode/internal/config"
|
||||||
"github.com/kujtimiihoxha/opencode/internal/llm/models"
|
"github.com/opencode-ai/opencode/internal/llm/models"
|
||||||
)
|
)
|
||||||
|
|
||||||
// contextFiles is a list of potential context files to check for
|
// contextFiles is a list of potential context files to check for
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ package prompt
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/kujtimiihoxha/opencode/internal/llm/models"
|
"github.com/opencode-ai/opencode/internal/llm/models"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TaskPrompt(_ models.ModelProvider) string {
|
func TaskPrompt(_ models.ModelProvider) string {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
package prompt
|
package prompt
|
||||||
|
|
||||||
import "github.com/kujtimiihoxha/opencode/internal/llm/models"
|
import "github.com/opencode-ai/opencode/internal/llm/models"
|
||||||
|
|
||||||
func TitlePrompt(_ models.ModelProvider) string {
|
func TitlePrompt(_ models.ModelProvider) string {
|
||||||
return `you will generate a short title based on the first message a user begins a conversation with
|
return `you will generate a short title based on the first message a user begins a conversation with
|
||||||
|
|||||||
@@ -12,10 +12,10 @@ import (
|
|||||||
"github.com/anthropics/anthropic-sdk-go"
|
"github.com/anthropics/anthropic-sdk-go"
|
||||||
"github.com/anthropics/anthropic-sdk-go/bedrock"
|
"github.com/anthropics/anthropic-sdk-go/bedrock"
|
||||||
"github.com/anthropics/anthropic-sdk-go/option"
|
"github.com/anthropics/anthropic-sdk-go/option"
|
||||||
"github.com/kujtimiihoxha/opencode/internal/config"
|
"github.com/opencode-ai/opencode/internal/config"
|
||||||
"github.com/kujtimiihoxha/opencode/internal/llm/tools"
|
"github.com/opencode-ai/opencode/internal/llm/tools"
|
||||||
"github.com/kujtimiihoxha/opencode/internal/logging"
|
"github.com/opencode-ai/opencode/internal/logging"
|
||||||
"github.com/kujtimiihoxha/opencode/internal/message"
|
"github.com/opencode-ai/opencode/internal/message"
|
||||||
)
|
)
|
||||||
|
|
||||||
type anthropicOptions struct {
|
type anthropicOptions struct {
|
||||||
|
|||||||
@@ -7,8 +7,8 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/kujtimiihoxha/opencode/internal/llm/tools"
|
"github.com/opencode-ai/opencode/internal/llm/tools"
|
||||||
"github.com/kujtimiihoxha/opencode/internal/message"
|
"github.com/opencode-ai/opencode/internal/message"
|
||||||
)
|
)
|
||||||
|
|
||||||
type bedrockOptions struct {
|
type bedrockOptions struct {
|
||||||
|
|||||||
@@ -11,10 +11,10 @@ import (
|
|||||||
|
|
||||||
"github.com/google/generative-ai-go/genai"
|
"github.com/google/generative-ai-go/genai"
|
||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
"github.com/kujtimiihoxha/opencode/internal/config"
|
"github.com/opencode-ai/opencode/internal/config"
|
||||||
"github.com/kujtimiihoxha/opencode/internal/llm/tools"
|
"github.com/opencode-ai/opencode/internal/llm/tools"
|
||||||
"github.com/kujtimiihoxha/opencode/internal/logging"
|
"github.com/opencode-ai/opencode/internal/logging"
|
||||||
"github.com/kujtimiihoxha/opencode/internal/message"
|
"github.com/opencode-ai/opencode/internal/message"
|
||||||
"google.golang.org/api/iterator"
|
"google.golang.org/api/iterator"
|
||||||
"google.golang.org/api/option"
|
"google.golang.org/api/option"
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -8,10 +8,10 @@ import (
|
|||||||
"io"
|
"io"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/kujtimiihoxha/opencode/internal/config"
|
"github.com/opencode-ai/opencode/internal/config"
|
||||||
"github.com/kujtimiihoxha/opencode/internal/llm/tools"
|
"github.com/opencode-ai/opencode/internal/llm/tools"
|
||||||
"github.com/kujtimiihoxha/opencode/internal/logging"
|
"github.com/opencode-ai/opencode/internal/logging"
|
||||||
"github.com/kujtimiihoxha/opencode/internal/message"
|
"github.com/opencode-ai/opencode/internal/message"
|
||||||
"github.com/openai/openai-go"
|
"github.com/openai/openai-go"
|
||||||
"github.com/openai/openai-go/option"
|
"github.com/openai/openai-go/option"
|
||||||
"github.com/openai/openai-go/shared"
|
"github.com/openai/openai-go/shared"
|
||||||
|
|||||||
@@ -4,9 +4,9 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/kujtimiihoxha/opencode/internal/llm/models"
|
"github.com/opencode-ai/opencode/internal/llm/models"
|
||||||
"github.com/kujtimiihoxha/opencode/internal/llm/tools"
|
"github.com/opencode-ai/opencode/internal/llm/tools"
|
||||||
"github.com/kujtimiihoxha/opencode/internal/message"
|
"github.com/opencode-ai/opencode/internal/message"
|
||||||
)
|
)
|
||||||
|
|
||||||
type EventType string
|
type EventType string
|
||||||
|
|||||||
@@ -7,9 +7,9 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/kujtimiihoxha/opencode/internal/config"
|
"github.com/opencode-ai/opencode/internal/config"
|
||||||
"github.com/kujtimiihoxha/opencode/internal/llm/tools/shell"
|
"github.com/opencode-ai/opencode/internal/llm/tools/shell"
|
||||||
"github.com/kujtimiihoxha/opencode/internal/permission"
|
"github.com/opencode-ai/opencode/internal/permission"
|
||||||
)
|
)
|
||||||
|
|
||||||
type BashParams struct {
|
type BashParams struct {
|
||||||
|
|||||||
@@ -9,8 +9,8 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/kujtimiihoxha/opencode/internal/lsp"
|
"github.com/opencode-ai/opencode/internal/lsp"
|
||||||
"github.com/kujtimiihoxha/opencode/internal/lsp/protocol"
|
"github.com/opencode-ai/opencode/internal/lsp/protocol"
|
||||||
)
|
)
|
||||||
|
|
||||||
type DiagnosticsParams struct {
|
type DiagnosticsParams struct {
|
||||||
|
|||||||
@@ -9,12 +9,12 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/kujtimiihoxha/opencode/internal/config"
|
"github.com/opencode-ai/opencode/internal/config"
|
||||||
"github.com/kujtimiihoxha/opencode/internal/diff"
|
"github.com/opencode-ai/opencode/internal/diff"
|
||||||
"github.com/kujtimiihoxha/opencode/internal/history"
|
"github.com/opencode-ai/opencode/internal/history"
|
||||||
"github.com/kujtimiihoxha/opencode/internal/logging"
|
"github.com/opencode-ai/opencode/internal/logging"
|
||||||
"github.com/kujtimiihoxha/opencode/internal/lsp"
|
"github.com/opencode-ai/opencode/internal/lsp"
|
||||||
"github.com/kujtimiihoxha/opencode/internal/permission"
|
"github.com/opencode-ai/opencode/internal/permission"
|
||||||
)
|
)
|
||||||
|
|
||||||
type EditParams struct {
|
type EditParams struct {
|
||||||
|
|||||||
@@ -11,8 +11,8 @@ import (
|
|||||||
|
|
||||||
md "github.com/JohannesKaufmann/html-to-markdown"
|
md "github.com/JohannesKaufmann/html-to-markdown"
|
||||||
"github.com/PuerkitoBio/goquery"
|
"github.com/PuerkitoBio/goquery"
|
||||||
"github.com/kujtimiihoxha/opencode/internal/config"
|
"github.com/opencode-ai/opencode/internal/config"
|
||||||
"github.com/kujtimiihoxha/opencode/internal/permission"
|
"github.com/opencode-ai/opencode/internal/permission"
|
||||||
)
|
)
|
||||||
|
|
||||||
type FetchParams struct {
|
type FetchParams struct {
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/bmatcuk/doublestar/v4"
|
"github.com/bmatcuk/doublestar/v4"
|
||||||
"github.com/kujtimiihoxha/opencode/internal/config"
|
"github.com/opencode-ai/opencode/internal/config"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/kujtimiihoxha/opencode/internal/config"
|
"github.com/opencode-ai/opencode/internal/config"
|
||||||
)
|
)
|
||||||
|
|
||||||
type GrepParams struct {
|
type GrepParams struct {
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import (
|
|||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/kujtimiihoxha/opencode/internal/config"
|
"github.com/opencode-ai/opencode/internal/config"
|
||||||
)
|
)
|
||||||
|
|
||||||
type LSParams struct {
|
type LSParams struct {
|
||||||
|
|||||||
@@ -8,12 +8,12 @@ import (
|
|||||||
"path/filepath"
|
"path/filepath"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/kujtimiihoxha/opencode/internal/config"
|
"github.com/opencode-ai/opencode/internal/config"
|
||||||
"github.com/kujtimiihoxha/opencode/internal/diff"
|
"github.com/opencode-ai/opencode/internal/diff"
|
||||||
"github.com/kujtimiihoxha/opencode/internal/history"
|
"github.com/opencode-ai/opencode/internal/history"
|
||||||
"github.com/kujtimiihoxha/opencode/internal/logging"
|
"github.com/opencode-ai/opencode/internal/logging"
|
||||||
"github.com/kujtimiihoxha/opencode/internal/lsp"
|
"github.com/opencode-ai/opencode/internal/lsp"
|
||||||
"github.com/kujtimiihoxha/opencode/internal/permission"
|
"github.com/opencode-ai/opencode/internal/permission"
|
||||||
)
|
)
|
||||||
|
|
||||||
type PatchParams struct {
|
type PatchParams struct {
|
||||||
|
|||||||
@@ -10,8 +10,8 @@ import (
|
|||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/kujtimiihoxha/opencode/internal/config"
|
"github.com/opencode-ai/opencode/internal/config"
|
||||||
"github.com/kujtimiihoxha/opencode/internal/lsp"
|
"github.com/opencode-ai/opencode/internal/lsp"
|
||||||
)
|
)
|
||||||
|
|
||||||
type ViewParams struct {
|
type ViewParams struct {
|
||||||
|
|||||||
@@ -9,12 +9,12 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/kujtimiihoxha/opencode/internal/config"
|
"github.com/opencode-ai/opencode/internal/config"
|
||||||
"github.com/kujtimiihoxha/opencode/internal/diff"
|
"github.com/opencode-ai/opencode/internal/diff"
|
||||||
"github.com/kujtimiihoxha/opencode/internal/history"
|
"github.com/opencode-ai/opencode/internal/history"
|
||||||
"github.com/kujtimiihoxha/opencode/internal/logging"
|
"github.com/opencode-ai/opencode/internal/logging"
|
||||||
"github.com/kujtimiihoxha/opencode/internal/lsp"
|
"github.com/opencode-ai/opencode/internal/lsp"
|
||||||
"github.com/kujtimiihoxha/opencode/internal/permission"
|
"github.com/opencode-ai/opencode/internal/permission"
|
||||||
)
|
)
|
||||||
|
|
||||||
type WriteParams struct {
|
type WriteParams struct {
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/go-logfmt/logfmt"
|
"github.com/go-logfmt/logfmt"
|
||||||
"github.com/kujtimiihoxha/opencode/internal/pubsub"
|
"github.com/opencode-ai/opencode/internal/pubsub"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
|||||||
@@ -14,9 +14,9 @@ import (
|
|||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/kujtimiihoxha/opencode/internal/config"
|
"github.com/opencode-ai/opencode/internal/config"
|
||||||
"github.com/kujtimiihoxha/opencode/internal/logging"
|
"github.com/opencode-ai/opencode/internal/logging"
|
||||||
"github.com/kujtimiihoxha/opencode/internal/lsp/protocol"
|
"github.com/opencode-ai/opencode/internal/lsp/protocol"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Client struct {
|
type Client struct {
|
||||||
|
|||||||
@@ -3,10 +3,10 @@ package lsp
|
|||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
|
||||||
"github.com/kujtimiihoxha/opencode/internal/config"
|
"github.com/opencode-ai/opencode/internal/config"
|
||||||
"github.com/kujtimiihoxha/opencode/internal/logging"
|
"github.com/opencode-ai/opencode/internal/logging"
|
||||||
"github.com/kujtimiihoxha/opencode/internal/lsp/protocol"
|
"github.com/opencode-ai/opencode/internal/lsp/protocol"
|
||||||
"github.com/kujtimiihoxha/opencode/internal/lsp/util"
|
"github.com/opencode-ai/opencode/internal/lsp/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Requests
|
// Requests
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import (
|
|||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/kujtimiihoxha/opencode/internal/lsp/protocol"
|
"github.com/opencode-ai/opencode/internal/lsp/protocol"
|
||||||
)
|
)
|
||||||
|
|
||||||
func DetectLanguageID(uri string) protocol.LanguageKind {
|
func DetectLanguageID(uri string) protocol.LanguageKind {
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ package lsp
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
|
||||||
"github.com/kujtimiihoxha/opencode/internal/lsp/protocol"
|
"github.com/opencode-ai/opencode/internal/lsp/protocol"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Implementation sends a textDocument/implementation request to the LSP server.
|
// Implementation sends a textDocument/implementation request to the LSP server.
|
||||||
|
|||||||
@@ -8,8 +8,8 @@ import (
|
|||||||
"io"
|
"io"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/kujtimiihoxha/opencode/internal/config"
|
"github.com/opencode-ai/opencode/internal/config"
|
||||||
"github.com/kujtimiihoxha/opencode/internal/logging"
|
"github.com/opencode-ai/opencode/internal/logging"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Write writes an LSP message to the given writer
|
// Write writes an LSP message to the given writer
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import (
|
|||||||
"sort"
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/kujtimiihoxha/opencode/internal/lsp/protocol"
|
"github.com/opencode-ai/opencode/internal/lsp/protocol"
|
||||||
)
|
)
|
||||||
|
|
||||||
func applyTextEdits(uri protocol.DocumentUri, edits []protocol.TextEdit) error {
|
func applyTextEdits(uri protocol.DocumentUri, edits []protocol.TextEdit) error {
|
||||||
|
|||||||
@@ -11,10 +11,10 @@ import (
|
|||||||
|
|
||||||
"github.com/bmatcuk/doublestar/v4"
|
"github.com/bmatcuk/doublestar/v4"
|
||||||
"github.com/fsnotify/fsnotify"
|
"github.com/fsnotify/fsnotify"
|
||||||
"github.com/kujtimiihoxha/opencode/internal/config"
|
"github.com/opencode-ai/opencode/internal/config"
|
||||||
"github.com/kujtimiihoxha/opencode/internal/logging"
|
"github.com/opencode-ai/opencode/internal/logging"
|
||||||
"github.com/kujtimiihoxha/opencode/internal/lsp"
|
"github.com/opencode-ai/opencode/internal/lsp"
|
||||||
"github.com/kujtimiihoxha/opencode/internal/lsp/protocol"
|
"github.com/opencode-ai/opencode/internal/lsp/protocol"
|
||||||
)
|
)
|
||||||
|
|
||||||
// WorkspaceWatcher manages LSP file watching
|
// WorkspaceWatcher manages LSP file watching
|
||||||
@@ -96,19 +96,19 @@ func (w *WorkspaceWatcher) AddRegistrations(ctx context.Context, id string, watc
|
|||||||
// Determine server type for specialized handling
|
// Determine server type for specialized handling
|
||||||
serverName := getServerNameFromContext(ctx)
|
serverName := getServerNameFromContext(ctx)
|
||||||
logging.Debug("Server type detected", "serverName", serverName)
|
logging.Debug("Server type detected", "serverName", serverName)
|
||||||
|
|
||||||
// Check if this server has sent file watchers
|
// Check if this server has sent file watchers
|
||||||
hasFileWatchers := len(watchers) > 0
|
hasFileWatchers := len(watchers) > 0
|
||||||
|
|
||||||
// For servers that need file preloading, we'll use a smart approach
|
// For servers that need file preloading, we'll use a smart approach
|
||||||
if shouldPreloadFiles(serverName) || !hasFileWatchers {
|
if shouldPreloadFiles(serverName) || !hasFileWatchers {
|
||||||
go func() {
|
go func() {
|
||||||
startTime := time.Now()
|
startTime := time.Now()
|
||||||
filesOpened := 0
|
filesOpened := 0
|
||||||
|
|
||||||
// Determine max files to open based on server type
|
// Determine max files to open based on server type
|
||||||
maxFilesToOpen := 50 // Default conservative limit
|
maxFilesToOpen := 50 // Default conservative limit
|
||||||
|
|
||||||
switch serverName {
|
switch serverName {
|
||||||
case "typescript", "typescript-language-server", "tsserver", "vtsls":
|
case "typescript", "typescript-language-server", "tsserver", "vtsls":
|
||||||
// TypeScript servers benefit from seeing more files
|
// TypeScript servers benefit from seeing more files
|
||||||
@@ -117,17 +117,17 @@ func (w *WorkspaceWatcher) AddRegistrations(ctx context.Context, id string, watc
|
|||||||
// Java servers need to see many files for project model
|
// Java servers need to see many files for project model
|
||||||
maxFilesToOpen = 200
|
maxFilesToOpen = 200
|
||||||
}
|
}
|
||||||
|
|
||||||
// First, open high-priority files
|
// First, open high-priority files
|
||||||
highPriorityFilesOpened := w.openHighPriorityFiles(ctx, serverName)
|
highPriorityFilesOpened := w.openHighPriorityFiles(ctx, serverName)
|
||||||
filesOpened += highPriorityFilesOpened
|
filesOpened += highPriorityFilesOpened
|
||||||
|
|
||||||
if cnf.DebugLSP {
|
if cnf.DebugLSP {
|
||||||
logging.Debug("Opened high-priority files",
|
logging.Debug("Opened high-priority files",
|
||||||
"count", highPriorityFilesOpened,
|
"count", highPriorityFilesOpened,
|
||||||
"serverName", serverName)
|
"serverName", serverName)
|
||||||
}
|
}
|
||||||
|
|
||||||
// If we've already opened enough high-priority files, we might not need more
|
// If we've already opened enough high-priority files, we might not need more
|
||||||
if filesOpened >= maxFilesToOpen {
|
if filesOpened >= maxFilesToOpen {
|
||||||
if cnf.DebugLSP {
|
if cnf.DebugLSP {
|
||||||
@@ -137,9 +137,9 @@ func (w *WorkspaceWatcher) AddRegistrations(ctx context.Context, id string, watc
|
|||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// For the remaining slots, walk the directory and open matching files
|
// For the remaining slots, walk the directory and open matching files
|
||||||
|
|
||||||
err := filepath.WalkDir(w.workspacePath, func(path string, d os.DirEntry, err error) error {
|
err := filepath.WalkDir(w.workspacePath, func(path string, d os.DirEntry, err error) error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -199,10 +199,10 @@ func (w *WorkspaceWatcher) AddRegistrations(ctx context.Context, id string, watc
|
|||||||
func (w *WorkspaceWatcher) openHighPriorityFiles(ctx context.Context, serverName string) int {
|
func (w *WorkspaceWatcher) openHighPriorityFiles(ctx context.Context, serverName string) int {
|
||||||
cnf := config.Get()
|
cnf := config.Get()
|
||||||
filesOpened := 0
|
filesOpened := 0
|
||||||
|
|
||||||
// Define patterns for high-priority files based on server type
|
// Define patterns for high-priority files based on server type
|
||||||
var patterns []string
|
var patterns []string
|
||||||
|
|
||||||
switch serverName {
|
switch serverName {
|
||||||
case "typescript", "typescript-language-server", "tsserver", "vtsls":
|
case "typescript", "typescript-language-server", "tsserver", "vtsls":
|
||||||
patterns = []string{
|
patterns = []string{
|
||||||
@@ -256,7 +256,7 @@ func (w *WorkspaceWatcher) openHighPriorityFiles(ctx context.Context, serverName
|
|||||||
"**/.editorconfig",
|
"**/.editorconfig",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// For each pattern, find and open matching files
|
// For each pattern, find and open matching files
|
||||||
for _, pattern := range patterns {
|
for _, pattern := range patterns {
|
||||||
// Use doublestar.Glob to find files matching the pattern (supports ** patterns)
|
// Use doublestar.Glob to find files matching the pattern (supports ** patterns)
|
||||||
@@ -267,17 +267,17 @@ func (w *WorkspaceWatcher) openHighPriorityFiles(ctx context.Context, serverName
|
|||||||
}
|
}
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, match := range matches {
|
for _, match := range matches {
|
||||||
// Convert relative path to absolute
|
// Convert relative path to absolute
|
||||||
fullPath := filepath.Join(w.workspacePath, match)
|
fullPath := filepath.Join(w.workspacePath, match)
|
||||||
|
|
||||||
// Skip directories and excluded files
|
// Skip directories and excluded files
|
||||||
info, err := os.Stat(fullPath)
|
info, err := os.Stat(fullPath)
|
||||||
if err != nil || info.IsDir() || shouldExcludeFile(fullPath) {
|
if err != nil || info.IsDir() || shouldExcludeFile(fullPath) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
// Open the file
|
// Open the file
|
||||||
if err := w.client.OpenFile(ctx, fullPath); err != nil {
|
if err := w.client.OpenFile(ctx, fullPath); err != nil {
|
||||||
if cnf.DebugLSP {
|
if cnf.DebugLSP {
|
||||||
@@ -289,17 +289,17 @@ func (w *WorkspaceWatcher) openHighPriorityFiles(ctx context.Context, serverName
|
|||||||
logging.Debug("Opened high-priority file", "path", fullPath)
|
logging.Debug("Opened high-priority file", "path", fullPath)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add a small delay to prevent overwhelming the server
|
// Add a small delay to prevent overwhelming the server
|
||||||
time.Sleep(20 * time.Millisecond)
|
time.Sleep(20 * time.Millisecond)
|
||||||
|
|
||||||
// Limit the number of files opened per pattern
|
// Limit the number of files opened per pattern
|
||||||
if filesOpened >= 5 && (serverName != "java" && serverName != "jdtls") {
|
if filesOpened >= 5 && (serverName != "java" && serverName != "jdtls") {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return filesOpened
|
return filesOpened
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -310,16 +310,16 @@ func (w *WorkspaceWatcher) WatchWorkspace(ctx context.Context, workspacePath str
|
|||||||
|
|
||||||
// Store the watcher in the context for later use
|
// Store the watcher in the context for later use
|
||||||
ctx = context.WithValue(ctx, "workspaceWatcher", w)
|
ctx = context.WithValue(ctx, "workspaceWatcher", w)
|
||||||
|
|
||||||
// If the server name isn't already in the context, try to detect it
|
// If the server name isn't already in the context, try to detect it
|
||||||
if _, ok := ctx.Value("serverName").(string); !ok {
|
if _, ok := ctx.Value("serverName").(string); !ok {
|
||||||
serverName := getServerNameFromContext(ctx)
|
serverName := getServerNameFromContext(ctx)
|
||||||
ctx = context.WithValue(ctx, "serverName", serverName)
|
ctx = context.WithValue(ctx, "serverName", serverName)
|
||||||
}
|
}
|
||||||
|
|
||||||
serverName := getServerNameFromContext(ctx)
|
serverName := getServerNameFromContext(ctx)
|
||||||
logging.Debug("Starting workspace watcher", "workspacePath", workspacePath, "serverName", serverName)
|
logging.Debug("Starting workspace watcher", "workspacePath", workspacePath, "serverName", serverName)
|
||||||
|
|
||||||
// Register handler for file watcher registrations from the server
|
// Register handler for file watcher registrations from the server
|
||||||
lsp.RegisterFileWatchHandler(func(id string, watchers []protocol.FileSystemWatcher) {
|
lsp.RegisterFileWatchHandler(func(id string, watchers []protocol.FileSystemWatcher) {
|
||||||
w.AddRegistrations(ctx, id, watchers)
|
w.AddRegistrations(ctx, id, watchers)
|
||||||
@@ -414,7 +414,11 @@ func (w *WorkspaceWatcher) WatchWorkspace(ctx context.Context, workspacePath str
|
|||||||
case event.Op&fsnotify.Create != 0:
|
case event.Op&fsnotify.Create != 0:
|
||||||
// Already handled earlier in the event loop
|
// Already handled earlier in the event loop
|
||||||
// Just send the notification if needed
|
// Just send the notification if needed
|
||||||
info, _ := os.Stat(event.Name)
|
info, err := os.Stat(event.Name)
|
||||||
|
if err != nil {
|
||||||
|
logging.Error("Error getting file info", "path", event.Name, "error", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
if !info.IsDir() && watchKind&protocol.WatchCreate != 0 {
|
if !info.IsDir() && watchKind&protocol.WatchCreate != 0 {
|
||||||
w.debounceHandleFileEvent(ctx, uri, protocol.FileChangeType(protocol.Created))
|
w.debounceHandleFileEvent(ctx, uri, protocol.FileChangeType(protocol.Created))
|
||||||
}
|
}
|
||||||
@@ -682,7 +686,7 @@ func getServerNameFromContext(ctx context.Context) string {
|
|||||||
if serverName, ok := ctx.Value("serverName").(string); ok && serverName != "" {
|
if serverName, ok := ctx.Value("serverName").(string); ok && serverName != "" {
|
||||||
return strings.ToLower(serverName)
|
return strings.ToLower(serverName)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Otherwise, try to extract server name from the client command path
|
// Otherwise, try to extract server name from the client command path
|
||||||
if w, ok := ctx.Value("workspaceWatcher").(*WorkspaceWatcher); ok && w != nil && w.client != nil && w.client.Cmd != nil {
|
if w, ok := ctx.Value("workspaceWatcher").(*WorkspaceWatcher); ok && w != nil && w.client != nil && w.client.Cmd != nil {
|
||||||
path := strings.ToLower(w.client.Cmd.Path)
|
path := strings.ToLower(w.client.Cmd.Path)
|
||||||
@@ -865,7 +869,7 @@ func (w *WorkspaceWatcher) openMatchingFile(ctx context.Context, path string) {
|
|||||||
if watched, _ := w.isPathWatched(path); watched {
|
if watched, _ := w.isPathWatched(path); watched {
|
||||||
// Get server name for specialized handling
|
// Get server name for specialized handling
|
||||||
serverName := getServerNameFromContext(ctx)
|
serverName := getServerNameFromContext(ctx)
|
||||||
|
|
||||||
// Check if the file is a high-priority file that should be opened immediately
|
// Check if the file is a high-priority file that should be opened immediately
|
||||||
// This helps with project initialization for certain language servers
|
// This helps with project initialization for certain language servers
|
||||||
if isHighPriorityFile(path, serverName) {
|
if isHighPriorityFile(path, serverName) {
|
||||||
@@ -881,7 +885,7 @@ func (w *WorkspaceWatcher) openMatchingFile(ctx context.Context, path string) {
|
|||||||
// For non-high-priority files, we'll use different strategies based on server type
|
// For non-high-priority files, we'll use different strategies based on server type
|
||||||
if shouldPreloadFiles(serverName) {
|
if shouldPreloadFiles(serverName) {
|
||||||
// For servers that benefit from preloading, open files but with limits
|
// For servers that benefit from preloading, open files but with limits
|
||||||
|
|
||||||
// Check file size - for preloading we're more conservative
|
// Check file size - for preloading we're more conservative
|
||||||
if info.Size() > (1 * 1024 * 1024) { // 1MB limit for preloaded files
|
if info.Size() > (1 * 1024 * 1024) { // 1MB limit for preloaded files
|
||||||
if cnf.DebugLSP {
|
if cnf.DebugLSP {
|
||||||
@@ -889,13 +893,13 @@ func (w *WorkspaceWatcher) openMatchingFile(ctx context.Context, path string) {
|
|||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check file extension for common source files
|
// Check file extension for common source files
|
||||||
ext := strings.ToLower(filepath.Ext(path))
|
ext := strings.ToLower(filepath.Ext(path))
|
||||||
|
|
||||||
// Only preload source files for the specific language
|
// Only preload source files for the specific language
|
||||||
shouldOpen := false
|
shouldOpen := false
|
||||||
|
|
||||||
switch serverName {
|
switch serverName {
|
||||||
case "typescript", "typescript-language-server", "tsserver", "vtsls":
|
case "typescript", "typescript-language-server", "tsserver", "vtsls":
|
||||||
shouldOpen = ext == ".ts" || ext == ".js" || ext == ".tsx" || ext == ".jsx"
|
shouldOpen = ext == ".ts" || ext == ".js" || ext == ".tsx" || ext == ".jsx"
|
||||||
@@ -913,7 +917,7 @@ func (w *WorkspaceWatcher) openMatchingFile(ctx context.Context, path string) {
|
|||||||
// For unknown servers, be conservative
|
// For unknown servers, be conservative
|
||||||
shouldOpen = false
|
shouldOpen = false
|
||||||
}
|
}
|
||||||
|
|
||||||
if shouldOpen {
|
if shouldOpen {
|
||||||
// Don't need to check if it's already open - the client.OpenFile handles that
|
// Don't need to check if it's already open - the client.OpenFile handles that
|
||||||
if err := w.client.OpenFile(ctx, path); err != nil && cnf.DebugLSP {
|
if err := w.client.OpenFile(ctx, path); err != nil && cnf.DebugLSP {
|
||||||
@@ -943,13 +947,13 @@ func isHighPriorityFile(path string, serverName string) bool {
|
|||||||
fileName == "main.js"
|
fileName == "main.js"
|
||||||
case "gopls":
|
case "gopls":
|
||||||
// For Go, we want to open go.mod files immediately
|
// For Go, we want to open go.mod files immediately
|
||||||
return fileName == "go.mod" ||
|
return fileName == "go.mod" ||
|
||||||
fileName == "go.sum" ||
|
fileName == "go.sum" ||
|
||||||
// Also open main.go files
|
// Also open main.go files
|
||||||
fileName == "main.go"
|
fileName == "main.go"
|
||||||
case "rust-analyzer":
|
case "rust-analyzer":
|
||||||
// For Rust, we want to open Cargo.toml files immediately
|
// For Rust, we want to open Cargo.toml files immediately
|
||||||
return fileName == "Cargo.toml" ||
|
return fileName == "Cargo.toml" ||
|
||||||
fileName == "Cargo.lock" ||
|
fileName == "Cargo.lock" ||
|
||||||
// Also open lib.rs and main.rs
|
// Also open lib.rs and main.rs
|
||||||
fileName == "lib.rs" ||
|
fileName == "lib.rs" ||
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import (
|
|||||||
"slices"
|
"slices"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/kujtimiihoxha/opencode/internal/llm/models"
|
"github.com/opencode-ai/opencode/internal/llm/models"
|
||||||
)
|
)
|
||||||
|
|
||||||
type MessageRole string
|
type MessageRole string
|
||||||
|
|||||||
@@ -8,9 +8,9 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
"github.com/kujtimiihoxha/opencode/internal/db"
|
"github.com/opencode-ai/opencode/internal/db"
|
||||||
"github.com/kujtimiihoxha/opencode/internal/llm/models"
|
"github.com/opencode-ai/opencode/internal/llm/models"
|
||||||
"github.com/kujtimiihoxha/opencode/internal/pubsub"
|
"github.com/opencode-ai/opencode/internal/pubsub"
|
||||||
)
|
)
|
||||||
|
|
||||||
type CreateMessageParams struct {
|
type CreateMessageParams struct {
|
||||||
|
|||||||
@@ -8,8 +8,8 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
"github.com/kujtimiihoxha/opencode/internal/config"
|
"github.com/opencode-ai/opencode/internal/config"
|
||||||
"github.com/kujtimiihoxha/opencode/internal/pubsub"
|
"github.com/opencode-ai/opencode/internal/pubsub"
|
||||||
)
|
)
|
||||||
|
|
||||||
var ErrorPermissionDenied = errors.New("permission denied")
|
var ErrorPermissionDenied = errors.New("permission denied")
|
||||||
|
|||||||
@@ -5,8 +5,8 @@ import (
|
|||||||
"database/sql"
|
"database/sql"
|
||||||
|
|
||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
"github.com/kujtimiihoxha/opencode/internal/db"
|
"github.com/opencode-ai/opencode/internal/db"
|
||||||
"github.com/kujtimiihoxha/opencode/internal/pubsub"
|
"github.com/opencode-ai/opencode/internal/pubsub"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Session struct {
|
type Session struct {
|
||||||
|
|||||||
@@ -6,10 +6,10 @@ import (
|
|||||||
|
|
||||||
"github.com/charmbracelet/lipgloss"
|
"github.com/charmbracelet/lipgloss"
|
||||||
"github.com/charmbracelet/x/ansi"
|
"github.com/charmbracelet/x/ansi"
|
||||||
"github.com/kujtimiihoxha/opencode/internal/config"
|
"github.com/opencode-ai/opencode/internal/config"
|
||||||
"github.com/kujtimiihoxha/opencode/internal/session"
|
"github.com/opencode-ai/opencode/internal/session"
|
||||||
"github.com/kujtimiihoxha/opencode/internal/tui/styles"
|
"github.com/opencode-ai/opencode/internal/tui/styles"
|
||||||
"github.com/kujtimiihoxha/opencode/internal/version"
|
"github.com/opencode-ai/opencode/internal/version"
|
||||||
)
|
)
|
||||||
|
|
||||||
type SendMsg struct {
|
type SendMsg struct {
|
||||||
@@ -92,7 +92,7 @@ func logo(width int) string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func repo(width int) string {
|
func repo(width int) string {
|
||||||
repo := "https://github.com/kujtimiihoxha/opencode"
|
repo := "https://github.com/opencode-ai/opencode"
|
||||||
return styles.BaseStyle.
|
return styles.BaseStyle.
|
||||||
Foreground(styles.ForgroundDim).
|
Foreground(styles.ForgroundDim).
|
||||||
Width(width).
|
Width(width).
|
||||||
|
|||||||
@@ -8,11 +8,11 @@ import (
|
|||||||
"github.com/charmbracelet/bubbles/textarea"
|
"github.com/charmbracelet/bubbles/textarea"
|
||||||
tea "github.com/charmbracelet/bubbletea"
|
tea "github.com/charmbracelet/bubbletea"
|
||||||
"github.com/charmbracelet/lipgloss"
|
"github.com/charmbracelet/lipgloss"
|
||||||
"github.com/kujtimiihoxha/opencode/internal/app"
|
"github.com/opencode-ai/opencode/internal/app"
|
||||||
"github.com/kujtimiihoxha/opencode/internal/session"
|
"github.com/opencode-ai/opencode/internal/session"
|
||||||
"github.com/kujtimiihoxha/opencode/internal/tui/layout"
|
"github.com/opencode-ai/opencode/internal/tui/layout"
|
||||||
"github.com/kujtimiihoxha/opencode/internal/tui/styles"
|
"github.com/opencode-ai/opencode/internal/tui/styles"
|
||||||
"github.com/kujtimiihoxha/opencode/internal/tui/util"
|
"github.com/opencode-ai/opencode/internal/tui/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
type editorCmp struct {
|
type editorCmp struct {
|
||||||
@@ -21,12 +21,9 @@ type editorCmp struct {
|
|||||||
textarea textarea.Model
|
textarea textarea.Model
|
||||||
}
|
}
|
||||||
|
|
||||||
type FocusEditorMsg bool
|
type EditorKeyMaps struct {
|
||||||
|
|
||||||
type focusedEditorKeyMaps struct {
|
|
||||||
Send key.Binding
|
Send key.Binding
|
||||||
OpenEditor key.Binding
|
OpenEditor key.Binding
|
||||||
Blur key.Binding
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type bluredEditorKeyMaps struct {
|
type bluredEditorKeyMaps struct {
|
||||||
@@ -35,29 +32,10 @@ type bluredEditorKeyMaps struct {
|
|||||||
OpenEditor key.Binding
|
OpenEditor key.Binding
|
||||||
}
|
}
|
||||||
|
|
||||||
var focusedKeyMaps = focusedEditorKeyMaps{
|
var editorMaps = EditorKeyMaps{
|
||||||
Send: key.NewBinding(
|
Send: key.NewBinding(
|
||||||
key.WithKeys("ctrl+s"),
|
key.WithKeys("enter", "ctrl+s"),
|
||||||
key.WithHelp("ctrl+s", "send message"),
|
key.WithHelp("enter", "send message"),
|
||||||
),
|
|
||||||
Blur: key.NewBinding(
|
|
||||||
key.WithKeys("esc"),
|
|
||||||
key.WithHelp("esc", "focus messages"),
|
|
||||||
),
|
|
||||||
OpenEditor: key.NewBinding(
|
|
||||||
key.WithKeys("ctrl+e"),
|
|
||||||
key.WithHelp("ctrl+e", "open editor"),
|
|
||||||
),
|
|
||||||
}
|
|
||||||
|
|
||||||
var bluredKeyMaps = bluredEditorKeyMaps{
|
|
||||||
Send: key.NewBinding(
|
|
||||||
key.WithKeys("ctrl+s", "enter"),
|
|
||||||
key.WithHelp("ctrl+s/enter", "send message"),
|
|
||||||
),
|
|
||||||
Focus: key.NewBinding(
|
|
||||||
key.WithKeys("i"),
|
|
||||||
key.WithHelp("i", "focus editor"),
|
|
||||||
),
|
),
|
||||||
OpenEditor: key.NewBinding(
|
OpenEditor: key.NewBinding(
|
||||||
key.WithKeys("ctrl+e"),
|
key.WithKeys("ctrl+e"),
|
||||||
@@ -88,6 +66,9 @@ func openEditor() tea.Cmd {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return util.ReportError(err)
|
return util.ReportError(err)
|
||||||
}
|
}
|
||||||
|
if len(content) == 0 {
|
||||||
|
return util.ReportWarn("Message is empty")
|
||||||
|
}
|
||||||
os.Remove(tmpfile.Name())
|
os.Remove(tmpfile.Name())
|
||||||
return SendMsg{
|
return SendMsg{
|
||||||
Text: string(content),
|
Text: string(content),
|
||||||
@@ -106,7 +87,6 @@ func (m *editorCmp) send() tea.Cmd {
|
|||||||
|
|
||||||
value := m.textarea.Value()
|
value := m.textarea.Value()
|
||||||
m.textarea.Reset()
|
m.textarea.Reset()
|
||||||
m.textarea.Blur()
|
|
||||||
if value == "" {
|
if value == "" {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -125,32 +105,28 @@ func (m *editorCmp) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
|||||||
m.session = msg
|
m.session = msg
|
||||||
}
|
}
|
||||||
return m, nil
|
return m, nil
|
||||||
case FocusEditorMsg:
|
|
||||||
if msg {
|
|
||||||
m.textarea.Focus()
|
|
||||||
return m, tea.Batch(textarea.Blink, util.CmdHandler(EditorFocusMsg(true)))
|
|
||||||
}
|
|
||||||
case tea.KeyMsg:
|
case tea.KeyMsg:
|
||||||
if key.Matches(msg, focusedKeyMaps.OpenEditor) {
|
if key.Matches(msg, messageKeys.PageUp) || key.Matches(msg, messageKeys.PageDown) ||
|
||||||
|
key.Matches(msg, messageKeys.HalfPageUp) || key.Matches(msg, messageKeys.HalfPageDown) {
|
||||||
|
return m, nil
|
||||||
|
}
|
||||||
|
if key.Matches(msg, editorMaps.OpenEditor) {
|
||||||
if m.app.CoderAgent.IsSessionBusy(m.session.ID) {
|
if m.app.CoderAgent.IsSessionBusy(m.session.ID) {
|
||||||
return m, util.ReportWarn("Agent is working, please wait...")
|
return m, util.ReportWarn("Agent is working, please wait...")
|
||||||
}
|
}
|
||||||
return m, openEditor()
|
return m, openEditor()
|
||||||
}
|
}
|
||||||
// if the key does not match any binding, return
|
// Handle Enter key
|
||||||
if m.textarea.Focused() && key.Matches(msg, focusedKeyMaps.Send) {
|
if m.textarea.Focused() && key.Matches(msg, editorMaps.Send) {
|
||||||
return m, m.send()
|
value := m.textarea.Value()
|
||||||
}
|
if len(value) > 0 && value[len(value)-1] == '\\' {
|
||||||
if !m.textarea.Focused() && key.Matches(msg, bluredKeyMaps.Send) {
|
// If the last character is a backslash, remove it and add a newline
|
||||||
return m, m.send()
|
m.textarea.SetValue(value[:len(value)-1] + "\n")
|
||||||
}
|
return m, nil
|
||||||
if m.textarea.Focused() && key.Matches(msg, focusedKeyMaps.Blur) {
|
} else {
|
||||||
m.textarea.Blur()
|
// Otherwise, send the message
|
||||||
return m, util.CmdHandler(EditorFocusMsg(false))
|
return m, m.send()
|
||||||
}
|
}
|
||||||
if !m.textarea.Focused() && key.Matches(msg, bluredKeyMaps.Focus) {
|
|
||||||
m.textarea.Focus()
|
|
||||||
return m, tea.Batch(textarea.Blink, util.CmdHandler(EditorFocusMsg(true)))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
m.textarea, cmd = m.textarea.Update(msg)
|
m.textarea, cmd = m.textarea.Update(msg)
|
||||||
@@ -175,13 +151,7 @@ func (m *editorCmp) GetSize() (int, int) {
|
|||||||
|
|
||||||
func (m *editorCmp) BindingKeys() []key.Binding {
|
func (m *editorCmp) BindingKeys() []key.Binding {
|
||||||
bindings := []key.Binding{}
|
bindings := []key.Binding{}
|
||||||
if m.textarea.Focused() {
|
bindings = append(bindings, layout.KeyMapToSlice(editorMaps)...)
|
||||||
bindings = append(bindings, layout.KeyMapToSlice(focusedKeyMaps)...)
|
|
||||||
} else {
|
|
||||||
bindings = append(bindings, layout.KeyMapToSlice(bluredKeyMaps)...)
|
|
||||||
}
|
|
||||||
|
|
||||||
bindings = append(bindings, layout.KeyMapToSlice(m.textarea.KeyMap)...)
|
|
||||||
return bindings
|
return bindings
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -10,13 +10,12 @@ import (
|
|||||||
"github.com/charmbracelet/bubbles/viewport"
|
"github.com/charmbracelet/bubbles/viewport"
|
||||||
tea "github.com/charmbracelet/bubbletea"
|
tea "github.com/charmbracelet/bubbletea"
|
||||||
"github.com/charmbracelet/lipgloss"
|
"github.com/charmbracelet/lipgloss"
|
||||||
"github.com/kujtimiihoxha/opencode/internal/app"
|
"github.com/opencode-ai/opencode/internal/app"
|
||||||
"github.com/kujtimiihoxha/opencode/internal/message"
|
"github.com/opencode-ai/opencode/internal/message"
|
||||||
"github.com/kujtimiihoxha/opencode/internal/pubsub"
|
"github.com/opencode-ai/opencode/internal/pubsub"
|
||||||
"github.com/kujtimiihoxha/opencode/internal/session"
|
"github.com/opencode-ai/opencode/internal/session"
|
||||||
"github.com/kujtimiihoxha/opencode/internal/tui/layout"
|
"github.com/opencode-ai/opencode/internal/tui/styles"
|
||||||
"github.com/kujtimiihoxha/opencode/internal/tui/styles"
|
"github.com/opencode-ai/opencode/internal/tui/util"
|
||||||
"github.com/kujtimiihoxha/opencode/internal/tui/util"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type cacheItem struct {
|
type cacheItem struct {
|
||||||
@@ -26,7 +25,6 @@ type cacheItem struct {
|
|||||||
type messagesCmp struct {
|
type messagesCmp struct {
|
||||||
app *app.App
|
app *app.App
|
||||||
width, height int
|
width, height int
|
||||||
writingMode bool
|
|
||||||
viewport viewport.Model
|
viewport viewport.Model
|
||||||
session session.Session
|
session session.Session
|
||||||
messages []message.Message
|
messages []message.Message
|
||||||
@@ -38,6 +36,32 @@ type messagesCmp struct {
|
|||||||
}
|
}
|
||||||
type renderFinishedMsg struct{}
|
type renderFinishedMsg struct{}
|
||||||
|
|
||||||
|
type MessageKeys struct {
|
||||||
|
PageDown key.Binding
|
||||||
|
PageUp key.Binding
|
||||||
|
HalfPageUp key.Binding
|
||||||
|
HalfPageDown key.Binding
|
||||||
|
}
|
||||||
|
|
||||||
|
var messageKeys = MessageKeys{
|
||||||
|
PageDown: key.NewBinding(
|
||||||
|
key.WithKeys("pgdown"),
|
||||||
|
key.WithHelp("f/pgdn", "page down"),
|
||||||
|
),
|
||||||
|
PageUp: key.NewBinding(
|
||||||
|
key.WithKeys("pgup"),
|
||||||
|
key.WithHelp("b/pgup", "page up"),
|
||||||
|
),
|
||||||
|
HalfPageUp: key.NewBinding(
|
||||||
|
key.WithKeys("ctrl+u"),
|
||||||
|
key.WithHelp("ctrl+u", "½ page up"),
|
||||||
|
),
|
||||||
|
HalfPageDown: key.NewBinding(
|
||||||
|
key.WithKeys("ctrl+d", "ctrl+d"),
|
||||||
|
key.WithHelp("ctrl+d", "½ page down"),
|
||||||
|
),
|
||||||
|
}
|
||||||
|
|
||||||
func (m *messagesCmp) Init() tea.Cmd {
|
func (m *messagesCmp) Init() tea.Cmd {
|
||||||
return tea.Batch(m.viewport.Init(), m.spinner.Tick)
|
return tea.Batch(m.viewport.Init(), m.spinner.Tick)
|
||||||
}
|
}
|
||||||
@@ -45,8 +69,7 @@ func (m *messagesCmp) Init() tea.Cmd {
|
|||||||
func (m *messagesCmp) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
func (m *messagesCmp) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||||
var cmds []tea.Cmd
|
var cmds []tea.Cmd
|
||||||
switch msg := msg.(type) {
|
switch msg := msg.(type) {
|
||||||
case EditorFocusMsg:
|
|
||||||
m.writingMode = bool(msg)
|
|
||||||
case SessionSelectedMsg:
|
case SessionSelectedMsg:
|
||||||
if msg.ID != m.session.ID {
|
if msg.ID != m.session.ID {
|
||||||
cmd := m.SetSession(msg)
|
cmd := m.SetSession(msg)
|
||||||
@@ -60,13 +83,17 @@ func (m *messagesCmp) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
|||||||
m.rendering = false
|
m.rendering = false
|
||||||
return m, nil
|
return m, nil
|
||||||
|
|
||||||
|
case tea.KeyMsg:
|
||||||
|
if key.Matches(msg, messageKeys.PageUp) || key.Matches(msg, messageKeys.PageDown) ||
|
||||||
|
key.Matches(msg, messageKeys.HalfPageUp) || key.Matches(msg, messageKeys.HalfPageDown) {
|
||||||
|
u, cmd := m.viewport.Update(msg)
|
||||||
|
m.viewport = u
|
||||||
|
cmds = append(cmds, cmd)
|
||||||
|
}
|
||||||
|
|
||||||
case renderFinishedMsg:
|
case renderFinishedMsg:
|
||||||
m.rendering = false
|
m.rendering = false
|
||||||
m.viewport.GotoBottom()
|
m.viewport.GotoBottom()
|
||||||
case tea.KeyMsg:
|
|
||||||
if m.writingMode {
|
|
||||||
return m, nil
|
|
||||||
}
|
|
||||||
case pubsub.Event[message.Message]:
|
case pubsub.Event[message.Message]:
|
||||||
needsRerender := false
|
needsRerender := false
|
||||||
if msg.Type == pubsub.CreatedEvent {
|
if msg.Type == pubsub.CreatedEvent {
|
||||||
@@ -122,10 +149,6 @@ func (m *messagesCmp) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
u, cmd := m.viewport.Update(msg)
|
|
||||||
m.viewport = u
|
|
||||||
cmds = append(cmds, cmd)
|
|
||||||
|
|
||||||
spinner, cmd := m.spinner.Update(msg)
|
spinner, cmd := m.spinner.Update(msg)
|
||||||
m.spinner = spinner
|
m.spinner = spinner
|
||||||
cmds = append(cmds, cmd)
|
cmds = append(cmds, cmd)
|
||||||
@@ -326,22 +349,24 @@ func (m *messagesCmp) working() string {
|
|||||||
func (m *messagesCmp) help() string {
|
func (m *messagesCmp) help() string {
|
||||||
text := ""
|
text := ""
|
||||||
|
|
||||||
if m.writingMode {
|
if m.app.CoderAgent.IsBusy() {
|
||||||
text += lipgloss.JoinHorizontal(
|
text += lipgloss.JoinHorizontal(
|
||||||
lipgloss.Left,
|
lipgloss.Left,
|
||||||
styles.BaseStyle.Foreground(styles.ForgroundDim).Bold(true).Render("press "),
|
styles.BaseStyle.Foreground(styles.ForgroundDim).Bold(true).Render("press "),
|
||||||
styles.BaseStyle.Foreground(styles.Forground).Bold(true).Render("esc"),
|
styles.BaseStyle.Foreground(styles.Forground).Bold(true).Render("esc"),
|
||||||
styles.BaseStyle.Foreground(styles.ForgroundDim).Bold(true).Render(" to exit writing mode"),
|
styles.BaseStyle.Foreground(styles.ForgroundDim).Bold(true).Render(" to exit cancel"),
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
text += lipgloss.JoinHorizontal(
|
text += lipgloss.JoinHorizontal(
|
||||||
lipgloss.Left,
|
lipgloss.Left,
|
||||||
styles.BaseStyle.Foreground(styles.ForgroundDim).Bold(true).Render("press "),
|
styles.BaseStyle.Foreground(styles.ForgroundDim).Bold(true).Render("press "),
|
||||||
styles.BaseStyle.Foreground(styles.Forground).Bold(true).Render("i"),
|
styles.BaseStyle.Foreground(styles.Forground).Bold(true).Render("enter"),
|
||||||
styles.BaseStyle.Foreground(styles.ForgroundDim).Bold(true).Render(" to start writing"),
|
styles.BaseStyle.Foreground(styles.ForgroundDim).Bold(true).Render(" to send the message,"),
|
||||||
|
styles.BaseStyle.Foreground(styles.ForgroundDim).Bold(true).Render(" write"),
|
||||||
|
styles.BaseStyle.Foreground(styles.Forground).Bold(true).Render(" \\"),
|
||||||
|
styles.BaseStyle.Foreground(styles.ForgroundDim).Bold(true).Render(" and enter to add a new line"),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
return styles.BaseStyle.
|
return styles.BaseStyle.
|
||||||
Width(m.width).
|
Width(m.width).
|
||||||
Render(text)
|
Render(text)
|
||||||
@@ -398,18 +423,26 @@ func (m *messagesCmp) SetSession(session session.Session) tea.Cmd {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (m *messagesCmp) BindingKeys() []key.Binding {
|
func (m *messagesCmp) BindingKeys() []key.Binding {
|
||||||
bindings := layout.KeyMapToSlice(m.viewport.KeyMap)
|
return []key.Binding{
|
||||||
return bindings
|
m.viewport.KeyMap.PageDown,
|
||||||
|
m.viewport.KeyMap.PageUp,
|
||||||
|
m.viewport.KeyMap.HalfPageUp,
|
||||||
|
m.viewport.KeyMap.HalfPageDown,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewMessagesCmp(app *app.App) tea.Model {
|
func NewMessagesCmp(app *app.App) tea.Model {
|
||||||
s := spinner.New()
|
s := spinner.New()
|
||||||
s.Spinner = spinner.Pulse
|
s.Spinner = spinner.Pulse
|
||||||
|
vp := viewport.New(0, 0)
|
||||||
|
vp.KeyMap.PageUp = messageKeys.PageUp
|
||||||
|
vp.KeyMap.PageDown = messageKeys.PageDown
|
||||||
|
vp.KeyMap.HalfPageUp = messageKeys.HalfPageUp
|
||||||
|
vp.KeyMap.HalfPageDown = messageKeys.HalfPageDown
|
||||||
return &messagesCmp{
|
return &messagesCmp{
|
||||||
app: app,
|
app: app,
|
||||||
writingMode: true,
|
|
||||||
cachedContent: make(map[string]cacheItem),
|
cachedContent: make(map[string]cacheItem),
|
||||||
viewport: viewport.New(0, 0),
|
viewport: vp,
|
||||||
spinner: s,
|
spinner: s,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,13 +12,13 @@ import (
|
|||||||
"github.com/charmbracelet/glamour"
|
"github.com/charmbracelet/glamour"
|
||||||
"github.com/charmbracelet/lipgloss"
|
"github.com/charmbracelet/lipgloss"
|
||||||
"github.com/charmbracelet/x/ansi"
|
"github.com/charmbracelet/x/ansi"
|
||||||
"github.com/kujtimiihoxha/opencode/internal/config"
|
"github.com/opencode-ai/opencode/internal/config"
|
||||||
"github.com/kujtimiihoxha/opencode/internal/diff"
|
"github.com/opencode-ai/opencode/internal/diff"
|
||||||
"github.com/kujtimiihoxha/opencode/internal/llm/agent"
|
"github.com/opencode-ai/opencode/internal/llm/agent"
|
||||||
"github.com/kujtimiihoxha/opencode/internal/llm/models"
|
"github.com/opencode-ai/opencode/internal/llm/models"
|
||||||
"github.com/kujtimiihoxha/opencode/internal/llm/tools"
|
"github.com/opencode-ai/opencode/internal/llm/tools"
|
||||||
"github.com/kujtimiihoxha/opencode/internal/message"
|
"github.com/opencode-ai/opencode/internal/message"
|
||||||
"github.com/kujtimiihoxha/opencode/internal/tui/styles"
|
"github.com/opencode-ai/opencode/internal/tui/styles"
|
||||||
)
|
)
|
||||||
|
|
||||||
type uiMessageType int
|
type uiMessageType int
|
||||||
@@ -28,7 +28,7 @@ const (
|
|||||||
assistantMessageType
|
assistantMessageType
|
||||||
toolMessageType
|
toolMessageType
|
||||||
|
|
||||||
maxResultHeight = 15
|
maxResultHeight = 10
|
||||||
)
|
)
|
||||||
|
|
||||||
var diffStyle = diff.NewStyleConfig(diff.WithShowHeader(false), diff.WithShowHunkHeader(false))
|
var diffStyle = diff.NewStyleConfig(diff.WithShowHeader(false), diff.WithShowHunkHeader(false))
|
||||||
@@ -148,7 +148,7 @@ func renderAssistantMessage(
|
|||||||
content = "*Finished without output*"
|
content = "*Finished without output*"
|
||||||
}
|
}
|
||||||
|
|
||||||
content = renderMessage(content, false, msg.ID == focusedUIMessageId, width, info...)
|
content = renderMessage(content, false, true, width, info...)
|
||||||
messages = append(messages, uiMessage{
|
messages = append(messages, uiMessage{
|
||||||
ID: msg.ID,
|
ID: msg.ID,
|
||||||
messageType: assistantMessageType,
|
messageType: assistantMessageType,
|
||||||
|
|||||||
@@ -8,12 +8,12 @@ import (
|
|||||||
|
|
||||||
tea "github.com/charmbracelet/bubbletea"
|
tea "github.com/charmbracelet/bubbletea"
|
||||||
"github.com/charmbracelet/lipgloss"
|
"github.com/charmbracelet/lipgloss"
|
||||||
"github.com/kujtimiihoxha/opencode/internal/config"
|
"github.com/opencode-ai/opencode/internal/config"
|
||||||
"github.com/kujtimiihoxha/opencode/internal/diff"
|
"github.com/opencode-ai/opencode/internal/diff"
|
||||||
"github.com/kujtimiihoxha/opencode/internal/history"
|
"github.com/opencode-ai/opencode/internal/history"
|
||||||
"github.com/kujtimiihoxha/opencode/internal/pubsub"
|
"github.com/opencode-ai/opencode/internal/pubsub"
|
||||||
"github.com/kujtimiihoxha/opencode/internal/session"
|
"github.com/opencode-ai/opencode/internal/session"
|
||||||
"github.com/kujtimiihoxha/opencode/internal/tui/styles"
|
"github.com/opencode-ai/opencode/internal/tui/styles"
|
||||||
)
|
)
|
||||||
|
|
||||||
type sidebarCmp struct {
|
type sidebarCmp struct {
|
||||||
@@ -116,13 +116,18 @@ func (m *sidebarCmp) sessionSection() string {
|
|||||||
func (m *sidebarCmp) modifiedFile(filePath string, additions, removals int) string {
|
func (m *sidebarCmp) modifiedFile(filePath string, additions, removals int) string {
|
||||||
stats := ""
|
stats := ""
|
||||||
if additions > 0 && removals > 0 {
|
if additions > 0 && removals > 0 {
|
||||||
stats = styles.BaseStyle.Foreground(styles.ForgroundDim).Render(fmt.Sprintf(" %d additions and %d removals", additions, removals))
|
additions := styles.BaseStyle.Foreground(styles.Green).PaddingLeft(1).Render(fmt.Sprintf("+%d", additions))
|
||||||
|
removals := styles.BaseStyle.Foreground(styles.Red).PaddingLeft(1).Render(fmt.Sprintf("-%d", removals))
|
||||||
|
content := lipgloss.JoinHorizontal(lipgloss.Left, additions, removals)
|
||||||
|
stats = styles.BaseStyle.Width(lipgloss.Width(content)).Render(content)
|
||||||
} else if additions > 0 {
|
} else if additions > 0 {
|
||||||
stats = styles.BaseStyle.Foreground(styles.ForgroundDim).Render(fmt.Sprintf(" %d additions", additions))
|
additions := fmt.Sprintf(" %s", styles.BaseStyle.PaddingLeft(1).Foreground(styles.Green).Render(fmt.Sprintf("+%d", additions)))
|
||||||
|
stats = styles.BaseStyle.Width(lipgloss.Width(additions)).Render(additions)
|
||||||
} else if removals > 0 {
|
} else if removals > 0 {
|
||||||
stats = styles.BaseStyle.Foreground(styles.ForgroundDim).Render(fmt.Sprintf(" %d removals", removals))
|
removals := fmt.Sprintf(" %s", styles.BaseStyle.PaddingLeft(1).Foreground(styles.Red).Render(fmt.Sprintf("-%d", removals)))
|
||||||
|
stats = styles.BaseStyle.Width(lipgloss.Width(removals)).Render(removals)
|
||||||
}
|
}
|
||||||
filePathStr := styles.BaseStyle.Foreground(styles.Forground).Render(filePath)
|
filePathStr := styles.BaseStyle.Render(filePath)
|
||||||
|
|
||||||
return styles.BaseStyle.
|
return styles.BaseStyle.
|
||||||
Width(m.width).
|
Width(m.width).
|
||||||
|
|||||||
@@ -7,15 +7,15 @@ import (
|
|||||||
|
|
||||||
tea "github.com/charmbracelet/bubbletea"
|
tea "github.com/charmbracelet/bubbletea"
|
||||||
"github.com/charmbracelet/lipgloss"
|
"github.com/charmbracelet/lipgloss"
|
||||||
"github.com/kujtimiihoxha/opencode/internal/config"
|
"github.com/opencode-ai/opencode/internal/config"
|
||||||
"github.com/kujtimiihoxha/opencode/internal/llm/models"
|
"github.com/opencode-ai/opencode/internal/llm/models"
|
||||||
"github.com/kujtimiihoxha/opencode/internal/lsp"
|
"github.com/opencode-ai/opencode/internal/lsp"
|
||||||
"github.com/kujtimiihoxha/opencode/internal/lsp/protocol"
|
"github.com/opencode-ai/opencode/internal/lsp/protocol"
|
||||||
"github.com/kujtimiihoxha/opencode/internal/pubsub"
|
"github.com/opencode-ai/opencode/internal/pubsub"
|
||||||
"github.com/kujtimiihoxha/opencode/internal/session"
|
"github.com/opencode-ai/opencode/internal/session"
|
||||||
"github.com/kujtimiihoxha/opencode/internal/tui/components/chat"
|
"github.com/opencode-ai/opencode/internal/tui/components/chat"
|
||||||
"github.com/kujtimiihoxha/opencode/internal/tui/styles"
|
"github.com/opencode-ai/opencode/internal/tui/styles"
|
||||||
"github.com/kujtimiihoxha/opencode/internal/tui/util"
|
"github.com/opencode-ai/opencode/internal/tui/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
type StatusCmp interface {
|
type StatusCmp interface {
|
||||||
|
|||||||
@@ -4,9 +4,9 @@ import (
|
|||||||
"github.com/charmbracelet/bubbles/key"
|
"github.com/charmbracelet/bubbles/key"
|
||||||
tea "github.com/charmbracelet/bubbletea"
|
tea "github.com/charmbracelet/bubbletea"
|
||||||
"github.com/charmbracelet/lipgloss"
|
"github.com/charmbracelet/lipgloss"
|
||||||
"github.com/kujtimiihoxha/opencode/internal/tui/layout"
|
"github.com/opencode-ai/opencode/internal/tui/layout"
|
||||||
"github.com/kujtimiihoxha/opencode/internal/tui/styles"
|
"github.com/opencode-ai/opencode/internal/tui/styles"
|
||||||
"github.com/kujtimiihoxha/opencode/internal/tui/util"
|
"github.com/opencode-ai/opencode/internal/tui/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Command represents a command that can be executed
|
// Command represents a command that can be executed
|
||||||
@@ -190,7 +190,6 @@ func (c *commandDialogCmp) View() string {
|
|||||||
styles.BaseStyle.Width(maxWidth).Render(""),
|
styles.BaseStyle.Width(maxWidth).Render(""),
|
||||||
styles.BaseStyle.Width(maxWidth).Render(lipgloss.JoinVertical(lipgloss.Left, commandItems...)),
|
styles.BaseStyle.Width(maxWidth).Render(lipgloss.JoinVertical(lipgloss.Left, commandItems...)),
|
||||||
styles.BaseStyle.Width(maxWidth).Render(""),
|
styles.BaseStyle.Width(maxWidth).Render(""),
|
||||||
styles.BaseStyle.Width(maxWidth).Padding(0, 1).Foreground(styles.ForgroundDim).Render("↑/k: up ↓/j: down enter: select esc: cancel"),
|
|
||||||
)
|
)
|
||||||
|
|
||||||
return styles.BaseStyle.Padding(1, 2).
|
return styles.BaseStyle.Padding(1, 2).
|
||||||
@@ -244,4 +243,3 @@ func NewCommandDialogCmp() CommandDialog {
|
|||||||
selectedCommandID: "",
|
selectedCommandID: "",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import (
|
|||||||
"github.com/charmbracelet/bubbles/key"
|
"github.com/charmbracelet/bubbles/key"
|
||||||
tea "github.com/charmbracelet/bubbletea"
|
tea "github.com/charmbracelet/bubbletea"
|
||||||
"github.com/charmbracelet/lipgloss"
|
"github.com/charmbracelet/lipgloss"
|
||||||
"github.com/kujtimiihoxha/opencode/internal/tui/styles"
|
"github.com/opencode-ai/opencode/internal/tui/styles"
|
||||||
)
|
)
|
||||||
|
|
||||||
type helpCmp struct {
|
type helpCmp struct {
|
||||||
@@ -62,7 +62,7 @@ func (h *helpCmp) render() string {
|
|||||||
var (
|
var (
|
||||||
pairs []string
|
pairs []string
|
||||||
width int
|
width int
|
||||||
rows = 14 - 2
|
rows = 10 - 2
|
||||||
)
|
)
|
||||||
for i := 0; i < len(bindings); i += rows {
|
for i := 0; i < len(bindings); i += rows {
|
||||||
var (
|
var (
|
||||||
|
|||||||
@@ -5,8 +5,8 @@ import (
|
|||||||
tea "github.com/charmbracelet/bubbletea"
|
tea "github.com/charmbracelet/bubbletea"
|
||||||
"github.com/charmbracelet/lipgloss"
|
"github.com/charmbracelet/lipgloss"
|
||||||
|
|
||||||
"github.com/kujtimiihoxha/opencode/internal/tui/styles"
|
"github.com/opencode-ai/opencode/internal/tui/styles"
|
||||||
"github.com/kujtimiihoxha/opencode/internal/tui/util"
|
"github.com/opencode-ai/opencode/internal/tui/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
// InitDialogCmp is a component that asks the user if they want to initialize the project.
|
// InitDialogCmp is a component that asks the user if they want to initialize the project.
|
||||||
@@ -46,8 +46,8 @@ func (k initDialogKeyMap) ShortHelp() []key.Binding {
|
|||||||
key.WithHelp("enter", "confirm"),
|
key.WithHelp("enter", "confirm"),
|
||||||
),
|
),
|
||||||
key.NewBinding(
|
key.NewBinding(
|
||||||
key.WithKeys("esc"),
|
key.WithKeys("esc", "q"),
|
||||||
key.WithHelp("esc", "cancel"),
|
key.WithHelp("esc/q", "cancel"),
|
||||||
),
|
),
|
||||||
key.NewBinding(
|
key.NewBinding(
|
||||||
key.WithKeys("y", "n"),
|
key.WithKeys("y", "n"),
|
||||||
@@ -114,6 +114,7 @@ func (m InitDialogCmp) View() string {
|
|||||||
Padding(1, 1).
|
Padding(1, 1).
|
||||||
Render("Would you like to initialize this project?")
|
Render("Would you like to initialize this project?")
|
||||||
|
|
||||||
|
maxWidth = min(maxWidth, m.width-10)
|
||||||
yesStyle := styles.BaseStyle
|
yesStyle := styles.BaseStyle
|
||||||
noStyle := styles.BaseStyle
|
noStyle := styles.BaseStyle
|
||||||
|
|
||||||
@@ -144,12 +145,6 @@ func (m InitDialogCmp) View() string {
|
|||||||
Padding(1, 0).
|
Padding(1, 0).
|
||||||
Render(buttons)
|
Render(buttons)
|
||||||
|
|
||||||
help := styles.BaseStyle.
|
|
||||||
Width(maxWidth).
|
|
||||||
Padding(0, 1).
|
|
||||||
Foreground(styles.ForgroundDim).
|
|
||||||
Render("tab/←/→: toggle y/n: yes/no enter: confirm esc: cancel")
|
|
||||||
|
|
||||||
content := lipgloss.JoinVertical(
|
content := lipgloss.JoinVertical(
|
||||||
lipgloss.Left,
|
lipgloss.Left,
|
||||||
title,
|
title,
|
||||||
@@ -158,7 +153,6 @@ func (m InitDialogCmp) View() string {
|
|||||||
question,
|
question,
|
||||||
buttons,
|
buttons,
|
||||||
styles.BaseStyle.Width(maxWidth).Render(""),
|
styles.BaseStyle.Width(maxWidth).Render(""),
|
||||||
help,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
return styles.BaseStyle.Padding(1, 2).
|
return styles.BaseStyle.Padding(1, 2).
|
||||||
|
|||||||
@@ -9,12 +9,12 @@ import (
|
|||||||
tea "github.com/charmbracelet/bubbletea"
|
tea "github.com/charmbracelet/bubbletea"
|
||||||
"github.com/charmbracelet/glamour"
|
"github.com/charmbracelet/glamour"
|
||||||
"github.com/charmbracelet/lipgloss"
|
"github.com/charmbracelet/lipgloss"
|
||||||
"github.com/kujtimiihoxha/opencode/internal/diff"
|
"github.com/opencode-ai/opencode/internal/diff"
|
||||||
"github.com/kujtimiihoxha/opencode/internal/llm/tools"
|
"github.com/opencode-ai/opencode/internal/llm/tools"
|
||||||
"github.com/kujtimiihoxha/opencode/internal/permission"
|
"github.com/opencode-ai/opencode/internal/permission"
|
||||||
"github.com/kujtimiihoxha/opencode/internal/tui/layout"
|
"github.com/opencode-ai/opencode/internal/tui/layout"
|
||||||
"github.com/kujtimiihoxha/opencode/internal/tui/styles"
|
"github.com/opencode-ai/opencode/internal/tui/styles"
|
||||||
"github.com/kujtimiihoxha/opencode/internal/tui/util"
|
"github.com/opencode-ai/opencode/internal/tui/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
type PermissionAction string
|
type PermissionAction string
|
||||||
@@ -67,8 +67,8 @@ var permissionsKeys = permissionsMapping{
|
|||||||
key.WithHelp("a", "allow"),
|
key.WithHelp("a", "allow"),
|
||||||
),
|
),
|
||||||
AllowSession: key.NewBinding(
|
AllowSession: key.NewBinding(
|
||||||
key.WithKeys("A"),
|
key.WithKeys("s"),
|
||||||
key.WithHelp("A", "allow for session"),
|
key.WithHelp("s", "allow for session"),
|
||||||
),
|
),
|
||||||
Deny: key.NewBinding(
|
Deny: key.NewBinding(
|
||||||
key.WithKeys("d"),
|
key.WithKeys("d"),
|
||||||
@@ -171,7 +171,7 @@ func (p *permissionDialogCmp) renderButtons() string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
allowButton := allowStyle.Padding(0, 1).Render("Allow (a)")
|
allowButton := allowStyle.Padding(0, 1).Render("Allow (a)")
|
||||||
allowSessionButton := allowSessionStyle.Padding(0, 1).Render("Allow for session (A)")
|
allowSessionButton := allowSessionStyle.Padding(0, 1).Render("Allow for session (s)")
|
||||||
denyButton := denyStyle.Padding(0, 1).Render("Deny (d)")
|
denyButton := denyStyle.Padding(0, 1).Render("Deny (d)")
|
||||||
|
|
||||||
content := lipgloss.JoinHorizontal(
|
content := lipgloss.JoinHorizontal(
|
||||||
@@ -375,9 +375,6 @@ func (p *permissionDialogCmp) render() string {
|
|||||||
contentFinal = p.renderDefaultContent()
|
contentFinal = p.renderDefaultContent()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add help text
|
|
||||||
helpText := styles.BaseStyle.Width(p.width - 4).Padding(0, 1).Foreground(styles.ForgroundDim).Render("←/→/tab: switch options a: allow A: allow for session d: deny enter/space: confirm")
|
|
||||||
|
|
||||||
content := lipgloss.JoinVertical(
|
content := lipgloss.JoinVertical(
|
||||||
lipgloss.Top,
|
lipgloss.Top,
|
||||||
title,
|
title,
|
||||||
@@ -385,8 +382,7 @@ func (p *permissionDialogCmp) render() string {
|
|||||||
headerContent,
|
headerContent,
|
||||||
contentFinal,
|
contentFinal,
|
||||||
buttons,
|
buttons,
|
||||||
styles.BaseStyle.Render(strings.Repeat(" ", p.width - 4)),
|
styles.BaseStyle.Render(strings.Repeat(" ", p.width-4)),
|
||||||
helpText,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
return styles.BaseStyle.
|
return styles.BaseStyle.
|
||||||
|
|||||||
@@ -6,9 +6,9 @@ import (
|
|||||||
"github.com/charmbracelet/bubbles/key"
|
"github.com/charmbracelet/bubbles/key"
|
||||||
tea "github.com/charmbracelet/bubbletea"
|
tea "github.com/charmbracelet/bubbletea"
|
||||||
"github.com/charmbracelet/lipgloss"
|
"github.com/charmbracelet/lipgloss"
|
||||||
"github.com/kujtimiihoxha/opencode/internal/tui/layout"
|
"github.com/opencode-ai/opencode/internal/tui/layout"
|
||||||
"github.com/kujtimiihoxha/opencode/internal/tui/styles"
|
"github.com/opencode-ai/opencode/internal/tui/styles"
|
||||||
"github.com/kujtimiihoxha/opencode/internal/tui/util"
|
"github.com/opencode-ai/opencode/internal/tui/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
const question = "Are you sure you want to quit?"
|
const question = "Are you sure you want to quit?"
|
||||||
|
|||||||
@@ -4,10 +4,10 @@ import (
|
|||||||
"github.com/charmbracelet/bubbles/key"
|
"github.com/charmbracelet/bubbles/key"
|
||||||
tea "github.com/charmbracelet/bubbletea"
|
tea "github.com/charmbracelet/bubbletea"
|
||||||
"github.com/charmbracelet/lipgloss"
|
"github.com/charmbracelet/lipgloss"
|
||||||
"github.com/kujtimiihoxha/opencode/internal/session"
|
"github.com/opencode-ai/opencode/internal/session"
|
||||||
"github.com/kujtimiihoxha/opencode/internal/tui/layout"
|
"github.com/opencode-ai/opencode/internal/tui/layout"
|
||||||
"github.com/kujtimiihoxha/opencode/internal/tui/styles"
|
"github.com/opencode-ai/opencode/internal/tui/styles"
|
||||||
"github.com/kujtimiihoxha/opencode/internal/tui/util"
|
"github.com/opencode-ai/opencode/internal/tui/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
// SessionSelectedMsg is sent when a session is selected
|
// SessionSelectedMsg is sent when a session is selected
|
||||||
@@ -122,6 +122,8 @@ func (s *sessionDialogCmp) View() string {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
maxWidth = max(30, min(maxWidth, s.width-15)) // Limit width to avoid overflow
|
||||||
|
|
||||||
// Limit height to avoid taking up too much screen space
|
// Limit height to avoid taking up too much screen space
|
||||||
maxVisibleSessions := min(10, len(s.sessions))
|
maxVisibleSessions := min(10, len(s.sessions))
|
||||||
|
|
||||||
@@ -169,7 +171,6 @@ func (s *sessionDialogCmp) View() string {
|
|||||||
styles.BaseStyle.Width(maxWidth).Render(""),
|
styles.BaseStyle.Width(maxWidth).Render(""),
|
||||||
styles.BaseStyle.Width(maxWidth).Render(lipgloss.JoinVertical(lipgloss.Left, sessionItems...)),
|
styles.BaseStyle.Width(maxWidth).Render(lipgloss.JoinVertical(lipgloss.Left, sessionItems...)),
|
||||||
styles.BaseStyle.Width(maxWidth).Render(""),
|
styles.BaseStyle.Width(maxWidth).Render(""),
|
||||||
styles.BaseStyle.Width(maxWidth).Padding(0, 1).Foreground(styles.ForgroundDim).Render("↑/k: up ↓/j: down enter: select esc: cancel"),
|
|
||||||
)
|
)
|
||||||
|
|
||||||
return styles.BaseStyle.Padding(1, 2).
|
return styles.BaseStyle.Padding(1, 2).
|
||||||
@@ -223,4 +224,3 @@ func NewSessionDialogCmp() SessionDialog {
|
|||||||
selectedSessionID: "",
|
selectedSessionID: "",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -9,9 +9,9 @@ import (
|
|||||||
"github.com/charmbracelet/bubbles/viewport"
|
"github.com/charmbracelet/bubbles/viewport"
|
||||||
tea "github.com/charmbracelet/bubbletea"
|
tea "github.com/charmbracelet/bubbletea"
|
||||||
"github.com/charmbracelet/lipgloss"
|
"github.com/charmbracelet/lipgloss"
|
||||||
"github.com/kujtimiihoxha/opencode/internal/logging"
|
"github.com/opencode-ai/opencode/internal/logging"
|
||||||
"github.com/kujtimiihoxha/opencode/internal/tui/layout"
|
"github.com/opencode-ai/opencode/internal/tui/layout"
|
||||||
"github.com/kujtimiihoxha/opencode/internal/tui/styles"
|
"github.com/opencode-ai/opencode/internal/tui/styles"
|
||||||
)
|
)
|
||||||
|
|
||||||
type DetailComponent interface {
|
type DetailComponent interface {
|
||||||
|
|||||||
@@ -7,11 +7,11 @@ import (
|
|||||||
"github.com/charmbracelet/bubbles/key"
|
"github.com/charmbracelet/bubbles/key"
|
||||||
"github.com/charmbracelet/bubbles/table"
|
"github.com/charmbracelet/bubbles/table"
|
||||||
tea "github.com/charmbracelet/bubbletea"
|
tea "github.com/charmbracelet/bubbletea"
|
||||||
"github.com/kujtimiihoxha/opencode/internal/logging"
|
"github.com/opencode-ai/opencode/internal/logging"
|
||||||
"github.com/kujtimiihoxha/opencode/internal/pubsub"
|
"github.com/opencode-ai/opencode/internal/pubsub"
|
||||||
"github.com/kujtimiihoxha/opencode/internal/tui/layout"
|
"github.com/opencode-ai/opencode/internal/tui/layout"
|
||||||
"github.com/kujtimiihoxha/opencode/internal/tui/styles"
|
"github.com/opencode-ai/opencode/internal/tui/styles"
|
||||||
"github.com/kujtimiihoxha/opencode/internal/tui/util"
|
"github.com/opencode-ai/opencode/internal/tui/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
type TableComponent interface {
|
type TableComponent interface {
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import (
|
|||||||
"github.com/charmbracelet/bubbles/key"
|
"github.com/charmbracelet/bubbles/key"
|
||||||
tea "github.com/charmbracelet/bubbletea"
|
tea "github.com/charmbracelet/bubbletea"
|
||||||
"github.com/charmbracelet/lipgloss"
|
"github.com/charmbracelet/lipgloss"
|
||||||
"github.com/kujtimiihoxha/opencode/internal/tui/styles"
|
"github.com/opencode-ai/opencode/internal/tui/styles"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Container interface {
|
type Container interface {
|
||||||
|
|||||||
@@ -5,8 +5,8 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/charmbracelet/lipgloss"
|
"github.com/charmbracelet/lipgloss"
|
||||||
"github.com/kujtimiihoxha/opencode/internal/tui/styles"
|
"github.com/opencode-ai/opencode/internal/tui/styles"
|
||||||
"github.com/kujtimiihoxha/opencode/internal/tui/util"
|
"github.com/opencode-ai/opencode/internal/tui/util"
|
||||||
"github.com/mattn/go-runewidth"
|
"github.com/mattn/go-runewidth"
|
||||||
"github.com/muesli/ansi"
|
"github.com/muesli/ansi"
|
||||||
"github.com/muesli/reflow/truncate"
|
"github.com/muesli/reflow/truncate"
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import (
|
|||||||
"github.com/charmbracelet/bubbles/key"
|
"github.com/charmbracelet/bubbles/key"
|
||||||
tea "github.com/charmbracelet/bubbletea"
|
tea "github.com/charmbracelet/bubbletea"
|
||||||
"github.com/charmbracelet/lipgloss"
|
"github.com/charmbracelet/lipgloss"
|
||||||
"github.com/kujtimiihoxha/opencode/internal/tui/styles"
|
"github.com/opencode-ai/opencode/internal/tui/styles"
|
||||||
)
|
)
|
||||||
|
|
||||||
type SplitPaneLayout interface {
|
type SplitPaneLayout interface {
|
||||||
|
|||||||
+16
-24
@@ -5,22 +5,21 @@ import (
|
|||||||
|
|
||||||
"github.com/charmbracelet/bubbles/key"
|
"github.com/charmbracelet/bubbles/key"
|
||||||
tea "github.com/charmbracelet/bubbletea"
|
tea "github.com/charmbracelet/bubbletea"
|
||||||
"github.com/kujtimiihoxha/opencode/internal/app"
|
"github.com/opencode-ai/opencode/internal/app"
|
||||||
"github.com/kujtimiihoxha/opencode/internal/session"
|
"github.com/opencode-ai/opencode/internal/session"
|
||||||
"github.com/kujtimiihoxha/opencode/internal/tui/components/chat"
|
"github.com/opencode-ai/opencode/internal/tui/components/chat"
|
||||||
"github.com/kujtimiihoxha/opencode/internal/tui/layout"
|
"github.com/opencode-ai/opencode/internal/tui/layout"
|
||||||
"github.com/kujtimiihoxha/opencode/internal/tui/util"
|
"github.com/opencode-ai/opencode/internal/tui/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
var ChatPage PageID = "chat"
|
var ChatPage PageID = "chat"
|
||||||
|
|
||||||
type chatPage struct {
|
type chatPage struct {
|
||||||
app *app.App
|
app *app.App
|
||||||
editor layout.Container
|
editor layout.Container
|
||||||
messages layout.Container
|
messages layout.Container
|
||||||
layout layout.SplitPaneLayout
|
layout layout.SplitPaneLayout
|
||||||
session session.Session
|
session session.Session
|
||||||
editingMode bool
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type ChatKeyMap struct {
|
type ChatKeyMap struct {
|
||||||
@@ -34,8 +33,8 @@ var keyMap = ChatKeyMap{
|
|||||||
key.WithHelp("ctrl+n", "new session"),
|
key.WithHelp("ctrl+n", "new session"),
|
||||||
),
|
),
|
||||||
Cancel: key.NewBinding(
|
Cancel: key.NewBinding(
|
||||||
key.WithKeys("ctrl+x"),
|
key.WithKeys("esc"),
|
||||||
key.WithHelp("ctrl+x", "cancel"),
|
key.WithHelp("esc", "cancel"),
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -65,8 +64,6 @@ func (p *chatPage) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
p.session = msg
|
p.session = msg
|
||||||
case chat.EditorFocusMsg:
|
|
||||||
p.editingMode = bool(msg)
|
|
||||||
case tea.KeyMsg:
|
case tea.KeyMsg:
|
||||||
switch {
|
switch {
|
||||||
case key.Matches(msg, keyMap.NewSession):
|
case key.Matches(msg, keyMap.NewSession):
|
||||||
@@ -136,11 +133,7 @@ func (p *chatPage) View() string {
|
|||||||
|
|
||||||
func (p *chatPage) BindingKeys() []key.Binding {
|
func (p *chatPage) BindingKeys() []key.Binding {
|
||||||
bindings := layout.KeyMapToSlice(keyMap)
|
bindings := layout.KeyMapToSlice(keyMap)
|
||||||
if p.editingMode {
|
bindings = append(bindings, p.messages.BindingKeys()...)
|
||||||
bindings = append(bindings, p.editor.BindingKeys()...)
|
|
||||||
} else {
|
|
||||||
bindings = append(bindings, p.messages.BindingKeys()...)
|
|
||||||
}
|
|
||||||
return bindings
|
return bindings
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -155,10 +148,9 @@ func NewChatPage(app *app.App) tea.Model {
|
|||||||
layout.WithBorder(true, false, false, false),
|
layout.WithBorder(true, false, false, false),
|
||||||
)
|
)
|
||||||
return &chatPage{
|
return &chatPage{
|
||||||
app: app,
|
app: app,
|
||||||
editor: editorContainer,
|
editor: editorContainer,
|
||||||
messages: messagesContainer,
|
messages: messagesContainer,
|
||||||
editingMode: true,
|
|
||||||
layout: layout.NewSplitPane(
|
layout: layout.NewSplitPane(
|
||||||
layout.WithLeftPanel(messagesContainer),
|
layout.WithLeftPanel(messagesContainer),
|
||||||
layout.WithBottomPanel(editorContainer),
|
layout.WithBottomPanel(editorContainer),
|
||||||
|
|||||||
@@ -4,9 +4,9 @@ import (
|
|||||||
"github.com/charmbracelet/bubbles/key"
|
"github.com/charmbracelet/bubbles/key"
|
||||||
tea "github.com/charmbracelet/bubbletea"
|
tea "github.com/charmbracelet/bubbletea"
|
||||||
"github.com/charmbracelet/lipgloss"
|
"github.com/charmbracelet/lipgloss"
|
||||||
"github.com/kujtimiihoxha/opencode/internal/tui/components/logs"
|
"github.com/opencode-ai/opencode/internal/tui/components/logs"
|
||||||
"github.com/kujtimiihoxha/opencode/internal/tui/layout"
|
"github.com/opencode-ai/opencode/internal/tui/layout"
|
||||||
"github.com/kujtimiihoxha/opencode/internal/tui/styles"
|
"github.com/opencode-ai/opencode/internal/tui/styles"
|
||||||
)
|
)
|
||||||
|
|
||||||
var LogsPage PageID = "logs"
|
var LogsPage PageID = "logs"
|
||||||
|
|||||||
+16
-22
@@ -6,17 +6,17 @@ import (
|
|||||||
"github.com/charmbracelet/bubbles/key"
|
"github.com/charmbracelet/bubbles/key"
|
||||||
tea "github.com/charmbracelet/bubbletea"
|
tea "github.com/charmbracelet/bubbletea"
|
||||||
"github.com/charmbracelet/lipgloss"
|
"github.com/charmbracelet/lipgloss"
|
||||||
"github.com/kujtimiihoxha/opencode/internal/app"
|
"github.com/opencode-ai/opencode/internal/app"
|
||||||
"github.com/kujtimiihoxha/opencode/internal/config"
|
"github.com/opencode-ai/opencode/internal/config"
|
||||||
"github.com/kujtimiihoxha/opencode/internal/logging"
|
"github.com/opencode-ai/opencode/internal/logging"
|
||||||
"github.com/kujtimiihoxha/opencode/internal/permission"
|
"github.com/opencode-ai/opencode/internal/permission"
|
||||||
"github.com/kujtimiihoxha/opencode/internal/pubsub"
|
"github.com/opencode-ai/opencode/internal/pubsub"
|
||||||
"github.com/kujtimiihoxha/opencode/internal/tui/components/chat"
|
"github.com/opencode-ai/opencode/internal/tui/components/chat"
|
||||||
"github.com/kujtimiihoxha/opencode/internal/tui/components/core"
|
"github.com/opencode-ai/opencode/internal/tui/components/core"
|
||||||
"github.com/kujtimiihoxha/opencode/internal/tui/components/dialog"
|
"github.com/opencode-ai/opencode/internal/tui/components/dialog"
|
||||||
"github.com/kujtimiihoxha/opencode/internal/tui/layout"
|
"github.com/opencode-ai/opencode/internal/tui/layout"
|
||||||
"github.com/kujtimiihoxha/opencode/internal/tui/page"
|
"github.com/opencode-ai/opencode/internal/tui/page"
|
||||||
"github.com/kujtimiihoxha/opencode/internal/tui/util"
|
"github.com/opencode-ai/opencode/internal/tui/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
type keyMap struct {
|
type keyMap struct {
|
||||||
@@ -30,7 +30,7 @@ type keyMap struct {
|
|||||||
var keys = keyMap{
|
var keys = keyMap{
|
||||||
Logs: key.NewBinding(
|
Logs: key.NewBinding(
|
||||||
key.WithKeys("ctrl+l"),
|
key.WithKeys("ctrl+l"),
|
||||||
key.WithHelp("ctrl+L", "logs"),
|
key.WithHelp("ctrl+l", "logs"),
|
||||||
),
|
),
|
||||||
|
|
||||||
Quit: key.NewBinding(
|
Quit: key.NewBinding(
|
||||||
@@ -49,7 +49,7 @@ var keys = keyMap{
|
|||||||
|
|
||||||
Commands: key.NewBinding(
|
Commands: key.NewBinding(
|
||||||
key.WithKeys("ctrl+k"),
|
key.WithKeys("ctrl+k"),
|
||||||
key.WithHelp("ctrl+K", "commands"),
|
key.WithHelp("ctrl+k", "commands"),
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -95,8 +95,6 @@ type appModel struct {
|
|||||||
|
|
||||||
showInitDialog bool
|
showInitDialog bool
|
||||||
initDialog dialog.InitDialogCmp
|
initDialog dialog.InitDialogCmp
|
||||||
|
|
||||||
editingMode bool
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a appModel) Init() tea.Cmd {
|
func (a appModel) Init() tea.Cmd {
|
||||||
@@ -164,8 +162,6 @@ func (a appModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
|||||||
a.initDialog.SetSize(msg.Width, msg.Height)
|
a.initDialog.SetSize(msg.Width, msg.Height)
|
||||||
|
|
||||||
return a, tea.Batch(cmds...)
|
return a, tea.Batch(cmds...)
|
||||||
case chat.EditorFocusMsg:
|
|
||||||
a.editingMode = bool(msg)
|
|
||||||
// Status
|
// Status
|
||||||
case util.InfoMsg:
|
case util.InfoMsg:
|
||||||
s, cmd := a.status.Update(msg)
|
s, cmd := a.status.Update(msg)
|
||||||
@@ -228,7 +224,6 @@ func (a appModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
|||||||
a.app.Permissions.GrantPersistant(msg.Permission)
|
a.app.Permissions.GrantPersistant(msg.Permission)
|
||||||
case dialog.PermissionDeny:
|
case dialog.PermissionDeny:
|
||||||
a.app.Permissions.Deny(msg.Permission)
|
a.app.Permissions.Deny(msg.Permission)
|
||||||
cmd = util.CmdHandler(chat.FocusEditorMsg(true))
|
|
||||||
}
|
}
|
||||||
a.showPermissions = false
|
a.showPermissions = false
|
||||||
return a, cmd
|
return a, cmd
|
||||||
@@ -360,7 +355,7 @@ func (a appModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
|||||||
a.showHelp = !a.showHelp
|
a.showHelp = !a.showHelp
|
||||||
return a, nil
|
return a, nil
|
||||||
case key.Matches(msg, helpEsc):
|
case key.Matches(msg, helpEsc):
|
||||||
if !a.editingMode {
|
if a.app.CoderAgent.IsBusy() {
|
||||||
if a.showQuit {
|
if a.showQuit {
|
||||||
return a, nil
|
return a, nil
|
||||||
}
|
}
|
||||||
@@ -477,7 +472,7 @@ func (a appModel) View() string {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
if a.editingMode {
|
if !a.app.CoderAgent.IsBusy() {
|
||||||
a.status.SetHelpMsg("ctrl+? help")
|
a.status.SetHelpMsg("ctrl+? help")
|
||||||
} else {
|
} else {
|
||||||
a.status.SetHelpMsg("? help")
|
a.status.SetHelpMsg("? help")
|
||||||
@@ -494,7 +489,7 @@ func (a appModel) View() string {
|
|||||||
if a.currentPage == page.LogsPage {
|
if a.currentPage == page.LogsPage {
|
||||||
bindings = append(bindings, logsKeyReturnKey)
|
bindings = append(bindings, logsKeyReturnKey)
|
||||||
}
|
}
|
||||||
if !a.editingMode {
|
if !a.app.CoderAgent.IsBusy() {
|
||||||
bindings = append(bindings, helpEsc)
|
bindings = append(bindings, helpEsc)
|
||||||
}
|
}
|
||||||
a.help.SetBindings(bindings)
|
a.help.SetBindings(bindings)
|
||||||
@@ -585,7 +580,6 @@ func New(app *app.App) tea.Model {
|
|||||||
permissions: dialog.NewPermissionDialogCmp(),
|
permissions: dialog.NewPermissionDialogCmp(),
|
||||||
initDialog: dialog.NewInitDialogCmp(),
|
initDialog: dialog.NewInitDialogCmp(),
|
||||||
app: app,
|
app: app,
|
||||||
editingMode: true,
|
|
||||||
commands: []dialog.Command{},
|
commands: []dialog.Command{},
|
||||||
pages: map[page.PageID]tea.Model{
|
pages: map[page.PageID]tea.Model{
|
||||||
page.ChatPage: page.NewChatPage(app),
|
page.ChatPage: page.NewChatPage(app),
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import "runtime/debug"
|
|||||||
// Build-time parameters set via -ldflags
|
// Build-time parameters set via -ldflags
|
||||||
var Version = "unknown"
|
var Version = "unknown"
|
||||||
|
|
||||||
// A user may install pug using `go install github.com/kujtimiihoxha/opencode@latest`.
|
// A user may install pug using `go install github.com/opencode-ai/opencode@latest`.
|
||||||
// without -ldflags, in which case the version above is unset. As a workaround
|
// without -ldflags, in which case the version above is unset. As a workaround
|
||||||
// we use the embedded build version that *is* set when using `go install` (and
|
// we use the embedded build version that *is* set when using `go install` (and
|
||||||
// is only set for `go install` and not for `go build`).
|
// is only set for `go install` and not for `go build`).
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/kujtimiihoxha/opencode/cmd"
|
"github.com/opencode-ai/opencode/cmd"
|
||||||
"github.com/kujtimiihoxha/opencode/internal/logging"
|
"github.com/opencode-ai/opencode/internal/logging"
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
|||||||
Executable
+43
@@ -0,0 +1,43 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
# Parse command line arguments
|
||||||
|
minor=false
|
||||||
|
while [ "$#" -gt 0 ]; do
|
||||||
|
case "$1" in
|
||||||
|
--minor) minor=true; shift 1;;
|
||||||
|
*) echo "Unknown parameter: $1"; exit 1;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
git fetch --force --tags
|
||||||
|
|
||||||
|
# Get the latest Git tag
|
||||||
|
latest_tag=$(git tag --sort=committerdate | grep -E '[0-9]' | tail -1)
|
||||||
|
|
||||||
|
# If there is no tag, exit the script
|
||||||
|
if [ -z "$latest_tag" ]; then
|
||||||
|
echo "No tags found"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "Latest tag: $latest_tag"
|
||||||
|
|
||||||
|
# Split the tag into major, minor, and patch numbers
|
||||||
|
IFS='.' read -ra VERSION <<< "$latest_tag"
|
||||||
|
|
||||||
|
if [ "$minor" = true ]; then
|
||||||
|
# Increment the minor version and reset patch to 0
|
||||||
|
minor_number=${VERSION[1]}
|
||||||
|
let "minor_number++"
|
||||||
|
new_version="${VERSION[0]}.$minor_number.0"
|
||||||
|
else
|
||||||
|
# Increment the patch version
|
||||||
|
patch_number=${VERSION[2]}
|
||||||
|
let "patch_number++"
|
||||||
|
new_version="${VERSION[0]}.${VERSION[1]}.$patch_number"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "New version: $new_version"
|
||||||
|
|
||||||
|
git tag $new_version
|
||||||
|
git push --tags
|
||||||
Executable
+3
@@ -0,0 +1,3 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -e
|
||||||
|
goreleaser build --clean --snapshot --skip validate
|
||||||
Reference in New Issue
Block a user