| 2891 | } |
| 2892 | |
| 2893 | func generateCommitMessage(parent context.Context, apiKey string, diff string, status string, truncated bool) (string, error) { |
| 2894 | client := openai.NewClient(option.WithAPIKey(apiKey)) |
| 2895 | |
| 2896 | requestCtx, cancel := context.WithTimeout(parent, 45*time.Second) |
| 2897 | defer cancel() |
| 2898 | |
| 2899 | systemPrompt := "You are an expert software engineer who writes clear, concise git commit messages. Use imperative mood, keep the subject line under 72 characters, and include an optional body with bullet points if helpful. Never wrap the message in quotes. Never include secrets, credentials, or file contents from .env files, environment variables, keys, or other sensitive data—even if they appear in the diff." |
| 2900 | |
| 2901 | var userPromptBuilder strings.Builder |
| 2902 | userPromptBuilder.WriteString("Write a git commit message for the staged changes.\n\nGit diff:\n") |
| 2903 | userPromptBuilder.WriteString(diff) |
| 2904 | if truncated { |
| 2905 | userPromptBuilder.WriteString("\n\n[Diff truncated to fit within prompt]") |
| 2906 | } |
| 2907 | |
| 2908 | if s := strings.TrimSpace(status); s != "" { |
| 2909 | userPromptBuilder.WriteString("\n\nGit status --short:\n") |
| 2910 | userPromptBuilder.WriteString(s) |
| 2911 | } |
| 2912 | |
| 2913 | resp, err := client.Chat.Completions.New(requestCtx, openai.ChatCompletionNewParams{ |
| 2914 | Model: shared.ChatModel(commitModelName), |
| 2915 | Messages: []openai.ChatCompletionMessageParamUnion{ |
| 2916 | { |
| 2917 | OfSystem: &openai.ChatCompletionSystemMessageParam{ |
| 2918 | Content: openai.ChatCompletionSystemMessageParamContentUnion{OfString: openai.String(systemPrompt)}, |
| 2919 | }, |
| 2920 | }, |
| 2921 | { |
| 2922 | OfUser: &openai.ChatCompletionUserMessageParam{ |
| 2923 | Content: openai.ChatCompletionUserMessageParamContentUnion{OfString: openai.String(userPromptBuilder.String())}, |
| 2924 | }, |
| 2925 | }, |
| 2926 | }, |
| 2927 | }) |
| 2928 | if err != nil { |
| 2929 | return "", fmt.Errorf("generate commit message: %w", err) |
| 2930 | } |
| 2931 | |
| 2932 | if resp == nil || len(resp.Choices) == 0 { |
| 2933 | return "", fmt.Errorf("model returned no commit message choices") |
| 2934 | } |
| 2935 | |
| 2936 | message := strings.TrimSpace(resp.Choices[0].Message.Content) |
| 2937 | if message == "" { |
| 2938 | return "", fmt.Errorf("model returned an empty commit message") |
| 2939 | } |
| 2940 | |
| 2941 | return message, nil |
| 2942 | } |
| 2943 | |
| 2944 | func truncateDiffForCommit(diff string) (string, bool) { |
| 2945 | runes := []rune(diff) |