perf: don't recompile regex multiple times

Carlos Alexandro Becker created

Signed-off-by: Carlos Alexandro Becker <caarlos0@users.noreply.github.com>

Change summary

internal/agent/tools/fetch_helpers.go | 5 +++--
internal/oauth/copilot/client.go      | 3 ++-
2 files changed, 5 insertions(+), 3 deletions(-)

Detailed changes

internal/agent/tools/fetch_helpers.go 🔗

@@ -19,6 +19,8 @@ import (
 // BrowserUserAgent is a realistic browser User-Agent for better compatibility.
 const BrowserUserAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36"
 
+var multipleNewlinesRe = regexp.MustCompile(`\n{3,}`)
+
 // FetchURLAndConvert fetches a URL and converts HTML content to markdown.
 func FetchURLAndConvert(ctx context.Context, client *http.Client, url string) (string, error) {
 	req, err := http.NewRequestWithContext(ctx, "GET", url, nil)
@@ -128,8 +130,7 @@ func removeNoisyElements(htmlContent string) string {
 // cleanupMarkdown removes excessive whitespace and blank lines from markdown.
 func cleanupMarkdown(content string) string {
 	// Collapse multiple blank lines into at most two.
-	multipleNewlines := regexp.MustCompile(`\n{3,}`)
-	content = multipleNewlines.ReplaceAllString(content, "\n\n")
+	content = multipleNewlinesRe.ReplaceAllString(content, "\n\n")
 
 	// Remove trailing whitespace from each line.
 	lines := strings.Split(content, "\n")

internal/oauth/copilot/client.go 🔗

@@ -12,6 +12,8 @@ import (
 	"github.com/charmbracelet/crush/internal/log"
 )
 
+var assistantRolePattern = regexp.MustCompile(`"role"\s*:\s*"assistant"`)
+
 // NewClient creates a new HTTP client with a custom transport that adds the
 // X-Initiator header based on message history in the request body.
 func NewClient(isSubAgent, debug bool) *http.Client {
@@ -58,7 +60,6 @@ func (t *initiatorTransport) RoundTrip(req *http.Request) (*http.Response, error
 	// Check for assistant messages using regex to handle whitespace
 	// variations in the JSON while avoiding full unmarshalling overhead.
 	initiator := userInitiator
-	assistantRolePattern := regexp.MustCompile(`"role"\s*:\s*"assistant"`)
 	if assistantRolePattern.Match(bodyBytes) || t.isSubAgent {
 		slog.Debug("Setting X-Initiator header to agent (found assistant messages in history)")
 		initiator = agentInitiator