coder.go

 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(cfg *config.Config, 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(cfg)
32
33	basePrompt = fmt.Sprintf("%s\n\n%s\n%s", basePrompt, envInfo, lspInformation(cfg))
34
35	contextContent := getContextFromPaths(cfg.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 v2.md
49var coderV2Prompt []byte
50
51func getEnvironmentInfo(cfg *config.Config) string {
52	cwd := cfg.WorkingDir()
53	isGit := isGitRepo(cwd)
54	platform := runtime.GOOS
55	date := time.Now().Format("1/2/2006")
56	output, _ := tools.ListDirectoryTree(cwd, nil)
57	return fmt.Sprintf(`Here is useful information about the environment you are running in:
58<env>
59Working directory: %s
60Is directory a git repo: %s
61Platform: %s
62Today's date: %s
63</env>
64<project>
65%s
66</project>
67		`, cwd, boolToYesNo(isGit), platform, date, output)
68}
69
70func isGitRepo(dir string) bool {
71	_, err := os.Stat(filepath.Join(dir, ".git"))
72	return err == nil
73}
74
75func lspInformation(cfg *config.Config) string {
76	hasLSP := false
77	for _, v := range cfg.LSP {
78		if !v.Disabled {
79			hasLSP = true
80			break
81		}
82	}
83	if !hasLSP {
84		return ""
85	}
86	return `# LSP Information
87Tools that support it will also include useful diagnostics such as linting and typechecking.
88- 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.
89- Take necessary actions to fix the issues.
90- 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.
91`
92}
93
94func boolToYesNo(b bool) string {
95	if b {
96		return "Yes"
97	}
98	return "No"
99}