1package prompt
2
3import (
4 "context"
5 _ "embed"
6 "fmt"
7 "os"
8 "path/filepath"
9 "runtime"
10 "strconv"
11 "time"
12
13 "github.com/charmbracelet/catwalk/pkg/catwalk"
14 "github.com/charmbracelet/crush/internal/config"
15 "github.com/charmbracelet/crush/internal/llm/tools"
16)
17
18func CoderPrompt(p string, contextFiles ...string) string {
19 var basePrompt string
20
21 basePrompt = string(anthropicCoderPrompt)
22 switch p {
23 case string(catwalk.InferenceProviderOpenAI):
24 basePrompt = string(openaiCoderPrompt)
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 ls := tools.NewLsTool(cwd)
60 r, _ := ls.Run(context.Background(), tools.ToolCall{
61 Input: `{"path":"."}`,
62 })
63 return fmt.Sprintf(`Here is useful information about the environment you are running in:
64<env>
65Working directory: %s
66Is directory a git repo: %s
67Platform: %s
68Today's date: %s
69</env>
70<project>
71%s
72</project>
73 `, cwd, boolToYesNo(isGit), platform, date, r.Content)
74}
75
76func isGitRepo(dir string) bool {
77 _, err := os.Stat(filepath.Join(dir, ".git"))
78 return err == nil
79}
80
81func lspInformation() string {
82 cfg := config.Get()
83 hasLSP := false
84 for _, v := range cfg.LSP {
85 if !v.Disabled {
86 hasLSP = true
87 break
88 }
89 }
90 if !hasLSP {
91 return ""
92 }
93 return `# LSP Information
94Tools that support it will also include useful diagnostics such as linting and typechecking.
95- 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.
96- Take necessary actions to fix the issues.
97- 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.
98`
99}
100
101func boolToYesNo(b bool) string {
102 if b {
103 return "Yes"
104 }
105 return "No"
106}