1package prompt
2
3import (
4 "os"
5 "path/filepath"
6 "strings"
7 "sync"
8
9 "github.com/charmbracelet/crush/internal/config"
10 "github.com/charmbracelet/crush/internal/fur/provider"
11)
12
13type PromptID string
14
15const (
16 PromptCoder PromptID = "coder"
17 PromptTitle PromptID = "title"
18 PromptTask PromptID = "task"
19 PromptSummarizer PromptID = "summarizer"
20 PromptDefault PromptID = "default"
21)
22
23func GetPrompt(promptID PromptID, provider provider.InferenceProvider, contextPaths ...string) string {
24 basePrompt := ""
25 switch promptID {
26 case PromptCoder:
27 basePrompt = CoderPrompt(provider)
28 case PromptTitle:
29 basePrompt = TitlePrompt(provider)
30 case PromptTask:
31 basePrompt = TaskPrompt(provider)
32 case PromptSummarizer:
33 basePrompt = SummarizerPrompt(provider)
34 default:
35 basePrompt = "You are a helpful assistant"
36 }
37 return basePrompt
38}
39
40func getContextFromPaths(contextPaths []string) string {
41 return processContextPaths(config.WorkingDirectory(), contextPaths)
42}
43
44func processContextPaths(workDir string, paths []string) string {
45 var (
46 wg sync.WaitGroup
47 resultCh = make(chan string)
48 )
49
50 // Track processed files to avoid duplicates
51 processedFiles := make(map[string]bool)
52 var processedMutex sync.Mutex
53
54 for _, path := range paths {
55 wg.Add(1)
56 go func(p string) {
57 defer wg.Done()
58
59 if strings.HasSuffix(p, "/") {
60 filepath.WalkDir(filepath.Join(workDir, p), func(path string, d os.DirEntry, err error) error {
61 if err != nil {
62 return err
63 }
64 if !d.IsDir() {
65 // Check if we've already processed this file (case-insensitive)
66 lowerPath := strings.ToLower(path)
67
68 processedMutex.Lock()
69 alreadyProcessed := processedFiles[lowerPath]
70 if !alreadyProcessed {
71 processedFiles[lowerPath] = true
72 }
73 processedMutex.Unlock()
74
75 if !alreadyProcessed {
76 if result := processFile(path); result != "" {
77 resultCh <- result
78 }
79 }
80 }
81 return nil
82 })
83 } else {
84 fullPath := filepath.Join(workDir, p)
85
86 // Check if we've already processed this file (case-insensitive)
87 lowerPath := strings.ToLower(fullPath)
88
89 processedMutex.Lock()
90 alreadyProcessed := processedFiles[lowerPath]
91 if !alreadyProcessed {
92 processedFiles[lowerPath] = true
93 }
94 processedMutex.Unlock()
95
96 if !alreadyProcessed {
97 result := processFile(fullPath)
98 if result != "" {
99 resultCh <- result
100 }
101 }
102 }
103 }(path)
104 }
105
106 go func() {
107 wg.Wait()
108 close(resultCh)
109 }()
110
111 results := make([]string, 0)
112 for result := range resultCh {
113 results = append(results, result)
114 }
115
116 return strings.Join(results, "\n")
117}
118
119func processFile(filePath string) string {
120 content, err := os.ReadFile(filePath)
121 if err != nil {
122 return ""
123 }
124 return "# From:" + filePath + "\n" + string(content)
125}