fix: unwrap and rewrap body text correctly

Amolith created

Previously, formatBody processed each line independently. When input
text was already wrapped (but incorrectly), each line got rewrapped
separately, making the wrapping worse.

Now consecutive plain text lines are collected in a buffer, joined with
spaces to unwrap them, then passed to wordWrap once as a single
paragraph. Blank lines, bullet points, and numbered lists interrupt the
buffer to preserve paragraph boundaries and list formatting.

Fixes: ed58135
Assisted-by: Claude Sonnet 4.5 via Crush

Change summary

wrapBody.go | 17 ++++++++++++++++-
1 file changed, 16 insertions(+), 1 deletion(-)

Detailed changes

wrapBody.go 🔗

@@ -14,15 +14,27 @@ var numberedListRegex = regexp.MustCompile(`^\d+\.\s`)
 func formatBody(body string) (string, error) {
 	lines := strings.Split(body, "\n")
 	var result []string
+	var plainTextBuffer []string
+
+	flushPlainText := func() {
+		if len(plainTextBuffer) > 0 {
+			joined := strings.Join(plainTextBuffer, " ")
+			wrapped := wordWrap(joined, 72)
+			result = append(result, wrapped)
+			plainTextBuffer = nil
+		}
+	}
 
 	for _, line := range lines {
 		trimmed := strings.TrimSpace(line)
 		if trimmed == "" {
+			flushPlainText()
 			result = append(result, "")
 			continue
 		}
 
 		if strings.HasPrefix(trimmed, "- ") || strings.HasPrefix(trimmed, "* ") {
+			flushPlainText()
 			marker := trimmed[:2]
 			content := trimmed[2:]
 			wrapped := wrapWithHangingIndent(marker, "  ", content, 72)
@@ -31,6 +43,7 @@ func formatBody(body string) (string, error) {
 		}
 
 		if numberedListRegex.MatchString(trimmed) {
+			flushPlainText()
 			parts := strings.SplitN(trimmed, " ", 2)
 			marker := parts[0] + " "
 			content := ""
@@ -43,9 +56,11 @@ func formatBody(body string) (string, error) {
 			continue
 		}
 
-		result = append(result, wordWrap(trimmed, 72))
+		plainTextBuffer = append(plainTextBuffer, trimmed)
 	}
 
+	flushPlainText()
+
 	return strings.Join(result, "\n"), nil
 }