1package prompt
2
3import (
4 _ "embed"
5 "fmt"
6 "os"
7 "path/filepath"
8 "runtime"
9 "strconv"
10 "time"
11
12 "github.com/charmbracelet/catwalk/pkg/catwalk"
13 "github.com/charmbracelet/crush/internal/config"
14 "github.com/charmbracelet/crush/internal/llm/tools"
15)
16
17func CoderPrompt(p string, contextFiles ...string) string {
18 var basePrompt string
19
20 basePrompt = string(anthropicCoderPrompt)
21 switch p {
22 case string(catwalk.InferenceProviderOpenAI):
23 // seems to behave better
24 basePrompt = string(coderV2Prompt)
25 case string(catwalk.InferenceProviderGemini):
26 basePrompt = string(geminiCoderPrompt)
27 }
28 if ok, _ := strconv.ParseBool(os.Getenv("CRUSH_CODER_V2")); ok {
29 basePrompt = string(coderV2Prompt)
30 }
31 envInfo := getEnvironmentInfo()
32
33 basePrompt = fmt.Sprintf("%s\n\n%s\n%s", basePrompt, envInfo, lspInformation())
34
35 contextContent := getContextFromPaths(config.Get().WorkingDir(), contextFiles)
36 if contextContent != "" {
37 return fmt.Sprintf("%s\n\n# Project-Specific Context\n Make sure to follow the instructions in the context below\n%s", basePrompt, contextContent)
38 }
39 return basePrompt
40}
41
42//go:embed anthropic.md
43var anthropicCoderPrompt []byte
44
45//go:embed gemini.md
46var geminiCoderPrompt []byte
47
48//go:embed openai.md
49var openaiCoderPrompt []byte
50
51//go:embed v2.md
52var coderV2Prompt []byte
53
54func getEnvironmentInfo() string {
55 cwd := config.Get().WorkingDir()
56 isGit := isGitRepo(cwd)
57 platform := runtime.GOOS
58 date := time.Now().Format("1/2/2006")
59 output, _ := tools.ListDirectoryTree(cwd, nil)
60 return fmt.Sprintf(`Here is useful information about the environment you are running in:
61<env>
62Working directory: %s
63Is directory a git repo: %s
64Platform: %s
65Today's date: %s
66</env>
67<project>
68%s
69</project>
70 `, cwd, boolToYesNo(isGit), platform, date, output)
71}
72
73func isGitRepo(dir string) bool {
74 _, err := os.Stat(filepath.Join(dir, ".git"))
75 return err == nil
76}
77
78func lspInformation() string {
79 cfg := config.Get()
80 hasLSP := false
81 for _, v := range cfg.LSP {
82 if !v.Disabled {
83 hasLSP = true
84 break
85 }
86 }
87 if !hasLSP {
88 return ""
89 }
90 return `# LSP Information
91Tools that support it will also include useful diagnostics such as linting and typechecking.
92- These diagnostics will be automatically enabled when you run the tool, and will be displayed in the output at the bottom within the <file_diagnostics></file_diagnostics> and <project_diagnostics></project_diagnostics> tags.
93- Take necessary actions to fix the issues.
94- You should ignore diagnostics of files that you did not change or are not related or caused by your changes unless the user explicitly asks you to fix them.
95`
96}
97
98func boolToYesNo(b bool) string {
99 if b {
100 return "Yes"
101 }
102 return "No"
103}