coder.go

  1package prompt
  2
  3import (
  4	"context"
  5	_ "embed"
  6	"fmt"
  7	"log/slog"
  8	"os"
  9	"path/filepath"
 10	"runtime"
 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	if os.Getenv("CRUSH_CODER_V2") == "true" {
 22		basePrompt = string(baseCoderV2Prompt)
 23	} else {
 24		switch p {
 25		case string(catwalk.InferenceProviderOpenAI):
 26			basePrompt = string(baseOpenAICoderPrompt)
 27		case string(catwalk.InferenceProviderGemini), string(catwalk.InferenceProviderVertexAI):
 28			basePrompt = string(baseGeminiCoderPrompt)
 29		default:
 30			basePrompt = string(baseAnthropicCoderPrompt)
 31		}
 32	}
 33	envInfo := getEnvironmentInfo()
 34
 35	basePrompt = fmt.Sprintf("%s\n\n%s\n%s", basePrompt, envInfo, lspInformation())
 36
 37	contextContent := getContextFromPaths(config.Get().WorkingDir(), contextFiles)
 38	slog.Debug("Context content", "Context", contextContent)
 39	if contextContent != "" {
 40		return fmt.Sprintf("%s\n\n# Project-Specific Context\n Make sure to follow the instructions in the context below\n%s", basePrompt, contextContent)
 41	}
 42	return basePrompt
 43}
 44
 45//go:embed v2.md
 46var baseCoderV2Prompt []byte
 47
 48//go:embed openai.md
 49var baseOpenAICoderPrompt []byte
 50
 51//go:embed anthropic.md
 52var baseAnthropicCoderPrompt []byte
 53
 54//go:embed gemini.md
 55var baseGeminiCoderPrompt []byte
 56
 57func getEnvironmentInfo() string {
 58	cwd := config.Get().WorkingDir()
 59	isGit := isGitRepo(cwd)
 60	platform := runtime.GOOS
 61	date := time.Now().Format("1/2/2006")
 62	ls := tools.NewLsTool(cwd)
 63	r, _ := ls.Run(context.Background(), tools.ToolCall{
 64		Input: `{"path":"."}`,
 65	})
 66	return fmt.Sprintf(`Here is useful information about the environment you are running in:
 67<env>
 68Working directory: %s
 69Is directory a git repo: %s
 70Platform: %s
 71Today's date: %s
 72</env>
 73<project>
 74%s
 75</project>
 76		`, cwd, boolToYesNo(isGit), platform, date, r.Content)
 77}
 78
 79func isGitRepo(dir string) bool {
 80	_, err := os.Stat(filepath.Join(dir, ".git"))
 81	return err == nil
 82}
 83
 84func lspInformation() string {
 85	cfg := config.Get()
 86	hasLSP := false
 87	for _, v := range cfg.LSP {
 88		if !v.Disabled {
 89			hasLSP = true
 90			break
 91		}
 92	}
 93	if !hasLSP {
 94		return ""
 95	}
 96	return `# LSP Information
 97Tools that support it will also include useful diagnostics such as linting and typechecking.
 98- 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.
 99- Take necessary actions to fix the issues.
100- 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.
101`
102}
103
104func boolToYesNo(b bool) string {
105	if b {
106		return "Yes"
107	}
108	return "No"
109}