mirror of
https://github.com/aljazceru/opencode.git
synced 2025-12-22 10:14:22 +01:00
feat: configure context paths (#86)
This commit is contained in:
@@ -77,6 +77,27 @@ func generateSchema() map[string]any {
|
|||||||
"default": false,
|
"default": false,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
schema["properties"].(map[string]any)["contextPaths"] = map[string]any{
|
||||||
|
"type": "array",
|
||||||
|
"description": "Context paths for the application",
|
||||||
|
"items": map[string]any{
|
||||||
|
"type": "string",
|
||||||
|
},
|
||||||
|
"default": []string{
|
||||||
|
".github/copilot-instructions.md",
|
||||||
|
".cursorrules",
|
||||||
|
".cursor/rules/",
|
||||||
|
"CLAUDE.md",
|
||||||
|
"CLAUDE.local.md",
|
||||||
|
"opencode.md",
|
||||||
|
"opencode.local.md",
|
||||||
|
"OpenCode.md",
|
||||||
|
"OpenCode.local.md",
|
||||||
|
"OPENCODE.md",
|
||||||
|
"OPENCODE.local.md",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
// Add MCP servers
|
// Add MCP servers
|
||||||
schema["properties"].(map[string]any)["mcpServers"] = map[string]any{
|
schema["properties"].(map[string]any)["mcpServers"] = map[string]any{
|
||||||
"type": "object",
|
"type": "object",
|
||||||
@@ -259,4 +280,3 @@ func generateSchema() map[string]any {
|
|||||||
|
|
||||||
return schema
|
return schema
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -67,14 +67,15 @@ type LSPConfig struct {
|
|||||||
|
|
||||||
// Config is the main configuration structure for the application.
|
// Config is the main configuration structure for the application.
|
||||||
type Config struct {
|
type Config struct {
|
||||||
Data Data `json:"data"`
|
Data Data `json:"data"`
|
||||||
WorkingDir string `json:"wd,omitempty"`
|
WorkingDir string `json:"wd,omitempty"`
|
||||||
MCPServers map[string]MCPServer `json:"mcpServers,omitempty"`
|
MCPServers map[string]MCPServer `json:"mcpServers,omitempty"`
|
||||||
Providers map[models.ModelProvider]Provider `json:"providers,omitempty"`
|
Providers map[models.ModelProvider]Provider `json:"providers,omitempty"`
|
||||||
LSP map[string]LSPConfig `json:"lsp,omitempty"`
|
LSP map[string]LSPConfig `json:"lsp,omitempty"`
|
||||||
Agents map[AgentName]Agent `json:"agents"`
|
Agents map[AgentName]Agent `json:"agents"`
|
||||||
Debug bool `json:"debug,omitempty"`
|
Debug bool `json:"debug,omitempty"`
|
||||||
DebugLSP bool `json:"debugLSP,omitempty"`
|
DebugLSP bool `json:"debugLSP,omitempty"`
|
||||||
|
ContextPaths []string `json:"contextPaths,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Application constants
|
// Application constants
|
||||||
@@ -84,6 +85,20 @@ const (
|
|||||||
appName = "opencode"
|
appName = "opencode"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var defaultContextPaths = []string{
|
||||||
|
".github/copilot-instructions.md",
|
||||||
|
".cursorrules",
|
||||||
|
".cursor/rules/",
|
||||||
|
"CLAUDE.md",
|
||||||
|
"CLAUDE.local.md",
|
||||||
|
"opencode.md",
|
||||||
|
"opencode.local.md",
|
||||||
|
"OpenCode.md",
|
||||||
|
"OpenCode.local.md",
|
||||||
|
"OPENCODE.md",
|
||||||
|
"OPENCODE.local.md",
|
||||||
|
}
|
||||||
|
|
||||||
// Global configuration instance
|
// Global configuration instance
|
||||||
var cfg *Config
|
var cfg *Config
|
||||||
|
|
||||||
@@ -185,6 +200,7 @@ func configureViper() {
|
|||||||
// setDefaults configures default values for configuration options.
|
// setDefaults configures default values for configuration options.
|
||||||
func setDefaults(debug bool) {
|
func setDefaults(debug bool) {
|
||||||
viper.SetDefault("data.directory", defaultDataDirectory)
|
viper.SetDefault("data.directory", defaultDataDirectory)
|
||||||
|
viper.SetDefault("contextPaths", defaultContextPaths)
|
||||||
|
|
||||||
if debug {
|
if debug {
|
||||||
viper.SetDefault("debug", true)
|
viper.SetDefault("debug", true)
|
||||||
|
|||||||
@@ -5,26 +5,12 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
|
"sync"
|
||||||
|
|
||||||
"github.com/opencode-ai/opencode/internal/config"
|
"github.com/opencode-ai/opencode/internal/config"
|
||||||
"github.com/opencode-ai/opencode/internal/llm/models"
|
"github.com/opencode-ai/opencode/internal/llm/models"
|
||||||
)
|
)
|
||||||
|
|
||||||
// contextFiles is a list of potential context files to check for
|
|
||||||
var contextFiles = []string{
|
|
||||||
".github/copilot-instructions.md",
|
|
||||||
".cursorrules",
|
|
||||||
".cursor/rules/", // Directory containing multiple rule files
|
|
||||||
"CLAUDE.md",
|
|
||||||
"CLAUDE.local.md",
|
|
||||||
"opencode.md",
|
|
||||||
"opencode.local.md",
|
|
||||||
"OpenCode.md",
|
|
||||||
"OpenCode.local.md",
|
|
||||||
"OPENCODE.md",
|
|
||||||
"OPENCODE.local.md",
|
|
||||||
}
|
|
||||||
|
|
||||||
func GetAgentPrompt(agentName config.AgentName, provider models.ModelProvider) string {
|
func GetAgentPrompt(agentName config.AgentName, provider models.ModelProvider) string {
|
||||||
basePrompt := ""
|
basePrompt := ""
|
||||||
switch agentName {
|
switch agentName {
|
||||||
@@ -40,45 +26,86 @@ func GetAgentPrompt(agentName config.AgentName, provider models.ModelProvider) s
|
|||||||
|
|
||||||
if agentName == config.AgentCoder || agentName == config.AgentTask {
|
if agentName == config.AgentCoder || agentName == config.AgentTask {
|
||||||
// Add context from project-specific instruction files if they exist
|
// Add context from project-specific instruction files if they exist
|
||||||
contextContent := getContextFromFiles()
|
contextContent := getContextFromPaths()
|
||||||
if contextContent != "" {
|
if contextContent != "" {
|
||||||
return fmt.Sprintf("%s\n\n# Project-Specific Context\n%s", basePrompt, contextContent)
|
return fmt.Sprintf("%s\n\n# Project-Specific Context\n Make sure to follow the instructions in the context below\n%s", basePrompt, contextContent)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return basePrompt
|
return basePrompt
|
||||||
}
|
}
|
||||||
|
|
||||||
// getContextFromFiles checks for the existence of context files and returns their content
|
var (
|
||||||
func getContextFromFiles() string {
|
onceContext sync.Once
|
||||||
workDir := config.WorkingDirectory()
|
contextContent string
|
||||||
var contextContent string
|
)
|
||||||
|
|
||||||
for _, path := range contextFiles {
|
func getContextFromPaths() string {
|
||||||
// Check if path ends with a slash (indicating a directory)
|
onceContext.Do(func() {
|
||||||
if strings.HasSuffix(path, "/") {
|
var (
|
||||||
// Handle directory - read all files within it
|
cfg = config.Get()
|
||||||
dirPath := filepath.Join(workDir, path)
|
workDir = cfg.WorkingDir
|
||||||
files, err := os.ReadDir(dirPath)
|
contextPaths = cfg.ContextPaths
|
||||||
if err == nil {
|
)
|
||||||
for _, file := range files {
|
|
||||||
if !file.IsDir() {
|
contextContent = processContextPaths(workDir, contextPaths)
|
||||||
filePath := filepath.Join(dirPath, file.Name())
|
})
|
||||||
content, err := os.ReadFile(filePath)
|
|
||||||
if err == nil {
|
|
||||||
contextContent += fmt.Sprintf("\n# From %s\n%s\n", file.Name(), string(content))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// Handle individual file as before
|
|
||||||
filePath := filepath.Join(workDir, path)
|
|
||||||
content, err := os.ReadFile(filePath)
|
|
||||||
if err == nil {
|
|
||||||
contextContent += fmt.Sprintf("\n%s\n", string(content))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return contextContent
|
return contextContent
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func processContextPaths(workDir string, paths []string) string {
|
||||||
|
var (
|
||||||
|
wg sync.WaitGroup
|
||||||
|
resultCh = make(chan string)
|
||||||
|
)
|
||||||
|
|
||||||
|
for _, path := range paths {
|
||||||
|
wg.Add(1)
|
||||||
|
go func(p string) {
|
||||||
|
defer wg.Done()
|
||||||
|
|
||||||
|
if strings.HasSuffix(p, "/") {
|
||||||
|
filepath.WalkDir(filepath.Join(workDir, p), func(path string, d os.DirEntry, err error) error {
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if !d.IsDir() {
|
||||||
|
if result := processFile(path); result != "" {
|
||||||
|
resultCh <- result
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
result := processFile(filepath.Join(workDir, p))
|
||||||
|
if result != "" {
|
||||||
|
resultCh <- result
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}(path)
|
||||||
|
}
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
wg.Wait()
|
||||||
|
close(resultCh)
|
||||||
|
}()
|
||||||
|
|
||||||
|
var (
|
||||||
|
results = make([]string, len(resultCh))
|
||||||
|
i int
|
||||||
|
)
|
||||||
|
for result := range resultCh {
|
||||||
|
results[i] = result
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
|
||||||
|
return strings.Join(results, "\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
func processFile(filePath string) string {
|
||||||
|
content, err := os.ReadFile(filePath)
|
||||||
|
if err != nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return "# From:" + filePath + "\n" + string(content)
|
||||||
|
}
|
||||||
@@ -12,33 +12,33 @@
|
|||||||
"model": {
|
"model": {
|
||||||
"description": "Model ID for the agent",
|
"description": "Model ID for the agent",
|
||||||
"enum": [
|
"enum": [
|
||||||
"claude-3.7-sonnet",
|
|
||||||
"claude-3-opus",
|
|
||||||
"gpt-4.1-mini",
|
|
||||||
"gpt-4o",
|
|
||||||
"gpt-4o-mini",
|
|
||||||
"gemini-2.0-flash-lite",
|
|
||||||
"meta-llama/llama-4-maverick-17b-128e-instruct",
|
|
||||||
"gpt-4.1",
|
|
||||||
"gpt-4.5-preview",
|
|
||||||
"o1",
|
|
||||||
"gpt-4.1-nano",
|
|
||||||
"o3-mini",
|
|
||||||
"gemini-2.5-flash",
|
|
||||||
"gemini-2.0-flash",
|
|
||||||
"meta-llama/llama-4-scout-17b-16e-instruct",
|
|
||||||
"bedrock.claude-3.7-sonnet",
|
"bedrock.claude-3.7-sonnet",
|
||||||
"o1-pro",
|
|
||||||
"o3",
|
|
||||||
"gemini-2.5",
|
|
||||||
"qwen-qwq",
|
|
||||||
"llama-3.3-70b-versatile",
|
|
||||||
"deepseek-r1-distill-llama-70b",
|
|
||||||
"claude-3.5-sonnet",
|
|
||||||
"claude-3-haiku",
|
"claude-3-haiku",
|
||||||
|
"claude-3.7-sonnet",
|
||||||
"claude-3.5-haiku",
|
"claude-3.5-haiku",
|
||||||
|
"o3",
|
||||||
|
"gpt-4.5-preview",
|
||||||
|
"o1-pro",
|
||||||
"o4-mini",
|
"o4-mini",
|
||||||
"o1-mini"
|
"gpt-4.1",
|
||||||
|
"o3-mini",
|
||||||
|
"gpt-4.1-nano",
|
||||||
|
"gpt-4o-mini",
|
||||||
|
"o1",
|
||||||
|
"gemini-2.5-flash",
|
||||||
|
"qwen-qwq",
|
||||||
|
"meta-llama/llama-4-maverick-17b-128e-instruct",
|
||||||
|
"claude-3-opus",
|
||||||
|
"gpt-4o",
|
||||||
|
"gemini-2.0-flash-lite",
|
||||||
|
"gemini-2.0-flash",
|
||||||
|
"deepseek-r1-distill-llama-70b",
|
||||||
|
"llama-3.3-70b-versatile",
|
||||||
|
"claude-3.5-sonnet",
|
||||||
|
"o1-mini",
|
||||||
|
"gpt-4.1-mini",
|
||||||
|
"gemini-2.5",
|
||||||
|
"meta-llama/llama-4-scout-17b-16e-instruct"
|
||||||
],
|
],
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
@@ -72,33 +72,33 @@
|
|||||||
"model": {
|
"model": {
|
||||||
"description": "Model ID for the agent",
|
"description": "Model ID for the agent",
|
||||||
"enum": [
|
"enum": [
|
||||||
"claude-3.7-sonnet",
|
|
||||||
"claude-3-opus",
|
|
||||||
"gpt-4.1-mini",
|
|
||||||
"gpt-4o",
|
|
||||||
"gpt-4o-mini",
|
|
||||||
"gemini-2.0-flash-lite",
|
|
||||||
"meta-llama/llama-4-maverick-17b-128e-instruct",
|
|
||||||
"gpt-4.1",
|
|
||||||
"gpt-4.5-preview",
|
|
||||||
"o1",
|
|
||||||
"gpt-4.1-nano",
|
|
||||||
"o3-mini",
|
|
||||||
"gemini-2.5-flash",
|
|
||||||
"gemini-2.0-flash",
|
|
||||||
"meta-llama/llama-4-scout-17b-16e-instruct",
|
|
||||||
"bedrock.claude-3.7-sonnet",
|
"bedrock.claude-3.7-sonnet",
|
||||||
"o1-pro",
|
|
||||||
"o3",
|
|
||||||
"gemini-2.5",
|
|
||||||
"qwen-qwq",
|
|
||||||
"llama-3.3-70b-versatile",
|
|
||||||
"deepseek-r1-distill-llama-70b",
|
|
||||||
"claude-3.5-sonnet",
|
|
||||||
"claude-3-haiku",
|
"claude-3-haiku",
|
||||||
|
"claude-3.7-sonnet",
|
||||||
"claude-3.5-haiku",
|
"claude-3.5-haiku",
|
||||||
|
"o3",
|
||||||
|
"gpt-4.5-preview",
|
||||||
|
"o1-pro",
|
||||||
"o4-mini",
|
"o4-mini",
|
||||||
"o1-mini"
|
"gpt-4.1",
|
||||||
|
"o3-mini",
|
||||||
|
"gpt-4.1-nano",
|
||||||
|
"gpt-4o-mini",
|
||||||
|
"o1",
|
||||||
|
"gemini-2.5-flash",
|
||||||
|
"qwen-qwq",
|
||||||
|
"meta-llama/llama-4-maverick-17b-128e-instruct",
|
||||||
|
"claude-3-opus",
|
||||||
|
"gpt-4o",
|
||||||
|
"gemini-2.0-flash-lite",
|
||||||
|
"gemini-2.0-flash",
|
||||||
|
"deepseek-r1-distill-llama-70b",
|
||||||
|
"llama-3.3-70b-versatile",
|
||||||
|
"claude-3.5-sonnet",
|
||||||
|
"o1-mini",
|
||||||
|
"gpt-4.1-mini",
|
||||||
|
"gemini-2.5",
|
||||||
|
"meta-llama/llama-4-scout-17b-16e-instruct"
|
||||||
],
|
],
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
@@ -131,6 +131,26 @@
|
|||||||
},
|
},
|
||||||
"type": "object"
|
"type": "object"
|
||||||
},
|
},
|
||||||
|
"contextPaths": {
|
||||||
|
"default": [
|
||||||
|
".github/copilot-instructions.md",
|
||||||
|
".cursorrules",
|
||||||
|
".cursor/rules/",
|
||||||
|
"CLAUDE.md",
|
||||||
|
"CLAUDE.local.md",
|
||||||
|
"opencode.md",
|
||||||
|
"opencode.local.md",
|
||||||
|
"OpenCode.md",
|
||||||
|
"OpenCode.local.md",
|
||||||
|
"OPENCODE.md",
|
||||||
|
"OPENCODE.local.md"
|
||||||
|
],
|
||||||
|
"description": "Context paths for the application",
|
||||||
|
"items": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"type": "array"
|
||||||
|
},
|
||||||
"data": {
|
"data": {
|
||||||
"description": "Storage configuration",
|
"description": "Storage configuration",
|
||||||
"properties": {
|
"properties": {
|
||||||
|
|||||||
Reference in New Issue
Block a user