Skip to content
This repository was archived by the owner on Jul 29, 2025. It is now read-only.

Commit 8f3a94d

Browse files
feat: configure context paths (#86)
1 parent 4415220 commit 8f3a94d

File tree

4 files changed

+175
-92
lines changed

4 files changed

+175
-92
lines changed

cmd/schema/main.go

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,27 @@ func generateSchema() map[string]any {
7777
"default": false,
7878
}
7979

80+
schema["properties"].(map[string]any)["contextPaths"] = map[string]any{
81+
"type": "array",
82+
"description": "Context paths for the application",
83+
"items": map[string]any{
84+
"type": "string",
85+
},
86+
"default": []string{
87+
".github/copilot-instructions.md",
88+
".cursorrules",
89+
".cursor/rules/",
90+
"CLAUDE.md",
91+
"CLAUDE.local.md",
92+
"opencode.md",
93+
"opencode.local.md",
94+
"OpenCode.md",
95+
"OpenCode.local.md",
96+
"OPENCODE.md",
97+
"OPENCODE.local.md",
98+
},
99+
}
100+
80101
// Add MCP servers
81102
schema["properties"].(map[string]any)["mcpServers"] = map[string]any{
82103
"type": "object",
@@ -259,4 +280,3 @@ func generateSchema() map[string]any {
259280

260281
return schema
261282
}
262-

internal/config/config.go

Lines changed: 24 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -67,14 +67,15 @@ type LSPConfig struct {
6767

6868
// Config is the main configuration structure for the application.
6969
type Config struct {
70-
Data Data `json:"data"`
71-
WorkingDir string `json:"wd,omitempty"`
72-
MCPServers map[string]MCPServer `json:"mcpServers,omitempty"`
73-
Providers map[models.ModelProvider]Provider `json:"providers,omitempty"`
74-
LSP map[string]LSPConfig `json:"lsp,omitempty"`
75-
Agents map[AgentName]Agent `json:"agents"`
76-
Debug bool `json:"debug,omitempty"`
77-
DebugLSP bool `json:"debugLSP,omitempty"`
70+
Data Data `json:"data"`
71+
WorkingDir string `json:"wd,omitempty"`
72+
MCPServers map[string]MCPServer `json:"mcpServers,omitempty"`
73+
Providers map[models.ModelProvider]Provider `json:"providers,omitempty"`
74+
LSP map[string]LSPConfig `json:"lsp,omitempty"`
75+
Agents map[AgentName]Agent `json:"agents"`
76+
Debug bool `json:"debug,omitempty"`
77+
DebugLSP bool `json:"debugLSP,omitempty"`
78+
ContextPaths []string `json:"contextPaths,omitempty"`
7879
}
7980

8081
// Application constants
@@ -84,6 +85,20 @@ const (
8485
appName = "opencode"
8586
)
8687

88+
var defaultContextPaths = []string{
89+
".github/copilot-instructions.md",
90+
".cursorrules",
91+
".cursor/rules/",
92+
"CLAUDE.md",
93+
"CLAUDE.local.md",
94+
"opencode.md",
95+
"opencode.local.md",
96+
"OpenCode.md",
97+
"OpenCode.local.md",
98+
"OPENCODE.md",
99+
"OPENCODE.local.md",
100+
}
101+
87102
// Global configuration instance
88103
var cfg *Config
89104

@@ -185,6 +200,7 @@ func configureViper() {
185200
// setDefaults configures default values for configuration options.
186201
func setDefaults(debug bool) {
187202
viper.SetDefault("data.directory", defaultDataDirectory)
203+
viper.SetDefault("contextPaths", defaultContextPaths)
188204

189205
if debug {
190206
viper.SetDefault("debug", true)

internal/llm/prompt/prompt.go

Lines changed: 70 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -5,26 +5,12 @@ import (
55
"os"
66
"path/filepath"
77
"strings"
8+
"sync"
89

910
"github.com/opencode-ai/opencode/internal/config"
1011
"github.com/opencode-ai/opencode/internal/llm/models"
1112
)
1213

13-
// contextFiles is a list of potential context files to check for
14-
var contextFiles = []string{
15-
".github/copilot-instructions.md",
16-
".cursorrules",
17-
".cursor/rules/", // Directory containing multiple rule files
18-
"CLAUDE.md",
19-
"CLAUDE.local.md",
20-
"opencode.md",
21-
"opencode.local.md",
22-
"OpenCode.md",
23-
"OpenCode.local.md",
24-
"OPENCODE.md",
25-
"OPENCODE.local.md",
26-
}
27-
2814
func GetAgentPrompt(agentName config.AgentName, provider models.ModelProvider) string {
2915
basePrompt := ""
3016
switch agentName {
@@ -40,45 +26,86 @@ func GetAgentPrompt(agentName config.AgentName, provider models.ModelProvider) s
4026

4127
if agentName == config.AgentCoder || agentName == config.AgentTask {
4228
// Add context from project-specific instruction files if they exist
43-
contextContent := getContextFromFiles()
29+
contextContent := getContextFromPaths()
4430
if contextContent != "" {
45-
return fmt.Sprintf("%s\n\n# Project-Specific Context\n%s", basePrompt, contextContent)
31+
return fmt.Sprintf("%s\n\n# Project-Specific Context\n Make sure to follow the instructions in the context below\n%s", basePrompt, contextContent)
4632
}
4733
}
4834
return basePrompt
4935
}
5036

51-
// getContextFromFiles checks for the existence of context files and returns their content
52-
func getContextFromFiles() string {
53-
workDir := config.WorkingDirectory()
54-
var contextContent string
37+
var (
38+
onceContext sync.Once
39+
contextContent string
40+
)
41+
42+
func getContextFromPaths() string {
43+
onceContext.Do(func() {
44+
var (
45+
cfg = config.Get()
46+
workDir = cfg.WorkingDir
47+
contextPaths = cfg.ContextPaths
48+
)
49+
50+
contextContent = processContextPaths(workDir, contextPaths)
51+
})
52+
53+
return contextContent
54+
}
55+
56+
func processContextPaths(workDir string, paths []string) string {
57+
var (
58+
wg sync.WaitGroup
59+
resultCh = make(chan string)
60+
)
61+
62+
for _, path := range paths {
63+
wg.Add(1)
64+
go func(p string) {
65+
defer wg.Done()
5566

56-
for _, path := range contextFiles {
57-
// Check if path ends with a slash (indicating a directory)
58-
if strings.HasSuffix(path, "/") {
59-
// Handle directory - read all files within it
60-
dirPath := filepath.Join(workDir, path)
61-
files, err := os.ReadDir(dirPath)
62-
if err == nil {
63-
for _, file := range files {
64-
if !file.IsDir() {
65-
filePath := filepath.Join(dirPath, file.Name())
66-
content, err := os.ReadFile(filePath)
67-
if err == nil {
68-
contextContent += fmt.Sprintf("\n# From %s\n%s\n", file.Name(), string(content))
67+
if strings.HasSuffix(p, "/") {
68+
filepath.WalkDir(filepath.Join(workDir, p), func(path string, d os.DirEntry, err error) error {
69+
if err != nil {
70+
return err
71+
}
72+
if !d.IsDir() {
73+
if result := processFile(path); result != "" {
74+
resultCh <- result
6975
}
7076
}
77+
return nil
78+
})
79+
} else {
80+
result := processFile(filepath.Join(workDir, p))
81+
if result != "" {
82+
resultCh <- result
7183
}
7284
}
73-
} else {
74-
// Handle individual file as before
75-
filePath := filepath.Join(workDir, path)
76-
content, err := os.ReadFile(filePath)
77-
if err == nil {
78-
contextContent += fmt.Sprintf("\n%s\n", string(content))
79-
}
80-
}
85+
}(path)
8186
}
8287

83-
return contextContent
88+
go func() {
89+
wg.Wait()
90+
close(resultCh)
91+
}()
92+
93+
var (
94+
results = make([]string, len(resultCh))
95+
i int
96+
)
97+
for result := range resultCh {
98+
results[i] = result
99+
i++
100+
}
101+
102+
return strings.Join(results, "\n")
84103
}
104+
105+
func processFile(filePath string) string {
106+
content, err := os.ReadFile(filePath)
107+
if err != nil {
108+
return ""
109+
}
110+
return "# From:" + filePath + "\n" + string(content)
111+
}

opencode-schema.json

Lines changed: 60 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -12,33 +12,33 @@
1212
"model": {
1313
"description": "Model ID for the agent",
1414
"enum": [
15+
"bedrock.claude-3.7-sonnet",
16+
"claude-3-haiku",
1517
"claude-3.7-sonnet",
16-
"claude-3-opus",
17-
"gpt-4.1-mini",
18-
"gpt-4o",
19-
"gpt-4o-mini",
20-
"gemini-2.0-flash-lite",
21-
"meta-llama/llama-4-maverick-17b-128e-instruct",
22-
"gpt-4.1",
18+
"claude-3.5-haiku",
19+
"o3",
2320
"gpt-4.5-preview",
24-
"o1",
25-
"gpt-4.1-nano",
21+
"o1-pro",
22+
"o4-mini",
23+
"gpt-4.1",
2624
"o3-mini",
25+
"gpt-4.1-nano",
26+
"gpt-4o-mini",
27+
"o1",
2728
"gemini-2.5-flash",
28-
"gemini-2.0-flash",
29-
"meta-llama/llama-4-scout-17b-16e-instruct",
30-
"bedrock.claude-3.7-sonnet",
31-
"o1-pro",
32-
"o3",
33-
"gemini-2.5",
3429
"qwen-qwq",
35-
"llama-3.3-70b-versatile",
30+
"meta-llama/llama-4-maverick-17b-128e-instruct",
31+
"claude-3-opus",
32+
"gpt-4o",
33+
"gemini-2.0-flash-lite",
34+
"gemini-2.0-flash",
3635
"deepseek-r1-distill-llama-70b",
36+
"llama-3.3-70b-versatile",
3737
"claude-3.5-sonnet",
38-
"claude-3-haiku",
39-
"claude-3.5-haiku",
40-
"o4-mini",
41-
"o1-mini"
38+
"o1-mini",
39+
"gpt-4.1-mini",
40+
"gemini-2.5",
41+
"meta-llama/llama-4-scout-17b-16e-instruct"
4242
],
4343
"type": "string"
4444
},
@@ -72,33 +72,33 @@
7272
"model": {
7373
"description": "Model ID for the agent",
7474
"enum": [
75+
"bedrock.claude-3.7-sonnet",
76+
"claude-3-haiku",
7577
"claude-3.7-sonnet",
76-
"claude-3-opus",
77-
"gpt-4.1-mini",
78-
"gpt-4o",
79-
"gpt-4o-mini",
80-
"gemini-2.0-flash-lite",
81-
"meta-llama/llama-4-maverick-17b-128e-instruct",
82-
"gpt-4.1",
78+
"claude-3.5-haiku",
79+
"o3",
8380
"gpt-4.5-preview",
84-
"o1",
85-
"gpt-4.1-nano",
81+
"o1-pro",
82+
"o4-mini",
83+
"gpt-4.1",
8684
"o3-mini",
85+
"gpt-4.1-nano",
86+
"gpt-4o-mini",
87+
"o1",
8788
"gemini-2.5-flash",
88-
"gemini-2.0-flash",
89-
"meta-llama/llama-4-scout-17b-16e-instruct",
90-
"bedrock.claude-3.7-sonnet",
91-
"o1-pro",
92-
"o3",
93-
"gemini-2.5",
9489
"qwen-qwq",
95-
"llama-3.3-70b-versatile",
90+
"meta-llama/llama-4-maverick-17b-128e-instruct",
91+
"claude-3-opus",
92+
"gpt-4o",
93+
"gemini-2.0-flash-lite",
94+
"gemini-2.0-flash",
9695
"deepseek-r1-distill-llama-70b",
96+
"llama-3.3-70b-versatile",
9797
"claude-3.5-sonnet",
98-
"claude-3-haiku",
99-
"claude-3.5-haiku",
100-
"o4-mini",
101-
"o1-mini"
98+
"o1-mini",
99+
"gpt-4.1-mini",
100+
"gemini-2.5",
101+
"meta-llama/llama-4-scout-17b-16e-instruct"
102102
],
103103
"type": "string"
104104
},
@@ -131,6 +131,26 @@
131131
},
132132
"type": "object"
133133
},
134+
"contextPaths": {
135+
"default": [
136+
".github/copilot-instructions.md",
137+
".cursorrules",
138+
".cursor/rules/",
139+
"CLAUDE.md",
140+
"CLAUDE.local.md",
141+
"opencode.md",
142+
"opencode.local.md",
143+
"OpenCode.md",
144+
"OpenCode.local.md",
145+
"OPENCODE.md",
146+
"OPENCODE.local.md"
147+
],
148+
"description": "Context paths for the application",
149+
"items": {
150+
"type": "string"
151+
},
152+
"type": "array"
153+
},
134154
"data": {
135155
"description": "Storage configuration",
136156
"properties": {

0 commit comments

Comments
 (0)