Merge remote-tracking branch 'origin/main' into crush-thinking

Kujtim Hoxha created

Change summary

README.md                                                      |  22 
cmd/logs.go                                                    | 168 +-
cmd/root.go                                                    |  20 
go.mod                                                         |  10 
go.sum                                                         |  20 
internal/config/config.go                                      |  45 
internal/llm/agent/agent.go                                    |   4 
internal/llm/agent/mcp-tools.go                                | 115 +
internal/llm/tools/bash.go                                     |   4 
internal/tui/components/chat/messages/renderer.go              |  32 
internal/tui/components/completions/completions.go             |  19 
internal/tui/components/dialogs/permissions/permissions.go     |  41 
internal/tui/page/chat/chat.go                                 |  22 
internal/tui/tui.go                                            |  10 
vendor/github.com/aymanbagabas/go-udiff/myers/diff.go          | 246 ----
vendor/github.com/charmbracelet/bubbletea/v2/tea.go            |   2 
vendor/github.com/charmbracelet/lipgloss/v2/canvas.go          |  59 
vendor/github.com/charmbracelet/ultraviolet/README.md          |   2 
vendor/github.com/charmbracelet/ultraviolet/input.go           |  28 
vendor/github.com/charmbracelet/ultraviolet/terminal.go        |  89 
vendor/github.com/charmbracelet/ultraviolet/terminal_reader.go |  14 
vendor/github.com/charmbracelet/ultraviolet/winch_unix.go      |  27 
vendor/golang.org/x/sync/errgroup/errgroup.go                  | 118 -
vendor/golang.org/x/sys/unix/zerrors_linux.go                  |  25 
vendor/golang.org/x/sys/unix/zerrors_linux_386.go              |   1 
vendor/golang.org/x/sys/unix/zerrors_linux_amd64.go            |   1 
vendor/golang.org/x/sys/unix/zerrors_linux_arm.go              |   1 
vendor/golang.org/x/sys/unix/zerrors_linux_arm64.go            |   1 
vendor/golang.org/x/sys/unix/zerrors_linux_loong64.go          |   1 
vendor/golang.org/x/sys/unix/zerrors_linux_mips.go             |   1 
vendor/golang.org/x/sys/unix/zerrors_linux_mips64.go           |   1 
vendor/golang.org/x/sys/unix/zerrors_linux_mips64le.go         |   1 
vendor/golang.org/x/sys/unix/zerrors_linux_mipsle.go           |   1 
vendor/golang.org/x/sys/unix/zerrors_linux_ppc.go              |   1 
vendor/golang.org/x/sys/unix/zerrors_linux_ppc64.go            |   1 
vendor/golang.org/x/sys/unix/zerrors_linux_ppc64le.go          |   1 
vendor/golang.org/x/sys/unix/zerrors_linux_riscv64.go          |   1 
vendor/golang.org/x/sys/unix/zerrors_linux_s390x.go            |   1 
vendor/golang.org/x/sys/unix/zerrors_linux_sparc64.go          |   1 
vendor/golang.org/x/sys/unix/ztypes_linux.go                   | 108 +
vendor/golang.org/x/sys/unix/ztypes_linux_386.go               |  16 
vendor/golang.org/x/sys/unix/ztypes_linux_amd64.go             |  16 
vendor/golang.org/x/sys/unix/ztypes_linux_arm.go               |  16 
vendor/golang.org/x/sys/unix/ztypes_linux_arm64.go             |  16 
vendor/golang.org/x/sys/unix/ztypes_linux_loong64.go           |  16 
vendor/golang.org/x/sys/unix/ztypes_linux_mips.go              |  16 
vendor/golang.org/x/sys/unix/ztypes_linux_mips64.go            |  16 
vendor/golang.org/x/sys/unix/ztypes_linux_mips64le.go          |  16 
vendor/golang.org/x/sys/unix/ztypes_linux_mipsle.go            |  16 
vendor/golang.org/x/sys/unix/ztypes_linux_ppc.go               |  16 
vendor/golang.org/x/sys/unix/ztypes_linux_ppc64.go             |  16 
vendor/golang.org/x/sys/unix/ztypes_linux_ppc64le.go           |  16 
vendor/golang.org/x/sys/unix/ztypes_linux_riscv64.go           |  16 
vendor/golang.org/x/sys/unix/ztypes_linux_s390x.go             |  16 
vendor/golang.org/x/sys/unix/ztypes_linux_sparc64.go           |  16 
vendor/modules.txt                                             |  15 
56 files changed, 882 insertions(+), 638 deletions(-)

Detailed changes

README.md 🔗

@@ -78,6 +78,28 @@ Crush can use LSPs for additional context to help inform its decisions, just lik
 }
 ```
 
+### MCPs
+
+Crush can also use MCPs for additional context. Add LSPs to the config like so:
+
+```json
+{
+  "mcp": {
+    "context7": {
+      "url": "https://mcp.context7.com/mcp",
+      "type": "http"
+    },
+    "github": {
+      "type": "http",
+      "url": "https://api.githubcopilot.com/mcp/",
+      "headers": {
+        "Authorization": "$(echo Bearer $GH_MCP_TOKEN)"
+      }
+    }
+  }
+}
+```
+
 ### OpenAI-Compatible APIs
 
 Crush supports all OpenAI-compatible APIs. Here's an example configuration for Deepseek, which uses an OpenAI-compatible API. Don't forget to set `DEEPSEEK_API_KEY` in your environment.

cmd/logs.go 🔗

@@ -1,14 +1,13 @@
 package cmd
 
 import (
-	"bufio"
+	"context"
 	"encoding/json"
 	"fmt"
 	"io"
 	"os"
 	"path/filepath"
 	"slices"
-	"strings"
 	"time"
 
 	"github.com/charmbracelet/crush/internal/config"
@@ -17,6 +16,8 @@ import (
 	"github.com/spf13/cobra"
 )
 
+const defaultTailLines = 1000
+
 var logsCmd = &cobra.Command{
 	Use:   "logs",
 	Short: "View crush logs",
@@ -38,7 +39,6 @@ var logsCmd = &cobra.Command{
 		}
 
 		log.SetLevel(log.DebugLevel)
-		// Configure log to output to stdout instead of stderr
 		log.SetOutput(os.Stdout)
 
 		cfg, err := config.Load(cwd, false)
@@ -53,104 +53,114 @@ var logsCmd = &cobra.Command{
 		}
 
 		if follow {
-			// Follow mode - tail the file continuously
-			t, err := tail.TailFile(logsFile, tail.Config{Follow: true, ReOpen: true, Logger: tail.DiscardingLogger})
-			if err != nil {
-				return fmt.Errorf("failed to tail log file: %v", err)
-			}
-
-			// Print the text of each received line
-			for line := range t.Lines {
-				printLogLine(line.Text)
-			}
-		} else if tailLines > 0 {
-			// Tail mode - show last N lines
-			lines, err := readLastNLines(logsFile, tailLines)
-			if err != nil {
-				return fmt.Errorf("failed to read last %d lines: %v", tailLines, err)
-			}
-			for _, line := range lines {
-				printLogLine(line)
-			}
-		} else {
-			// Oneshot mode - read the entire file once
-			file, err := os.Open(logsFile)
-			if err != nil {
-				return fmt.Errorf("failed to open log file: %v", err)
-			}
-			defer file.Close()
-
-			reader := bufio.NewReader(file)
-			for {
-				line, err := reader.ReadString('\n')
-				if err != nil {
-					if err == io.EOF && line != "" {
-						// Handle last line without newline
-						printLogLine(line)
-					}
-					break
-				}
-				// Remove trailing newline
-				line = strings.TrimSuffix(line, "\n")
-				printLogLine(line)
-			}
+			return followLogs(cmd.Context(), logsFile, tailLines)
 		}
 
-		return nil
+		return showLogs(logsFile, tailLines)
 	},
 }
 
 func init() {
 	logsCmd.Flags().BoolP("follow", "f", false, "Follow log output")
-	logsCmd.Flags().IntP("tail", "t", 0, "Show only the last N lines")
+	logsCmd.Flags().IntP("tail", "t", defaultTailLines, "Show only the last N lines default: 1000 for performance")
 	rootCmd.AddCommand(logsCmd)
 }
 
-// readLastNLines reads the last N lines from a file using a simple circular buffer approach
-func readLastNLines(filename string, n int) ([]string, error) {
-	file, err := os.Open(filename)
+func followLogs(ctx context.Context, logsFile string, tailLines int) error {
+	t, err := tail.TailFile(logsFile, tail.Config{
+		Follow: false,
+		ReOpen: false,
+		Logger: tail.DiscardingLogger,
+	})
 	if err != nil {
-		return nil, err
+		return fmt.Errorf("failed to tail log file: %v", err)
 	}
-	defer file.Close()
 
-	// Use a circular buffer to keep only the last N lines
-	lines := make([]string, n)
-	count := 0
-	index := 0
+	var lines []string
+	lineCount := 0
+	for line := range t.Lines {
+		if line.Err != nil {
+			continue
+		}
+		lines = append(lines, line.Text)
+		lineCount++
+		if lineCount >= tailLines {
+			if len(lines) > tailLines {
+				lines = lines[len(lines)-tailLines:]
+			}
+		}
+	}
+	t.Stop()
+
+	for _, line := range lines {
+		printLogLine(line)
+	}
+
+	if len(lines) == tailLines {
+		fmt.Fprintf(os.Stderr, "\nShowing last %d lines. Full logs available at: %s\n", tailLines, logsFile)
+		fmt.Fprintf(os.Stderr, "Following new log entries...\n\n")
+	}
+
+	t, err = tail.TailFile(logsFile, tail.Config{
+		Follow:   true,
+		ReOpen:   true,
+		Logger:   tail.DiscardingLogger,
+		Location: &tail.SeekInfo{Offset: 0, Whence: io.SeekEnd},
+	})
+	if err != nil {
+		return fmt.Errorf("failed to tail log file: %v", err)
+	}
+	defer t.Stop()
 
-	reader := bufio.NewReader(file)
 	for {
-		line, err := reader.ReadString('\n')
-		if err != nil {
-			if err == io.EOF && line != "" {
-				// Handle last line without newline
-				line = strings.TrimSuffix(line, "\n")
-				lines[index] = line
-				count++
-				index = (index + 1) % n
+		select {
+		case line := <-t.Lines:
+			if line.Err != nil {
+				continue
+			}
+			printLogLine(line.Text)
+		case <-ctx.Done():
+			return nil
+		}
+	}
+}
+
+func showLogs(logsFile string, tailLines int) error {
+	t, err := tail.TailFile(logsFile, tail.Config{
+		Follow:      false,
+		ReOpen:      false,
+		Logger:      tail.DiscardingLogger,
+		MaxLineSize: 0,
+	})
+	if err != nil {
+		return fmt.Errorf("failed to tail log file: %v", err)
+	}
+	defer t.Stop()
+
+	var lines []string
+	lineCount := 0
+	for line := range t.Lines {
+		if line.Err != nil {
+			continue
+		}
+		lines = append(lines, line.Text)
+		lineCount++
+		if lineCount >= tailLines {
+			if len(lines) > tailLines {
+				lines = lines[len(lines)-tailLines:]
 			}
-			break
 		}
-		// Remove trailing newline
-		line = strings.TrimSuffix(line, "\n")
-		lines[index] = line
-		count++
-		index = (index + 1) % n
 	}
 
-	// Extract the last N lines in correct order
-	if count <= n {
-		// We have fewer lines than requested, return them all
-		return lines[:count], nil
+	for _, line := range lines {
+		printLogLine(line)
 	}
 
-	// We have more lines than requested, extract from circular buffer
-	result := make([]string, n)
-	for i := range n {
-		result[i] = lines[(index+i)%n]
+	if len(lines) == tailLines {
+		fmt.Fprintf(os.Stderr, "\nShowing last %d lines. Full logs available at: %s\n", tailLines, logsFile)
 	}
-	return result, nil
+
+	return nil
 }
 
 func printLogLine(lineText string) {

cmd/root.go 🔗

@@ -6,14 +6,11 @@ import (
 	"io"
 	"log/slog"
 	"os"
-	"time"
 
 	tea "github.com/charmbracelet/bubbletea/v2"
 	"github.com/charmbracelet/crush/internal/app"
 	"github.com/charmbracelet/crush/internal/config"
 	"github.com/charmbracelet/crush/internal/db"
-	"github.com/charmbracelet/crush/internal/llm/agent"
-	"github.com/charmbracelet/crush/internal/log"
 	"github.com/charmbracelet/crush/internal/tui"
 	"github.com/charmbracelet/crush/internal/version"
 	"github.com/charmbracelet/fang"
@@ -92,9 +89,6 @@ to assist developers in writing, debugging, and understanding code directly from
 		}
 		defer app.Shutdown()
 
-		// Initialize MCP tools early for both modes
-		initMCPTools(ctx, app, cfg)
-
 		prompt, err = maybePrependStdin(prompt)
 		if err != nil {
 			slog.Error(fmt.Sprintf("Failed to read from stdin: %v", err))
@@ -126,20 +120,6 @@ to assist developers in writing, debugging, and understanding code directly from
 	},
 }
 
-func initMCPTools(ctx context.Context, app *app.App, cfg *config.Config) {
-	go func() {
-		defer log.RecoverPanic("MCP-goroutine", nil)
-
-		// Create a context with timeout for the initial MCP tools fetch
-		ctxWithTimeout, cancel := context.WithTimeout(ctx, 30*time.Second)
-		defer cancel()
-
-		// Set this up once with proper error handling
-		agent.GetMcpTools(ctxWithTimeout, app.Permissions, cfg)
-		slog.Info("MCP message handling goroutine exiting")
-	}()
-}
-
 func Execute() {
 	if err := fang.Execute(
 		context.Background(),

go.mod 🔗

@@ -2,9 +2,9 @@ module github.com/charmbracelet/crush
 
 go 1.24.3
 
-replace github.com/charmbracelet/bubbletea/v2 => github.com/charmbracelet/bubbletea-internal/v2 v2.0.0-20250710185017-3c0ffd25e595
+replace github.com/charmbracelet/bubbletea/v2 => github.com/charmbracelet/bubbletea-internal/v2 v2.0.0-20250716142813-5d1379f56ba2
 
-replace github.com/charmbracelet/lipgloss/v2 => github.com/charmbracelet/lipgloss-internal/v2 v2.0.0-20250710185058-03664cb9cecb
+replace github.com/charmbracelet/lipgloss/v2 => github.com/charmbracelet/lipgloss-internal/v2 v2.0.0-20250716143013-48f34cb75679
 
 require (
 	github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.7.0
@@ -75,7 +75,7 @@ require (
 	github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect
 	github.com/aymerick/douceur v0.2.0 // indirect
 	github.com/charmbracelet/colorprofile v0.3.1 // indirect
-	github.com/charmbracelet/ultraviolet v0.0.0-20250708152637-0fe0235c8db9 // indirect
+	github.com/charmbracelet/ultraviolet v0.0.0-20250716141307-2fae2b322afb // indirect
 	github.com/charmbracelet/x/cellbuf v0.0.14-0.20250516160309-24eee56f89fa // indirect
 	github.com/charmbracelet/x/exp/slice v0.0.0-20250611152503-f53cdd7e01ef
 	github.com/charmbracelet/x/term v0.2.1
@@ -133,8 +133,8 @@ require (
 	golang.org/x/crypto v0.37.0 // indirect
 	golang.org/x/image v0.26.0 // indirect
 	golang.org/x/net v0.39.0 // indirect
-	golang.org/x/sync v0.15.0 // indirect
-	golang.org/x/sys v0.33.0 // indirect
+	golang.org/x/sync v0.16.0 // indirect
+	golang.org/x/sys v0.34.0 // indirect
 	golang.org/x/term v0.31.0 // indirect
 	golang.org/x/text v0.24.0 // indirect
 	google.golang.org/genai v1.3.0

go.sum 🔗

@@ -70,20 +70,20 @@ github.com/charlievieth/fastwalk v1.0.11 h1:5sLT/q9+d9xMdpKExawLppqvXFZCVKf6JHnr
 github.com/charlievieth/fastwalk v1.0.11/go.mod h1:yGy1zbxog41ZVMcKA/i8ojXLFsuayX5VvwhQVoj9PBI=
 github.com/charmbracelet/bubbles/v2 v2.0.0-beta.1.0.20250710161907-a4c42b579198 h1:CkMS9Ah9ac1Ego5JDC5NJyZyAAqu23Z+O0yDwsa3IxM=
 github.com/charmbracelet/bubbles/v2 v2.0.0-beta.1.0.20250710161907-a4c42b579198/go.mod h1:6HamsBKWqEC/FVHuQMHgQL+knPyvHH55HwJDHl/adMw=
-github.com/charmbracelet/bubbletea-internal/v2 v2.0.0-20250710185017-3c0ffd25e595 h1:wLMjzOqrwoM7Em9UR9sGbn4375G8WuxcwFB3kjZiqHo=
-github.com/charmbracelet/bubbletea-internal/v2 v2.0.0-20250710185017-3c0ffd25e595/go.mod h1:+Tl7rePElw6OKt382t04zXwtPFoPXxAaJzNrYmtsLds=
+github.com/charmbracelet/bubbletea-internal/v2 v2.0.0-20250716142813-5d1379f56ba2 h1:Gj/vSk7h96TxUU/GSuwbYkr9H0ze+ElAQjcl25wB0+U=
+github.com/charmbracelet/bubbletea-internal/v2 v2.0.0-20250716142813-5d1379f56ba2/go.mod h1:m240IQxo1/eDQ7klblSzOCAUyc3LddHcV3Rc/YEGAgw=
 github.com/charmbracelet/colorprofile v0.3.1 h1:k8dTHMd7fgw4bnFd7jXTLZrSU/CQrKnL3m+AxCzDz40=
 github.com/charmbracelet/colorprofile v0.3.1/go.mod h1:/GkGusxNs8VB/RSOh3fu0TJmQ4ICMMPApIIVn0KszZ0=
 github.com/charmbracelet/fang v0.3.1-0.20250711140230-d5ebb8c1d674 h1:+Cz+VfxD5DO+JT1LlswXWhre0HYLj6l2HW8HVGfMuC0=
 github.com/charmbracelet/fang v0.3.1-0.20250711140230-d5ebb8c1d674/go.mod h1:9gCUAHmVx5BwSafeyNr3GI0GgvlB1WYjL21SkPp1jyU=
 github.com/charmbracelet/glamour/v2 v2.0.0-20250516160903-6f1e2c8f9ebe h1:i6ce4CcAlPpTj2ER69m1DBeLZ3RRcHnKExuwhKa3GfY=
 github.com/charmbracelet/glamour/v2 v2.0.0-20250516160903-6f1e2c8f9ebe/go.mod h1:p3Q+aN4eQKeM5jhrmXPMgPrlKbmc59rWSnMsSA3udhk=
-github.com/charmbracelet/lipgloss-internal/v2 v2.0.0-20250710185058-03664cb9cecb h1:lswj7CYZVYbLn2OhYJsXOMRQQGdRIfyuSnh5FdVSMr0=
-github.com/charmbracelet/lipgloss-internal/v2 v2.0.0-20250710185058-03664cb9cecb/go.mod h1:wEc/TRrTAIDJYjVCg3+y8WeKaN+F88gpYfGbUuP6W3A=
+github.com/charmbracelet/lipgloss-internal/v2 v2.0.0-20250716143013-48f34cb75679 h1:z0og40Sck650PjX51SmiTP8Div2OSugiZJBPZyJInOg=
+github.com/charmbracelet/lipgloss-internal/v2 v2.0.0-20250716143013-48f34cb75679/go.mod h1:BXY7j7rZgAprFwzNcO698++5KTd6GKI6lU83Pr4o0r0=
 github.com/charmbracelet/log/v2 v2.0.0-20250226163916-c379e29ff706 h1:WkwO6Ks3mSIGnGuSdKl9qDSyfbYK50z2wc2gGMggegE=
 github.com/charmbracelet/log/v2 v2.0.0-20250226163916-c379e29ff706/go.mod h1:mjJGp00cxcfvD5xdCa+bso251Jt4owrQvuimJtVmEmM=
-github.com/charmbracelet/ultraviolet v0.0.0-20250708152637-0fe0235c8db9 h1:+LLFCLxtb/sHegwY3zYdFAbaOgI/I9pv/pxdUlI1Q9s=
-github.com/charmbracelet/ultraviolet v0.0.0-20250708152637-0fe0235c8db9/go.mod h1:/O+B00+dYG6lqRAWIaNxSvywnDrIH6dmLYQAsH0LRTg=
+github.com/charmbracelet/ultraviolet v0.0.0-20250716141307-2fae2b322afb h1:fMyaSpuEyPP+uuTu98Wx2S/7OY52uhjF+kYfM5w7x+s=
+github.com/charmbracelet/ultraviolet v0.0.0-20250716141307-2fae2b322afb/go.mod h1:XrrgNFfXLrFAyd9DUmrqVc3yQFVv8Uk+okj4PsNNzpc=
 github.com/charmbracelet/x/ansi v0.9.3 h1:BXt5DHS/MKF+LjuK4huWrC6NCvHtexww7dMayh6GXd0=
 github.com/charmbracelet/x/ansi v0.9.3/go.mod h1:3RQDQ6lDnROptfpWuUVIUG64bD2g2BgntdxH0Ya5TeE=
 github.com/charmbracelet/x/cellbuf v0.0.14-0.20250516160309-24eee56f89fa h1:lphz0Z3rsiOtMYiz8axkT24i9yFiueDhJbzyNUADmME=
@@ -309,8 +309,8 @@ golang.org/x/net v0.39.0/go.mod h1:X7NRbYVEA+ewNkCNyJ513WmMdQ3BineSwVtN2zD/d+E=
 golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sync v0.15.0 h1:KWH3jNZsfyT6xfAfKiz6MRNmd46ByHDYaZ7KSkCtdW8=
-golang.org/x/sync v0.15.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
+golang.org/x/sync v0.16.0 h1:ycBJEhp9p4vXvUZNszeOq0kGTPghopOL8q0fq3vstxw=
+golang.org/x/sync v0.16.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
 golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
@@ -325,8 +325,8 @@ golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
 golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
 golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
-golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw=
-golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
+golang.org/x/sys v0.34.0 h1:H5Y5sJ2L2JRdyv7ROF1he/lPdvFsd0mJHFw2ThKHxLA=
+golang.org/x/sys v0.34.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
 golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
 golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
 golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=

internal/config/config.go 🔗

@@ -6,8 +6,10 @@ import (
 	"slices"
 	"strings"
 
+	"github.com/charmbracelet/crush/internal/env"
 	"github.com/charmbracelet/crush/internal/fur/provider"
 	"github.com/tidwall/sjson"
+	"golang.org/x/exp/slog"
 )
 
 const (
@@ -90,12 +92,12 @@ const (
 )
 
 type MCPConfig struct {
-	Command  string   `json:"command,omitempty" `
-	Env      []string `json:"env,omitempty"`
-	Args     []string `json:"args,omitempty"`
-	Type     MCPType  `json:"type"`
-	URL      string   `json:"url,omitempty"`
-	Disabled bool     `json:"disabled,omitempty"`
+	Command  string            `json:"command,omitempty" `
+	Env      map[string]string `json:"env,omitempty"`
+	Args     []string          `json:"args,omitempty"`
+	Type     MCPType           `json:"type"`
+	URL      string            `json:"url,omitempty"`
+	Disabled bool              `json:"disabled,omitempty"`
 
 	// TODO: maybe make it possible to get the value from the env
 	Headers map[string]string `json:"headers,omitempty"`
@@ -165,6 +167,37 @@ func (l LSPs) Sorted() []LSP {
 	return sorted
 }
 
+func (m MCPConfig) ResolvedEnv() []string {
+	resolver := NewShellVariableResolver(env.New())
+	for e, v := range m.Env {
+		var err error
+		m.Env[e], err = resolver.ResolveValue(v)
+		if err != nil {
+			slog.Error("error resolving environment variable", "error", err, "variable", e, "value", v)
+			continue
+		}
+	}
+
+	env := make([]string, 0, len(m.Env))
+	for k, v := range m.Env {
+		env = append(env, fmt.Sprintf("%s=%s", k, v))
+	}
+	return env
+}
+
+func (m MCPConfig) ResolvedHeaders() map[string]string {
+	resolver := NewShellVariableResolver(env.New())
+	for e, v := range m.Headers {
+		var err error
+		m.Headers[e], err = resolver.ResolveValue(v)
+		if err != nil {
+			slog.Error("error resolving header variable", "error", err, "variable", e, "value", v)
+			continue
+		}
+	}
+	return m.Headers
+}
+
 type Agent struct {
 	ID          string `json:"id,omitempty"`
 	Name        string `json:"name,omitempty"`

internal/llm/agent/agent.go 🔗

@@ -94,7 +94,7 @@ func NewAgent(
 ) (Service, error) {
 	ctx := context.Background()
 	cfg := config.Get()
-	otherTools := GetMcpTools(ctx, permissions, cfg)
+	otherTools := GetMCPTools(ctx, permissions, cfg)
 	if len(lspClients) > 0 {
 		otherTools = append(otherTools, tools.NewDiagnosticsTool(lspClients))
 	}
@@ -859,7 +859,7 @@ func (a *agent) UpdateModel() error {
 
 	// Get current provider configuration
 	currentProviderCfg := cfg.GetProviderForModel(a.agentCfg.Model)
-	if currentProviderCfg.ID == "" {
+	if currentProviderCfg == nil || currentProviderCfg.ID == "" {
 		return fmt.Errorf("provider for agent %s not found in config", a.agentCfg.Name)
 	}
 

internal/llm/agent/mcp-tools.go 🔗

@@ -5,6 +5,7 @@ import (
 	"encoding/json"
 	"fmt"
 	"log/slog"
+	"sync"
 
 	"github.com/charmbracelet/crush/internal/config"
 	"github.com/charmbracelet/crush/internal/llm/tools"
@@ -36,7 +37,7 @@ type MCPClient interface {
 }
 
 func (b *mcpTool) Name() string {
-	return fmt.Sprintf("%s_%s", b.mcpName, b.tool.Name)
+	return fmt.Sprintf("mcp_%s_%s", b.mcpName, b.tool.Name)
 }
 
 func (b *mcpTool) Info() tools.ToolInfo {
@@ -45,7 +46,7 @@ func (b *mcpTool) Info() tools.ToolInfo {
 		required = make([]string, 0)
 	}
 	return tools.ToolInfo{
-		Name:        fmt.Sprintf("%s_%s", b.mcpName, b.tool.Name),
+		Name:        fmt.Sprintf("mcp_%s_%s", b.mcpName, b.tool.Name),
 		Description: b.tool.Description,
 		Parameters:  b.tool.InputSchema.Properties,
 		Required:    required,
@@ -107,14 +108,14 @@ func (b *mcpTool) Run(ctx context.Context, params tools.ToolCall) (tools.ToolRes
 		},
 	)
 	if !p {
-		return tools.NewTextErrorResponse("permission denied"), nil
+		return tools.ToolResponse{}, permission.ErrorPermissionDenied
 	}
 
 	switch b.mcpConfig.Type {
 	case config.MCPStdio:
 		c, err := client.NewStdioMCPClient(
 			b.mcpConfig.Command,
-			b.mcpConfig.Env,
+			b.mcpConfig.ResolvedEnv(),
 			b.mcpConfig.Args...,
 		)
 		if err != nil {
@@ -124,7 +125,7 @@ func (b *mcpTool) Run(ctx context.Context, params tools.ToolCall) (tools.ToolRes
 	case config.MCPHttp:
 		c, err := client.NewStreamableHttpClient(
 			b.mcpConfig.URL,
-			transport.WithHTTPHeaders(b.mcpConfig.Headers),
+			transport.WithHTTPHeaders(b.mcpConfig.ResolvedHeaders()),
 		)
 		if err != nil {
 			return tools.NewTextErrorResponse(err.Error()), nil
@@ -133,7 +134,7 @@ func (b *mcpTool) Run(ctx context.Context, params tools.ToolCall) (tools.ToolRes
 	case config.MCPSse:
 		c, err := client.NewSSEMCPClient(
 			b.mcpConfig.URL,
-			client.WithHeaders(b.mcpConfig.Headers),
+			client.WithHeaders(b.mcpConfig.ResolvedHeaders()),
 		)
 		if err != nil {
 			return tools.NewTextErrorResponse(err.Error()), nil
@@ -154,8 +155,6 @@ func NewMcpTool(name string, tool mcp.Tool, permissions permission.Service, mcpC
 	}
 }
 
-var mcpTools []tools.BaseTool
-
 func getTools(ctx context.Context, name string, m config.MCPConfig, permissions permission.Service, c MCPClient, workingDir string) []tools.BaseTool {
 	var stdioTools []tools.BaseTool
 	initRequest := mcp.InitializeRequest{}
@@ -183,50 +182,72 @@ func getTools(ctx context.Context, name string, m config.MCPConfig, permissions
 	return stdioTools
 }
 
-func GetMcpTools(ctx context.Context, permissions permission.Service, cfg *config.Config) []tools.BaseTool {
-	if len(mcpTools) > 0 {
-		return mcpTools
-	}
+var (
+	mcpToolsOnce sync.Once
+	mcpTools     []tools.BaseTool
+)
+
+func GetMCPTools(ctx context.Context, permissions permission.Service, cfg *config.Config) []tools.BaseTool {
+	mcpToolsOnce.Do(func() {
+		mcpTools = doGetMCPTools(ctx, permissions, cfg)
+	})
+	return mcpTools
+}
+
+func doGetMCPTools(ctx context.Context, permissions permission.Service, cfg *config.Config) []tools.BaseTool {
+	var mu sync.Mutex
+	var wg sync.WaitGroup
+	var result []tools.BaseTool
 	for name, m := range cfg.MCP {
 		if m.Disabled {
 			slog.Debug("skipping disabled mcp", "name", name)
 			continue
 		}
-		switch m.Type {
-		case config.MCPStdio:
-			c, err := client.NewStdioMCPClient(
-				m.Command,
-				m.Env,
-				m.Args...,
-			)
-			if err != nil {
-				slog.Error("error creating mcp client", "error", err)
-				continue
+		wg.Add(1)
+		go func(name string, m config.MCPConfig) {
+			defer wg.Done()
+			switch m.Type {
+			case config.MCPStdio:
+				c, err := client.NewStdioMCPClient(
+					m.Command,
+					m.ResolvedEnv(),
+					m.Args...,
+				)
+				if err != nil {
+					slog.Error("error creating mcp client", "error", err)
+					return
+				}
+
+				mu.Lock()
+				result = append(result, getTools(ctx, name, m, permissions, c, cfg.WorkingDir())...)
+				mu.Unlock()
+			case config.MCPHttp:
+				c, err := client.NewStreamableHttpClient(
+					m.URL,
+					transport.WithHTTPHeaders(m.ResolvedHeaders()),
+				)
+				if err != nil {
+					slog.Error("error creating mcp client", "error", err)
+					return
+				}
+				mu.Lock()
+				result = append(result, getTools(ctx, name, m, permissions, c, cfg.WorkingDir())...)
+				mu.Unlock()
+			case config.MCPSse:
+				c, err := client.NewSSEMCPClient(
+					m.URL,
+					client.WithHeaders(m.ResolvedHeaders()),
+				)
+				if err != nil {
+					slog.Error("error creating mcp client", "error", err)
+					return
+				}
+				mu.Lock()
+				result = append(result, getTools(ctx, name, m, permissions, c, cfg.WorkingDir())...)
+				mu.Unlock()
 			}
-
-			mcpTools = append(mcpTools, getTools(ctx, name, m, permissions, c, cfg.WorkingDir())...)
-		case config.MCPHttp:
-			c, err := client.NewStreamableHttpClient(
-				m.URL,
-				transport.WithHTTPHeaders(m.Headers),
-			)
-			if err != nil {
-				slog.Error("error creating mcp client", "error", err)
-				continue
-			}
-			mcpTools = append(mcpTools, getTools(ctx, name, m, permissions, c, cfg.WorkingDir())...)
-		case config.MCPSse:
-			c, err := client.NewSSEMCPClient(
-				m.URL,
-				client.WithHeaders(m.Headers),
-			)
-			if err != nil {
-				slog.Error("error creating mcp client", "error", err)
-				continue
-			}
-			mcpTools = append(mcpTools, getTools(ctx, name, m, permissions, c, cfg.WorkingDir())...)
-		}
+		}(name, m)
 	}
-
-	return mcpTools
+	wg.Wait()
+	return result
 }

internal/llm/tools/bash.go 🔗

@@ -188,7 +188,7 @@ When the user asks you to create a new git commit, follow these steps carefully:
 
 4. Create the commit with a message ending with:
 💘 Generated with Crush
-Co-Authored-By: Crush <noreply@crush.charm.land>
+Co-Authored-By: Crush <crush@charm.land>
 
 - In order to ensure good formatting, ALWAYS pass the commit message via a HEREDOC, a la this example:
 <example>
@@ -196,7 +196,7 @@ git commit -m "$(cat <<'EOF'
  Commit message here.
 
  💘 Generated with Crush
- Co-Authored-By: 💘 Crush <noreply@crush.charm.land>
+ Co-Authored-By: 💘 Crush <crush@charm.land>
  EOF
  )"
 </example>

internal/tui/components/chat/messages/renderer.go 🔗

@@ -655,6 +655,7 @@ func joinHeaderBody(header, body string) string {
 
 func renderPlainContent(v *toolCallCmp, content string) string {
 	t := styles.CurrentTheme()
+	content = strings.ReplaceAll(content, "\r\n", "\n") // Normalize line endings
 	content = strings.TrimSpace(content)
 	lines := strings.Split(content, "\n")
 
@@ -664,6 +665,7 @@ func renderPlainContent(v *toolCallCmp, content string) string {
 		if i >= responseContextHeight {
 			break
 		}
+		ln = escapeContent(ln)
 		ln = " " + ln // left padding
 		if len(ln) > width {
 			ln = v.fit(ln, width)
@@ -680,6 +682,7 @@ func renderPlainContent(v *toolCallCmp, content string) string {
 			Width(width).
 			Render(fmt.Sprintf("… (%d lines)", len(lines)-responseContextHeight)))
 	}
+
 	return strings.Join(out, "\n")
 }
 
@@ -694,10 +697,17 @@ func pad(v any, width int) string {
 
 func renderCodeContent(v *toolCallCmp, path, content string, offset int) string {
 	t := styles.CurrentTheme()
+	content = strings.ReplaceAll(content, "\r\n", "\n") // Normalize line endings
+	content = strings.ReplaceAll(content, "\t", "    ") // Replace tabs with spaces
 	truncated := truncateHeight(content, responseContextHeight)
 
-	highlighted, _ := highlight.SyntaxHighlight(truncated, path, t.BgBase)
-	lines := strings.Split(highlighted, "\n")
+	lines := strings.Split(truncated, "\n")
+	for i, ln := range lines {
+		lines[i] = escapeContent(ln)
+	}
+
+	highlighted, _ := highlight.SyntaxHighlight(strings.Join(lines, "\n"), path, t.BgBase)
+	lines = strings.Split(highlighted, "\n")
 
 	if len(strings.Split(content, "\n")) > responseContextHeight {
 		lines = append(lines, t.S().Muted.
@@ -721,6 +731,7 @@ func renderCodeContent(v *toolCallCmp, path, content string, offset int) string
 				PaddingLeft(1).
 				Render(v.fit(ln, w-1)))
 	}
+
 	return lipgloss.JoinVertical(lipgloss.Left, lines...)
 }
 
@@ -766,3 +777,20 @@ func prettifyToolName(name string) string {
 		return name
 	}
 }
+
+// escapeContent replaces control characters with their Unicode Control Picture
+// representations to ensure they are displayed correctly in the UI.
+func escapeContent(content string) string {
+	var sb strings.Builder
+	for _, r := range content {
+		switch {
+		case r >= 0 && r <= 0x1f: // Control characters 0x00-0x1F
+			sb.WriteRune('\u2400' + r)
+		case r == ansi.DEL:
+			sb.WriteRune('\u2421')
+		default:
+			sb.WriteRune(r)
+		}
+	}
+	return sb.String()
+}

internal/tui/components/completions/completions.go 🔗

@@ -40,6 +40,8 @@ type Completions interface {
 	Query() string // Returns the current filter query
 	KeyMap() KeyMap
 	Position() (int, int) // Returns the X and Y position of the completions popup
+	Width() int
+	Height() int
 }
 
 type completionsCmp struct {
@@ -54,6 +56,8 @@ type completionsCmp struct {
 	query string // The current filter query
 }
 
+const maxCompletionsWidth = 80 // Maximum width for the completions popup
+
 func New() Completions {
 	completionsKeyMap := DefaultKeyMap()
 	keyMap := list.DefaultKeyMap()
@@ -92,7 +96,7 @@ func (c *completionsCmp) Init() tea.Cmd {
 func (c *completionsCmp) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
 	switch msg := msg.(type) {
 	case tea.WindowSizeMsg:
-		c.width = min(msg.Width-c.x, 80)
+		c.width = min(msg.Width-c.x, maxCompletionsWidth)
 		c.height = min(msg.Height-c.y, 15)
 		return c, nil
 	case tea.KeyPressMsg:
@@ -118,10 +122,7 @@ func (c *completionsCmp) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
 				Value: selectedItem,
 			})
 		case key.Matches(msg, c.keyMap.Cancel):
-			if c.open {
-				c.open = false
-				return c, util.CmdHandler(CompletionsClosedMsg{})
-			}
+			return c, util.CmdHandler(CloseCompletionsMsg{})
 		}
 	case CloseCompletionsMsg:
 		c.open = false
@@ -197,3 +198,11 @@ func (c *completionsCmp) KeyMap() KeyMap {
 func (c *completionsCmp) Position() (int, int) {
 	return c.x, c.y - c.height
 }
+
+func (c *completionsCmp) Width() int {
+	return c.width
+}
+
+func (c *completionsCmp) Height() int {
+	return c.height
+}

internal/tui/components/dialogs/permissions/permissions.go 🔗

@@ -323,18 +323,14 @@ func (p *permissionDialogCmp) generateBashContent() string {
 		content := pr.Command
 		t := styles.CurrentTheme()
 		content = strings.TrimSpace(content)
-		content = "\n" + content + "\n"
 		lines := strings.Split(content, "\n")
 
 		width := p.width - 4
 		var out []string
 		for _, ln := range lines {
-			ln = " " + ln // left padding
-			if len(ln) > width {
-				ln = ansi.Truncate(ln, width, "…")
-			}
 			out = append(out, t.S().Muted.
 				Width(width).
+				Padding(0, 3).
 				Foreground(t.FgBase).
 				Background(t.BgSubtle).
 				Render(ln))
@@ -344,6 +340,7 @@ func (p *permissionDialogCmp) generateBashContent() string {
 		renderedContent := strings.Join(out, "\n")
 		finalContent := baseStyle.
 			Width(p.contentViewPort.Width()).
+			Padding(1, 0).
 			Render(renderedContent)
 
 		return finalContent
@@ -413,13 +410,26 @@ func (p *permissionDialogCmp) generateDefaultContent() string {
 
 	content := p.permission.Description
 
-	// Use the cache for markdown rendering
-	renderedContent := p.GetOrSetMarkdown(p.permission.ID, func() (string, error) {
-		r := styles.GetMarkdownRenderer(p.width - 4)
-		s, err := r.Render(content)
-		return s, err
-	})
+	content = strings.TrimSpace(content)
+	content = "\n" + content + "\n"
+	lines := strings.Split(content, "\n")
+
+	width := p.width - 4
+	var out []string
+	for _, ln := range lines {
+		ln = " " + ln // left padding
+		if len(ln) > width {
+			ln = ansi.Truncate(ln, width, "…")
+		}
+		out = append(out, t.S().Muted.
+			Width(width).
+			Foreground(t.FgBase).
+			Background(t.BgSubtle).
+			Render(ln))
+	}
 
+	// Use the cache for markdown rendering
+	renderedContent := strings.Join(out, "\n")
 	finalContent := baseStyle.
 		Width(p.contentViewPort.Width()).
 		Render(renderedContent)
@@ -459,7 +469,11 @@ func (p *permissionDialogCmp) render() string {
 	contentFinal := p.getOrGenerateContent()
 
 	// Always set viewport content (the caching is handled in getOrGenerateContent)
-	contentHeight := min(p.height-9, lipgloss.Height(contentFinal))
+	const minContentHeight = 9
+	contentHeight := min(
+		max(minContentHeight, p.height-minContentHeight),
+		lipgloss.Height(contentFinal),
+	)
 	p.contentViewPort.SetHeight(contentHeight)
 	p.contentViewPort.SetContent(contentFinal)
 
@@ -529,6 +543,9 @@ func (p *permissionDialogCmp) SetSize() tea.Cmd {
 	// Default to diff split mode when dialog is wide enough.
 	p.defaultDiffSplitMode = p.width >= 140
 
+	// Set a maximum width for the dialog
+	p.width = min(p.width, 180)
+
 	// Mark content as dirty if size changed
 	if oldWidth != p.width || oldHeight != p.height {
 		p.contentDirty = true

internal/tui/page/chat/chat.go 🔗

@@ -275,7 +275,7 @@ func (p *chatPage) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
 				return p, p.cancel()
 			}
 		case key.Matches(msg, p.keyMap.Details):
-			p.showDetails()
+			p.toggleDetails()
 			return p, nil
 		}
 
@@ -382,7 +382,7 @@ func (p *chatPage) View() string {
 			Width(p.detailsWidth).
 			Border(lipgloss.RoundedBorder()).
 			BorderForeground(t.BorderFocus)
-		version := t.S().Subtle.Width(p.detailsWidth - 2).AlignHorizontal(lipgloss.Right).Render(version.Version)
+		version := t.S().Base.Foreground(t.Border).Width(p.detailsWidth - 4).AlignHorizontal(lipgloss.Right).Render(version.Version)
 		details := style.Render(
 			lipgloss.JoinVertical(
 				lipgloss.Left,
@@ -446,12 +446,9 @@ func (p *chatPage) setCompactMode(compact bool) {
 	}
 	p.compact = compact
 	if compact {
-		p.compact = true
 		p.sidebar.SetCompactMode(true)
 	} else {
-		p.compact = false
-		p.showingDetails = false
-		p.sidebar.SetCompactMode(false)
+		p.setShowDetails(false)
 	}
 }
 
@@ -558,12 +555,19 @@ func (p *chatPage) cancel() tea.Cmd {
 	return cancelTimerCmd()
 }
 
-func (p *chatPage) showDetails() {
+func (p *chatPage) setShowDetails(show bool) {
+	p.showingDetails = show
+	p.header.SetDetailsOpen(p.showingDetails)
+	if !p.compact {
+		p.sidebar.SetCompactMode(false)
+	}
+}
+
+func (p *chatPage) toggleDetails() {
 	if p.session.ID == "" || !p.compact {
 		return
 	}
-	p.showingDetails = !p.showingDetails
-	p.header.SetDetailsOpen(p.showingDetails)
+	p.setShowDetails(!p.showingDetails)
 }
 
 func (p *chatPage) sendMessage(text string, attachments []message.Attachment) tea.Cmd {

internal/tui/tui.go 🔗

@@ -116,6 +116,16 @@ func (a *appModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
 	case completions.OpenCompletionsMsg, completions.FilterCompletionsMsg, completions.CloseCompletionsMsg:
 		u, completionCmd := a.completions.Update(msg)
 		a.completions = u.(completions.Completions)
+		switch msg := msg.(type) {
+		case completions.OpenCompletionsMsg:
+			x, _ := a.completions.Position()
+			if a.completions.Width()+x >= a.wWidth {
+				// Adjust X position to fit in the window.
+				msg.X = a.wWidth - a.completions.Width() - 1
+				u, completionCmd = a.completions.Update(msg)
+				a.completions = u.(completions.Completions)
+			}
+		}
 		return a, completionCmd
 
 	// Dialog messages

vendor/github.com/aymanbagabas/go-udiff/myers/diff.go 🔗

@@ -1,246 +0,0 @@
-// Copyright 2019 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-// Package myers implements the Myers diff algorithm.
-package myers
-
-import (
-	"strings"
-
-	diff "github.com/aymanbagabas/go-udiff"
-)
-
-// Sources:
-// https://blog.jcoglan.com/2017/02/17/the-myers-diff-algorithm-part-3/
-// https://www.codeproject.com/Articles/42279/%2FArticles%2F42279%2FInvestigating-Myers-diff-algorithm-Part-1-of-2
-
-// ComputeEdits returns the diffs of two strings using a simple
-// line-based implementation, like [diff.Strings].
-//
-// Deprecated: this implementation is moribund. However, when diffs
-// appear in marker test expectations, they are the particular diffs
-// produced by this implementation. The marker test framework
-// asserts diff(orig, got)==wantDiff, but ideally it would compute
-// got==apply(orig, wantDiff) so that the notation of the diff
-// is immaterial.
-func ComputeEdits(before, after string) []diff.Edit {
-	beforeLines := splitLines(before)
-	ops := operations(beforeLines, splitLines(after))
-
-	// Build a table mapping line number to offset.
-	lineOffsets := make([]int, 0, len(beforeLines)+1)
-	total := 0
-	for i := range beforeLines {
-		lineOffsets = append(lineOffsets, total)
-		total += len(beforeLines[i])
-	}
-	lineOffsets = append(lineOffsets, total) // EOF
-
-	edits := make([]diff.Edit, 0, len(ops))
-	for _, op := range ops {
-		start, end := lineOffsets[op.I1], lineOffsets[op.I2]
-		switch op.Kind {
-		case opDelete:
-			// Delete: before[I1:I2] is deleted.
-			edits = append(edits, diff.Edit{Start: start, End: end})
-		case opInsert:
-			// Insert: after[J1:J2] is inserted at before[I1:I1].
-			if content := strings.Join(op.Content, ""); content != "" {
-				edits = append(edits, diff.Edit{Start: start, End: end, New: content})
-			}
-		}
-	}
-	return edits
-}
-
-// opKind is used to denote the type of operation a line represents.
-type opKind int
-
-const (
-	opDelete opKind = iota // line deleted from input (-)
-	opInsert               // line inserted into output (+)
-	opEqual                // line present in input and output
-)
-
-func (kind opKind) String() string {
-	switch kind {
-	case opDelete:
-		return "delete"
-	case opInsert:
-		return "insert"
-	case opEqual:
-		return "equal"
-	default:
-		panic("unknown opKind")
-	}
-}
-
-type operation struct {
-	Kind    opKind
-	Content []string // content from b
-	I1, I2  int      // indices of the line in a
-	J1      int      // indices of the line in b, J2 implied by len(Content)
-}
-
-// operations returns the list of operations to convert a into b, consolidating
-// operations for multiple lines and not including equal lines.
-func operations(a, b []string) []*operation {
-	if len(a) == 0 && len(b) == 0 {
-		return nil
-	}
-
-	trace, offset := shortestEditSequence(a, b)
-	snakes := backtrack(trace, len(a), len(b), offset)
-
-	M, N := len(a), len(b)
-
-	var i int
-	solution := make([]*operation, len(a)+len(b))
-
-	add := func(op *operation, i2, j2 int) {
-		if op == nil {
-			return
-		}
-		op.I2 = i2
-		if op.Kind == opInsert {
-			op.Content = b[op.J1:j2]
-		}
-		solution[i] = op
-		i++
-	}
-	x, y := 0, 0
-	for _, snake := range snakes {
-		if len(snake) < 2 {
-			continue
-		}
-		var op *operation
-		// delete (horizontal)
-		for snake[0]-snake[1] > x-y {
-			if op == nil {
-				op = &operation{
-					Kind: opDelete,
-					I1:   x,
-					J1:   y,
-				}
-			}
-			x++
-			if x == M {
-				break
-			}
-		}
-		add(op, x, y)
-		op = nil
-		// insert (vertical)
-		for snake[0]-snake[1] < x-y {
-			if op == nil {
-				op = &operation{
-					Kind: opInsert,
-					I1:   x,
-					J1:   y,
-				}
-			}
-			y++
-		}
-		add(op, x, y)
-		op = nil
-		// equal (diagonal)
-		for x < snake[0] {
-			x++
-			y++
-		}
-		if x >= M && y >= N {
-			break
-		}
-	}
-	return solution[:i]
-}
-
-// backtrack uses the trace for the edit sequence computation and returns the
-// "snakes" that make up the solution. A "snake" is a single deletion or
-// insertion followed by zero or diagonals.
-func backtrack(trace [][]int, x, y, offset int) [][]int {
-	snakes := make([][]int, len(trace))
-	d := len(trace) - 1
-	for ; x > 0 && y > 0 && d > 0; d-- {
-		V := trace[d]
-		if len(V) == 0 {
-			continue
-		}
-		snakes[d] = []int{x, y}
-
-		k := x - y
-
-		var kPrev int
-		if k == -d || (k != d && V[k-1+offset] < V[k+1+offset]) {
-			kPrev = k + 1
-		} else {
-			kPrev = k - 1
-		}
-
-		x = V[kPrev+offset]
-		y = x - kPrev
-	}
-	if x < 0 || y < 0 {
-		return snakes
-	}
-	snakes[d] = []int{x, y}
-	return snakes
-}
-
-// shortestEditSequence returns the shortest edit sequence that converts a into b.
-func shortestEditSequence(a, b []string) ([][]int, int) {
-	M, N := len(a), len(b)
-	V := make([]int, 2*(N+M)+1)
-	offset := N + M
-	trace := make([][]int, N+M+1)
-
-	// Iterate through the maximum possible length of the SES (N+M).
-	for d := 0; d <= N+M; d++ {
-		copyV := make([]int, len(V))
-		// k lines are represented by the equation y = x - k. We move in
-		// increments of 2 because end points for even d are on even k lines.
-		for k := -d; k <= d; k += 2 {
-			// At each point, we either go down or to the right. We go down if
-			// k == -d, and we go to the right if k == d. We also prioritize
-			// the maximum x value, because we prefer deletions to insertions.
-			var x int
-			if k == -d || (k != d && V[k-1+offset] < V[k+1+offset]) {
-				x = V[k+1+offset] // down
-			} else {
-				x = V[k-1+offset] + 1 // right
-			}
-
-			y := x - k
-
-			// Diagonal moves while we have equal contents.
-			for x < M && y < N && a[x] == b[y] {
-				x++
-				y++
-			}
-
-			V[k+offset] = x
-
-			// Return if we've exceeded the maximum values.
-			if x == M && y == N {
-				// Makes sure to save the state of the array before returning.
-				copy(copyV, V)
-				trace[d] = copyV
-				return trace, offset
-			}
-		}
-
-		// Save the state of the array.
-		copy(copyV, V)
-		trace[d] = copyV
-	}
-	return nil, 0
-}
-
-func splitLines(text string) []string {
-	lines := strings.SplitAfter(text, "\n")
-	if lines[len(lines)-1] == "" {
-		lines = lines[:len(lines)-1]
-	}
-	return lines
-}

vendor/github.com/charmbracelet/bubbletea/v2/tea.go 🔗

@@ -320,12 +320,10 @@ type Program struct {
 
 	// where to read inputs from, this will usually be os.Stdin.
 	input io.Reader
-	mu    sync.Mutex
 	// ttyInput is null if input is not a TTY.
 	ttyInput              term.File
 	previousTtyInputState *term.State
 	inputReader           *uv.TerminalReader
-	traceInput            bool // true if input should be traced
 	readLoopDone          chan struct{}
 	mouseMode             bool // indicates whether we should enable mouse on Windows
 

vendor/github.com/charmbracelet/lipgloss/v2/canvas.go 🔗

@@ -1,6 +1,7 @@
 package lipgloss
 
 import (
+	"fmt"
 	"image"
 	"sort"
 
@@ -94,17 +95,14 @@ type Layer struct {
 	zIndex   int
 	children []*Layer
 	id       string
-	content  string
+	content  uv.Drawable
 }
 
 // NewLayer creates a new Layer with the given content. It calculates the size
 // based on the widest line and the number of lines in the content.
-func NewLayer(content string) (l *Layer) {
+func NewLayer(content any) (l *Layer) {
 	l = new(Layer)
-	l.content = content
-	height := Height(content)
-	width := Width(content)
-	l.rect = image.Rect(0, 0, width, height)
+	l.SetContent(content)
 	return l
 }
 
@@ -216,23 +214,56 @@ func (l *Layer) AddLayers(layers ...*Layer) *Layer {
 }
 
 // SetContent sets the content of the Layer.
-func (l *Layer) SetContent(content string) *Layer {
-	l.content = content
+func (l *Layer) SetContent(content any) *Layer {
+	var drawable uv.Drawable
+	var rect image.Rectangle
+	switch c := content.(type) {
+	case uv.Drawable:
+		drawable = c
+		switch c := c.(type) {
+		case interface{ Bounds() image.Rectangle }:
+			rect.Max.X = c.Bounds().Dx()
+			rect.Max.Y = c.Bounds().Dy()
+		case interface {
+			Width() int
+			Height() int
+		}:
+			rect.Max.X = c.Width()
+			rect.Max.Y = c.Height()
+		}
+	case fmt.Stringer:
+		s := c.String()
+		drawable = uv.NewStyledString(s)
+		rect = image.Rect(0, 0, Width(s), Height(s))
+	case string:
+		drawable = uv.NewStyledString(c)
+		rect = image.Rect(0, 0, Width(c), Height(c))
+	default:
+		s := fmt.Sprint(content)
+		drawable = uv.NewStyledString(s)
+		rect = image.Rect(0, 0, Width(s), Height(s))
+	}
+	l.content = drawable
+	l.rect = rect
 	return l
 }
 
 // Content returns the content of the Layer.
-func (l *Layer) Content() string {
+func (l *Layer) Content() any {
 	return l.content
 }
 
 // Draw draws the Layer onto the given screen buffer.
-func (l *Layer) Draw(scr uv.Screen, _ image.Rectangle) {
-	ss := uv.NewStyledString(l.content)
-	ss.Draw(scr, l.Bounds())
+func (l *Layer) Draw(scr uv.Screen, area image.Rectangle) {
+	if l.content == nil {
+		return
+	}
+	l.content.Draw(scr, area.Intersect(l.Bounds()))
 	for _, child := range l.children {
-		ss := uv.NewStyledString(child.content)
-		ss.Draw(scr, child.Bounds())
+		if child.content == nil {
+			continue
+		}
+		child.content.Draw(scr, area.Intersect(child.Bounds()))
 	}
 }
 

vendor/github.com/charmbracelet/ultraviolet/README.md 🔗

@@ -1,5 +1,7 @@
 # Ultraviolet
 
+<img width="400" alt="Charm Ultraviolet" src="https://github.com/user-attachments/assets/3484e4b0-3741-4e8c-bebf-9ea51f5bb49c" />
+
 <p>
     <a href="https://github.com/charmbracelet/ultraviolet/releases"><img src="https://img.shields.io/github/release/charmbracelet/ultraviolet.svg" alt="Latest Release"></a>
     <a href="https://pkg.go.dev/github.com/charmbracelet/ultraviolet?tab=doc"><img src="https://godoc.org/github.com/charmbracelet/ultraviolet?status.svg" alt="GoDoc"></a>

vendor/github.com/charmbracelet/ultraviolet/input.go 🔗

@@ -3,6 +3,7 @@ package uv
 import (
 	"context"
 
+	"github.com/charmbracelet/x/term"
 	"golang.org/x/sync/errgroup"
 )
 
@@ -59,3 +60,30 @@ func (im *InputManager) ReceiveEvents(ctx context.Context, events chan<- Event)
 	// Wait for all receivers to finish
 	return errg.Wait()
 }
+
+// InitialSizeReceiver query the initial size of the terminal window and sends
+// it to the given event channel.
+type InitialSizeReceiver struct {
+	File term.File
+}
+
+// ReceiveEvents queries the initial size of the terminal window and sends it
+// to the given event channel. It stops when event is sent, the context is done
+// or an error occurs.
+func (r *InitialSizeReceiver) ReceiveEvents(ctx context.Context, events chan<- Event) error {
+	if r.File == nil {
+		return nil // No file set, nothing to do.
+	}
+
+	w, h, err := term.GetSize(r.File.Fd())
+	if err != nil {
+		return err
+	}
+
+	select {
+	case <-ctx.Done():
+		return ctx.Err()
+	case events <- WindowSizeEvent{Width: w, Height: h}:
+		return nil
+	}
+}

vendor/github.com/charmbracelet/ultraviolet/terminal.go 🔗

@@ -20,6 +20,8 @@ import (
 	"github.com/muesli/cancelreader"
 )
 
+const isWindows = "windows"
+
 var (
 	// ErrNotTerminal is returned when one of the I/O streams is not a terminal.
 	ErrNotTerminal = fmt.Errorf("not a terminal")
@@ -42,8 +44,7 @@ type Terminal struct {
 	inTtyState  *term.State
 	outTty      term.File
 	outTtyState *term.State
-	winchTty    term.File // The terminal to receive window size changes from.
-	started     bool      // Indicates if the terminal has been started.
+	started     bool // Indicates if the terminal has been started.
 
 	// Terminal type, screen and buffer.
 	termtype            string            // The $TERM type.
@@ -123,7 +124,7 @@ func NewTerminal(in io.Reader, out io.Writer, env []string) *Terminal {
 	// Handle debugging I/O.
 	debug, ok := os.LookupEnv("UV_DEBUG")
 	if ok && len(debug) > 0 {
-		f, err := os.OpenFile(debug, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0o666)
+		f, err := os.OpenFile(debug, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0o600)
 		if err != nil {
 			panic("failed to open debug file: " + err.Error())
 		}
@@ -346,7 +347,7 @@ func (t *Terminal) EnableMode(modes ...ansi.Mode) {
 	for _, m := range modes {
 		t.modes[m] = ansi.ModeSet
 	}
-	t.scr.WriteString(ansi.SetMode(modes...)) //nolint:errcheck
+	t.scr.WriteString(ansi.SetMode(modes...)) //nolint:errcheck,gosec
 }
 
 // DisableMode disables the given modes on the terminal. This is typically
@@ -362,7 +363,7 @@ func (t *Terminal) DisableMode(modes ...ansi.Mode) {
 	for _, m := range modes {
 		t.modes[m] = ansi.ModeReset
 	}
-	t.scr.WriteString(ansi.ResetMode(modes...)) //nolint:errcheck
+	t.scr.WriteString(ansi.ResetMode(modes...)) //nolint:errcheck,gosec
 }
 
 // RequestMode requests the current state of the given modes from the terminal.
@@ -372,7 +373,7 @@ func (t *Terminal) DisableMode(modes ...ansi.Mode) {
 // Note that this won't take any effect until the next [Terminal.Display] or
 // [Terminal.Flush] call.
 func (t *Terminal) RequestMode(mode ansi.Mode) {
-	t.scr.WriteString(ansi.RequestMode(mode)) //nolint:errcheck
+	t.scr.WriteString(ansi.RequestMode(mode)) //nolint:errcheck,gosec
 }
 
 // MouseMode represents the mouse mode for the terminal. It is used to enable
@@ -399,7 +400,7 @@ func (t *Terminal) SetForegroundColor(c color.Color) {
 	t.setFg = c
 	col, ok := colorful.MakeColor(c)
 	if ok {
-		t.scr.WriteString(ansi.SetForegroundColor(col.Hex())) //nolint:errcheck
+		t.scr.WriteString(ansi.SetForegroundColor(col.Hex())) //nolint:errcheck,gosec
 	}
 }
 
@@ -408,7 +409,7 @@ func (t *Terminal) SetForegroundColor(c color.Color) {
 // Note that this won't take any effect until the next [Terminal.Display] or
 // [Terminal.Flush] call.
 func (t *Terminal) RequestForegroundColor() {
-	t.scr.WriteString(ansi.RequestForegroundColor) //nolint:errcheck
+	t.scr.WriteString(ansi.RequestForegroundColor) //nolint:errcheck,gosec
 }
 
 // ResetForegroundColor resets the terminal foreground color to the
@@ -418,7 +419,7 @@ func (t *Terminal) RequestForegroundColor() {
 // [Terminal.Flush] call.
 func (t *Terminal) ResetForegroundColor() {
 	t.setFg = nil
-	t.scr.WriteString(ansi.ResetForegroundColor) //nolint:errcheck
+	t.scr.WriteString(ansi.ResetForegroundColor) //nolint:errcheck,gosec
 }
 
 // SetBackgroundColor sets the terminal default background color.
@@ -429,7 +430,7 @@ func (t *Terminal) SetBackgroundColor(c color.Color) {
 	t.setBg = c
 	col, ok := colorful.MakeColor(c)
 	if ok {
-		t.scr.WriteString(ansi.SetBackgroundColor(col.Hex())) //nolint:errcheck
+		t.scr.WriteString(ansi.SetBackgroundColor(col.Hex())) //nolint:errcheck,gosec
 	}
 }
 
@@ -438,7 +439,7 @@ func (t *Terminal) SetBackgroundColor(c color.Color) {
 // Note that this won't take any effect until the next [Terminal.Display] or
 // [Terminal.Flush] call.
 func (t *Terminal) RequestBackgroundColor() {
-	t.scr.WriteString(ansi.RequestBackgroundColor) //nolint:errcheck
+	t.scr.WriteString(ansi.RequestBackgroundColor) //nolint:errcheck,gosec
 }
 
 // ResetBackgroundColor resets the terminal background color to the
@@ -448,7 +449,7 @@ func (t *Terminal) RequestBackgroundColor() {
 // [Terminal.Flush] call.
 func (t *Terminal) ResetBackgroundColor() {
 	t.setBg = nil
-	t.scr.WriteString(ansi.ResetBackgroundColor) //nolint:errcheck
+	t.scr.WriteString(ansi.ResetBackgroundColor) //nolint:errcheck,gosec
 }
 
 // SetCursorColor sets the terminal cursor color.
@@ -459,7 +460,7 @@ func (t *Terminal) SetCursorColor(c color.Color) {
 	t.setCc = c
 	col, ok := colorful.MakeColor(c)
 	if ok {
-		t.scr.WriteString(ansi.SetCursorColor(col.Hex())) //nolint:errcheck
+		t.scr.WriteString(ansi.SetCursorColor(col.Hex())) //nolint:errcheck,gosec
 	}
 }
 
@@ -468,7 +469,7 @@ func (t *Terminal) SetCursorColor(c color.Color) {
 // Note that this won't take any effect until the next [Terminal.Display] or
 // [Terminal.Flush] call.
 func (t *Terminal) RequestCursorColor() {
-	t.scr.WriteString(ansi.RequestCursorColor) //nolint:errcheck
+	t.scr.WriteString(ansi.RequestCursorColor) //nolint:errcheck,gosec
 }
 
 // ResetCursorColor resets the terminal cursor color to the
@@ -478,7 +479,7 @@ func (t *Terminal) RequestCursorColor() {
 // [Terminal.Flush] call.
 func (t *Terminal) ResetCursorColor() {
 	t.setCc = nil
-	t.scr.WriteString(ansi.ResetCursorColor) //nolint:errcheck
+	t.scr.WriteString(ansi.ResetCursorColor) //nolint:errcheck,gosec
 }
 
 // SetCursorShape sets the terminal cursor shape and blinking style.
@@ -488,7 +489,7 @@ func (t *Terminal) ResetCursorColor() {
 func (t *Terminal) SetCursorShape(shape CursorShape, blink bool) {
 	style := shape.Encode(blink)
 	t.curStyle = style
-	t.scr.WriteString(ansi.SetCursorStyle(style)) //nolint:errcheck
+	t.scr.WriteString(ansi.SetCursorStyle(style)) //nolint:errcheck,gosec
 }
 
 // EnableMouse enables mouse support on the terminal.
@@ -520,7 +521,7 @@ func (t *Terminal) EnableMouse(modes ...MouseMode) {
 		mode = ButtonMouseMode | DragMouseMode | AllMouseMode
 	}
 	t.mouseMode = mode
-	if runtime.GOOS != "windows" {
+	if runtime.GOOS != isWindows {
 		modes := []ansi.Mode{}
 		if t.mouseMode&AllMouseMode != 0 {
 			modes = append(modes, ansi.AnyEventMouseMode)
@@ -532,7 +533,7 @@ func (t *Terminal) EnableMouse(modes ...MouseMode) {
 		modes = append(modes, ansi.SgrExtMouseMode)
 		t.EnableMode(modes...)
 	}
-	t.enableWindowsMouse() //nolint:errcheck
+	t.enableWindowsMouse() //nolint:errcheck,gosec
 }
 
 // DisableMouse disables mouse support on the terminal. This will disable mouse
@@ -542,7 +543,7 @@ func (t *Terminal) EnableMouse(modes ...MouseMode) {
 // [Terminal.Display] or [Terminal.Flush] call.
 func (t *Terminal) DisableMouse() {
 	t.mouseMode = 0
-	if runtime.GOOS != "windows" {
+	if runtime.GOOS != isWindows {
 		var modes []ansi.Mode
 		if t.modes.Get(ansi.AnyEventMouseMode).IsSet() {
 			modes = append(modes, ansi.AnyEventMouseMode)
@@ -558,7 +559,7 @@ func (t *Terminal) DisableMouse() {
 		}
 		t.DisableMode(modes...)
 	}
-	t.disableWindowsMouse() //nolint:errcheck
+	t.disableWindowsMouse() //nolint:errcheck,gosec
 }
 
 // EnableBracketedPaste enables bracketed paste mode on the terminal. This is
@@ -608,9 +609,9 @@ func (t *Terminal) enterAltScreen(cursor bool) {
 	t.scr.EnterAltScreen()
 	if cursor && !altscreen {
 		if t.scr.CursorHidden() {
-			t.scr.WriteString(ansi.HideCursor) //nolint:errcheck
+			t.scr.WriteString(ansi.HideCursor) //nolint:errcheck,gosec
 		} else {
-			t.scr.WriteString(ansi.ShowCursor) //nolint:errcheck
+			t.scr.WriteString(ansi.ShowCursor) //nolint:errcheck,gosec
 		}
 	}
 	t.scr.SetRelativeCursor(false)
@@ -639,9 +640,9 @@ func (t *Terminal) exitAltScreen(cursor bool) {
 	t.scr.ExitAltScreen()
 	if cursor && altscreen {
 		if t.scr.CursorHidden() {
-			t.scr.WriteString(ansi.HideCursor) //nolint:errcheck
+			t.scr.WriteString(ansi.HideCursor) //nolint:errcheck,gosec
 		} else {
-			t.scr.WriteString(ansi.ShowCursor) //nolint:errcheck
+			t.scr.WriteString(ansi.ShowCursor) //nolint:errcheck,gosec
 		}
 	}
 	t.scr.SetRelativeCursor(true)
@@ -725,11 +726,6 @@ func (t *Terminal) Start() error {
 		return ErrNotTerminal
 	}
 
-	t.winchTty = t.inTty
-	if t.winchTty == nil {
-		t.winchTty = t.outTty
-	}
-
 	// Get the initial terminal size.
 	var err error
 	t.size.Width, t.size.Height, err = t.GetSize()
@@ -749,11 +745,11 @@ func (t *Terminal) Start() error {
 	t.configureRenderer()
 
 	if t.modes.Get(ansi.AltScreenSaveCursorMode).IsSet() {
-		t.enterAltScreen(false) //nolint:errcheck
+		t.enterAltScreen(false)
 	}
 	if t.cursorHidden == t.modes.Get(ansi.TextCursorEnableMode).IsReset() {
 		// We always hide the cursor when we start.
-		t.hideCursor() //nolint:errcheck
+		t.hideCursor()
 	}
 	// Restore terminal modes.
 	for m, s := range t.modes {
@@ -763,7 +759,7 @@ func (t *Terminal) Start() error {
 			continue
 		default:
 			if s.IsSet() {
-				t.scr.WriteString(ansi.SetMode(m)) //nolint:errcheck
+				t.scr.WriteString(ansi.SetMode(m)) //nolint:errcheck,gosec
 			}
 		}
 	}
@@ -779,21 +775,32 @@ func (t *Terminal) Start() error {
 		if c.colorp != nil && *c.colorp != nil {
 			col, ok := colorful.MakeColor(*c.colorp)
 			if ok {
-				t.scr.WriteString(c.setter(col.Hex())) //nolint:errcheck
+				t.scr.WriteString(c.setter(col.Hex())) //nolint:errcheck,gosec
 			}
 		}
 	}
 	if t.curStyle > 1 {
-		t.scr.WriteString(ansi.SetCursorStyle(t.curStyle)) //nolint:errcheck
+		t.scr.WriteString(ansi.SetCursorStyle(t.curStyle)) //nolint:errcheck,gosec
 	}
 
 	if err := t.rd.Start(); err != nil {
 		return fmt.Errorf("error starting terminal: %w", err)
 	}
 
-	recvs := []InputReceiver{t.rd}
-	if runtime.GOOS != "windows" {
-		t.wrdr = &WinChReceiver{t.winchTty}
+	var winchTty term.File
+	if runtime.GOOS == isWindows {
+		// On Windows, we need to use the console output buffer to get the
+		// window size.
+		winchTty = t.outTty
+	} else {
+		winchTty = t.inTty
+		if winchTty == nil {
+			winchTty = t.outTty
+		}
+	}
+	recvs := []InputReceiver{t.rd, &InitialSizeReceiver{winchTty}}
+	if runtime.GOOS != isWindows {
+		t.wrdr = &WinChReceiver{winchTty}
 		if err := t.wrdr.Start(); err != nil {
 			return fmt.Errorf("error starting window size receiver: %w", err)
 		}
@@ -818,13 +825,13 @@ func (t *Terminal) Start() error {
 func (t *Terminal) Restore() error {
 	if t.inTtyState != nil {
 		if err := term.Restore(t.inTty.Fd(), t.inTtyState); err != nil {
-			return err
+			return fmt.Errorf("error restoring input terminal state: %w", err)
 		}
 		t.inTtyState = nil
 	}
 	if t.outTtyState != nil {
 		if err := term.Restore(t.outTty.Fd(), t.outTtyState); err != nil {
-			return err
+			return fmt.Errorf("error restoring output terminal state: %w", err)
 		}
 		t.outTtyState = nil
 	}
@@ -835,10 +842,10 @@ func (t *Terminal) Restore() error {
 		t.scr.MoveTo(0, t.buf.Height()-1)
 	}
 	if altscreen {
-		t.exitAltScreen(false) //nolint:errcheck
+		t.exitAltScreen(false)
 	}
 	if cursorHidden {
-		t.showCursor() //nolint:errcheck
+		t.showCursor()
 		// Override the cursor hidden state so that we can auto hide it again
 		// when the terminal resumes using [Terminal.Start].
 		t.cursorHidden = false

vendor/github.com/charmbracelet/ultraviolet/terminal_reader.go 🔗

@@ -259,17 +259,22 @@ func (d *TerminalReader) run() {
 		// - "\x1b^" (alt+^ key press)
 		esc := n > 0 && n <= 2 && readBuf[0] == ansi.ESC
 		if esc {
-			d.esc.Store(true)
-			d.timeout.Reset(d.EscTimeout)
+			d.resetEsc()
 		}
 
 		d.notify <- readBuf[:n]
 	}
 }
 
+func (d *TerminalReader) resetEsc() {
+	// Reset the escape sequence state and timer.
+	d.esc.Store(true)
+	d.timeout.Reset(d.EscTimeout)
+}
+
 func (d *TerminalReader) sendEvents(events chan<- Event) {
 	// Lookup table first
-	if d.lookup && d.timedout.Load() && len(d.buf) > 0 && d.buf[0] == ansi.ESC {
+	if d.lookup && d.timedout.Load() && len(d.buf) > 2 && d.buf[0] == ansi.ESC {
 		if k, ok := d.table[string(d.buf)]; ok {
 			events <- KeyPressEvent(k)
 			d.buf = d.buf[:0]
@@ -309,8 +314,7 @@ LOOP:
 					if slices.Contains([]byte{
 						ansi.ESC, ansi.CSI, ansi.OSC, ansi.DCS, ansi.APC, ansi.SOS, ansi.PM,
 					}, d.buf[0]) {
-						d.esc.Store(true)
-						d.timeout.Reset(d.EscTimeout)
+						d.resetEsc()
 					}
 				}
 				// If this is the entire buffer, we can break and assume this

vendor/github.com/charmbracelet/ultraviolet/winch_unix.go 🔗

@@ -18,6 +18,8 @@ func (l *WinChReceiver) receiveEvents(ctx context.Context, f term.File, evch cha
 	sig := make(chan os.Signal, 1)
 	signal.Notify(sig, syscall.SIGWINCH)
 
+	defer signal.Stop(sig)
+
 	sendWinSize := func(w, h int) {
 		select {
 		case <-ctx.Done():
@@ -32,31 +34,8 @@ func (l *WinChReceiver) receiveEvents(ctx context.Context, f term.File, evch cha
 		}
 	}
 
-	defer signal.Stop(sig)
-
-	// Send the initial window size.
-	winsize, err := termios.GetWinsize(int(f.Fd()))
-	if err != nil {
-		return err
-	}
-
-	var wg sync.WaitGroup
-	wg.Add(1)
-	go func() {
-		defer wg.Done()
-		sendWinSize(int(winsize.Col), int(winsize.Row))
-	}()
-
-	wg.Add(1)
-	go func() {
-		defer wg.Done()
-		sendPixelSize(int(winsize.Xpixel), int(winsize.Ypixel))
-	}()
-
-	// Wait for all goroutines to finish before continuing.
-	wg.Wait()
-
 	// Listen for window size changes.
+	var wg sync.WaitGroup
 	for {
 		select {
 		case <-ctx.Done():

vendor/golang.org/x/sync/errgroup/errgroup.go 🔗

@@ -12,8 +12,6 @@ package errgroup
 import (
 	"context"
 	"fmt"
-	"runtime"
-	"runtime/debug"
 	"sync"
 )
 
@@ -33,10 +31,6 @@ type Group struct {
 
 	errOnce sync.Once
 	err     error
-
-	mu         sync.Mutex
-	panicValue any  // = PanicError | PanicValue; non-nil if some Group.Go goroutine panicked.
-	abnormal   bool // some Group.Go goroutine terminated abnormally (panic or goexit).
 }
 
 func (g *Group) done() {
@@ -56,22 +50,13 @@ func WithContext(ctx context.Context) (*Group, context.Context) {
 	return &Group{cancel: cancel}, ctx
 }
 
-// Wait blocks until all function calls from the Go method have returned
-// normally, then returns the first non-nil error (if any) from them.
-//
-// If any of the calls panics, Wait panics with a [PanicValue];
-// and if any of them calls [runtime.Goexit], Wait calls runtime.Goexit.
+// Wait blocks until all function calls from the Go method have returned, then
+// returns the first non-nil error (if any) from them.
 func (g *Group) Wait() error {
 	g.wg.Wait()
 	if g.cancel != nil {
 		g.cancel(g.err)
 	}
-	if g.panicValue != nil {
-		panic(g.panicValue)
-	}
-	if g.abnormal {
-		runtime.Goexit()
-	}
 	return g.err
 }
 
@@ -81,53 +66,31 @@ func (g *Group) Wait() error {
 // It blocks until the new goroutine can be added without the number of
 // goroutines in the group exceeding the configured limit.
 //
-// The first goroutine in the group that returns a non-nil error, panics, or
-// invokes [runtime.Goexit] will cancel the associated Context, if any.
+// The first goroutine in the group that returns a non-nil error will
+// cancel the associated Context, if any. The error will be returned
+// by Wait.
 func (g *Group) Go(f func() error) {
 	if g.sem != nil {
 		g.sem <- token{}
 	}
 
-	g.add(f)
-}
-
-func (g *Group) add(f func() error) {
 	g.wg.Add(1)
 	go func() {
 		defer g.done()
-		normalReturn := false
-		defer func() {
-			if normalReturn {
-				return
-			}
-			v := recover()
-			g.mu.Lock()
-			defer g.mu.Unlock()
-			if !g.abnormal {
-				if g.cancel != nil {
-					g.cancel(g.err)
-				}
-				g.abnormal = true
-			}
-			if v != nil && g.panicValue == nil {
-				switch v := v.(type) {
-				case error:
-					g.panicValue = PanicError{
-						Recovered: v,
-						Stack:     debug.Stack(),
-					}
-				default:
-					g.panicValue = PanicValue{
-						Recovered: v,
-						Stack:     debug.Stack(),
-					}
-				}
-			}
-		}()
 
-		err := f()
-		normalReturn = true
-		if err != nil {
+		// It is tempting to propagate panics from f()
+		// up to the goroutine that calls Wait, but
+		// it creates more problems than it solves:
+		// - it delays panics arbitrarily,
+		//   making bugs harder to detect;
+		// - it turns f's panic stack into a mere value,
+		//   hiding it from crash-monitoring tools;
+		// - it risks deadlocks that hide the panic entirely,
+		//   if f's panic leaves the program in a state
+		//   that prevents the Wait call from being reached.
+		// See #53757, #74275, #74304, #74306.
+
+		if err := f(); err != nil {
 			g.errOnce.Do(func() {
 				g.err = err
 				if g.cancel != nil {
@@ -152,7 +115,19 @@ func (g *Group) TryGo(f func() error) bool {
 		}
 	}
 
-	g.add(f)
+	g.wg.Add(1)
+	go func() {
+		defer g.done()
+
+		if err := f(); err != nil {
+			g.errOnce.Do(func() {
+				g.err = err
+				if g.cancel != nil {
+					g.cancel(g.err)
+				}
+			})
+		}
+	}()
 	return true
 }
 
@@ -174,34 +149,3 @@ func (g *Group) SetLimit(n int) {
 	}
 	g.sem = make(chan token, n)
 }
-
-// PanicError wraps an error recovered from an unhandled panic
-// when calling a function passed to Go or TryGo.
-type PanicError struct {
-	Recovered error
-	Stack     []byte // result of call to [debug.Stack]
-}
-
-func (p PanicError) Error() string {
-	if len(p.Stack) > 0 {
-		return fmt.Sprintf("recovered from errgroup.Group: %v\n%s", p.Recovered, p.Stack)
-	}
-	return fmt.Sprintf("recovered from errgroup.Group: %v", p.Recovered)
-}
-
-func (p PanicError) Unwrap() error { return p.Recovered }
-
-// PanicValue wraps a value that does not implement the error interface,
-// recovered from an unhandled panic when calling a function passed to Go or
-// TryGo.
-type PanicValue struct {
-	Recovered any
-	Stack     []byte // result of call to [debug.Stack]
-}
-
-func (p PanicValue) String() string {
-	if len(p.Stack) > 0 {
-		return fmt.Sprintf("recovered from errgroup.Group: %v\n%s", p.Recovered, p.Stack)
-	}
-	return fmt.Sprintf("recovered from errgroup.Group: %v", p.Recovered)
-}

vendor/golang.org/x/sys/unix/zerrors_linux.go 🔗

@@ -319,6 +319,7 @@ const (
 	AUDIT_INTEGRITY_POLICY_RULE                 = 0x70f
 	AUDIT_INTEGRITY_RULE                        = 0x70d
 	AUDIT_INTEGRITY_STATUS                      = 0x70a
+	AUDIT_INTEGRITY_USERSPACE                   = 0x710
 	AUDIT_IPC                                   = 0x517
 	AUDIT_IPC_SET_PERM                          = 0x51f
 	AUDIT_IPE_ACCESS                            = 0x58c
@@ -843,9 +844,9 @@ const (
 	DM_UUID_FLAG                                = 0x4000
 	DM_UUID_LEN                                 = 0x81
 	DM_VERSION                                  = 0xc138fd00
-	DM_VERSION_EXTRA                            = "-ioctl (2023-03-01)"
+	DM_VERSION_EXTRA                            = "-ioctl (2025-01-17)"
 	DM_VERSION_MAJOR                            = 0x4
-	DM_VERSION_MINOR                            = 0x30
+	DM_VERSION_MINOR                            = 0x31
 	DM_VERSION_PATCHLEVEL                       = 0x0
 	DT_BLK                                      = 0x6
 	DT_CHR                                      = 0x2
@@ -941,6 +942,8 @@ const (
 	ETHER_FLOW                                  = 0x12
 	ETHTOOL_BUSINFO_LEN                         = 0x20
 	ETHTOOL_EROMVERS_LEN                        = 0x20
+	ETHTOOL_FAMILY_NAME                         = "ethtool"
+	ETHTOOL_FAMILY_VERSION                      = 0x1
 	ETHTOOL_FEC_AUTO                            = 0x2
 	ETHTOOL_FEC_BASER                           = 0x10
 	ETHTOOL_FEC_LLRS                            = 0x20
@@ -1203,6 +1206,9 @@ const (
 	FAN_DENY                                    = 0x2
 	FAN_ENABLE_AUDIT                            = 0x40
 	FAN_EPIDFD                                  = -0x2
+	FAN_ERRNO_BITS                              = 0x8
+	FAN_ERRNO_MASK                              = 0xff
+	FAN_ERRNO_SHIFT                             = 0x18
 	FAN_EVENT_INFO_TYPE_DFID                    = 0x3
 	FAN_EVENT_INFO_TYPE_DFID_NAME               = 0x2
 	FAN_EVENT_INFO_TYPE_ERROR                   = 0x5
@@ -1210,6 +1216,7 @@ const (
 	FAN_EVENT_INFO_TYPE_NEW_DFID_NAME           = 0xc
 	FAN_EVENT_INFO_TYPE_OLD_DFID_NAME           = 0xa
 	FAN_EVENT_INFO_TYPE_PIDFD                   = 0x4
+	FAN_EVENT_INFO_TYPE_RANGE                   = 0x6
 	FAN_EVENT_METADATA_LEN                      = 0x18
 	FAN_EVENT_ON_CHILD                          = 0x8000000
 	FAN_FS_ERROR                                = 0x8000
@@ -1240,6 +1247,7 @@ const (
 	FAN_OPEN_EXEC                               = 0x1000
 	FAN_OPEN_EXEC_PERM                          = 0x40000
 	FAN_OPEN_PERM                               = 0x10000
+	FAN_PRE_ACCESS                              = 0x100000
 	FAN_Q_OVERFLOW                              = 0x4000
 	FAN_RENAME                                  = 0x10000000
 	FAN_REPORT_DFID_NAME                        = 0xc00
@@ -2787,7 +2795,7 @@ const (
 	RTAX_UNSPEC                                 = 0x0
 	RTAX_WINDOW                                 = 0x3
 	RTA_ALIGNTO                                 = 0x4
-	RTA_MAX                                     = 0x1e
+	RTA_MAX                                     = 0x1f
 	RTCF_DIRECTSRC                              = 0x4000000
 	RTCF_DOREDIRECT                             = 0x1000000
 	RTCF_LOG                                    = 0x2000000
@@ -2864,10 +2872,12 @@ const (
 	RTM_DELACTION                               = 0x31
 	RTM_DELADDR                                 = 0x15
 	RTM_DELADDRLABEL                            = 0x49
+	RTM_DELANYCAST                              = 0x3d
 	RTM_DELCHAIN                                = 0x65
 	RTM_DELLINK                                 = 0x11
 	RTM_DELLINKPROP                             = 0x6d
 	RTM_DELMDB                                  = 0x55
+	RTM_DELMULTICAST                            = 0x39
 	RTM_DELNEIGH                                = 0x1d
 	RTM_DELNETCONF                              = 0x51
 	RTM_DELNEXTHOP                              = 0x69
@@ -2917,11 +2927,13 @@ const (
 	RTM_NEWACTION                               = 0x30
 	RTM_NEWADDR                                 = 0x14
 	RTM_NEWADDRLABEL                            = 0x48
+	RTM_NEWANYCAST                              = 0x3c
 	RTM_NEWCACHEREPORT                          = 0x60
 	RTM_NEWCHAIN                                = 0x64
 	RTM_NEWLINK                                 = 0x10
 	RTM_NEWLINKPROP                             = 0x6c
 	RTM_NEWMDB                                  = 0x54
+	RTM_NEWMULTICAST                            = 0x38
 	RTM_NEWNDUSEROPT                            = 0x44
 	RTM_NEWNEIGH                                = 0x1c
 	RTM_NEWNEIGHTBL                             = 0x40
@@ -2987,11 +2999,12 @@ const (
 	RUSAGE_THREAD                               = 0x1
 	RWF_APPEND                                  = 0x10
 	RWF_ATOMIC                                  = 0x40
+	RWF_DONTCACHE                               = 0x80
 	RWF_DSYNC                                   = 0x2
 	RWF_HIPRI                                   = 0x1
 	RWF_NOAPPEND                                = 0x20
 	RWF_NOWAIT                                  = 0x8
-	RWF_SUPPORTED                               = 0x7f
+	RWF_SUPPORTED                               = 0xff
 	RWF_SYNC                                    = 0x4
 	RWF_WRITE_LIFE_NOT_SET                      = 0x0
 	SCHED_BATCH                                 = 0x3
@@ -3271,6 +3284,7 @@ const (
 	STATX_BTIME                                 = 0x800
 	STATX_CTIME                                 = 0x80
 	STATX_DIOALIGN                              = 0x2000
+	STATX_DIO_READ_ALIGN                        = 0x20000
 	STATX_GID                                   = 0x10
 	STATX_INO                                   = 0x100
 	STATX_MNT_ID                                = 0x1000
@@ -3322,7 +3336,7 @@ const (
 	TASKSTATS_GENL_NAME                         = "TASKSTATS"
 	TASKSTATS_GENL_VERSION                      = 0x1
 	TASKSTATS_TYPE_MAX                          = 0x6
-	TASKSTATS_VERSION                           = 0xe
+	TASKSTATS_VERSION                           = 0xf
 	TCIFLUSH                                    = 0x0
 	TCIOFF                                      = 0x2
 	TCIOFLUSH                                   = 0x2
@@ -3503,6 +3517,7 @@ const (
 	TP_STATUS_WRONG_FORMAT                      = 0x4
 	TRACEFS_MAGIC                               = 0x74726163
 	TS_COMM_LEN                                 = 0x20
+	UBI_IOCECNFO                                = 0xc01c6f06
 	UDF_SUPER_MAGIC                             = 0x15013346
 	UDP_CORK                                    = 0x1
 	UDP_ENCAP                                   = 0x64

vendor/golang.org/x/sys/unix/ztypes_linux.go 🔗

@@ -114,7 +114,7 @@ type Statx_t struct {
 	Atomic_write_unit_min     uint32
 	Atomic_write_unit_max     uint32
 	Atomic_write_segments_max uint32
-	_                         [1]uint32
+	Dio_read_offset_align     uint32
 	_                         [9]uint64
 }
 
@@ -2226,8 +2226,11 @@ const (
 	NFT_PAYLOAD_LL_HEADER             = 0x0
 	NFT_PAYLOAD_NETWORK_HEADER        = 0x1
 	NFT_PAYLOAD_TRANSPORT_HEADER      = 0x2
+	NFT_PAYLOAD_INNER_HEADER          = 0x3
+	NFT_PAYLOAD_TUN_HEADER            = 0x4
 	NFT_PAYLOAD_CSUM_NONE             = 0x0
 	NFT_PAYLOAD_CSUM_INET             = 0x1
+	NFT_PAYLOAD_CSUM_SCTP             = 0x2
 	NFT_PAYLOAD_L4CSUM_PSEUDOHDR      = 0x1
 	NFTA_PAYLOAD_UNSPEC               = 0x0
 	NFTA_PAYLOAD_DREG                 = 0x1
@@ -3802,7 +3805,16 @@ const (
 	ETHTOOL_MSG_PSE_GET                       = 0x24
 	ETHTOOL_MSG_PSE_SET                       = 0x25
 	ETHTOOL_MSG_RSS_GET                       = 0x26
-	ETHTOOL_MSG_USER_MAX                      = 0x2d
+	ETHTOOL_MSG_PLCA_GET_CFG                  = 0x27
+	ETHTOOL_MSG_PLCA_SET_CFG                  = 0x28
+	ETHTOOL_MSG_PLCA_GET_STATUS               = 0x29
+	ETHTOOL_MSG_MM_GET                        = 0x2a
+	ETHTOOL_MSG_MM_SET                        = 0x2b
+	ETHTOOL_MSG_MODULE_FW_FLASH_ACT           = 0x2c
+	ETHTOOL_MSG_PHY_GET                       = 0x2d
+	ETHTOOL_MSG_TSCONFIG_GET                  = 0x2e
+	ETHTOOL_MSG_TSCONFIG_SET                  = 0x2f
+	ETHTOOL_MSG_USER_MAX                      = 0x2f
 	ETHTOOL_MSG_KERNEL_NONE                   = 0x0
 	ETHTOOL_MSG_STRSET_GET_REPLY              = 0x1
 	ETHTOOL_MSG_LINKINFO_GET_REPLY            = 0x2
@@ -3842,7 +3854,17 @@ const (
 	ETHTOOL_MSG_MODULE_NTF                    = 0x24
 	ETHTOOL_MSG_PSE_GET_REPLY                 = 0x25
 	ETHTOOL_MSG_RSS_GET_REPLY                 = 0x26
-	ETHTOOL_MSG_KERNEL_MAX                    = 0x2e
+	ETHTOOL_MSG_PLCA_GET_CFG_REPLY            = 0x27
+	ETHTOOL_MSG_PLCA_GET_STATUS_REPLY         = 0x28
+	ETHTOOL_MSG_PLCA_NTF                      = 0x29
+	ETHTOOL_MSG_MM_GET_REPLY                  = 0x2a
+	ETHTOOL_MSG_MM_NTF                        = 0x2b
+	ETHTOOL_MSG_MODULE_FW_FLASH_NTF           = 0x2c
+	ETHTOOL_MSG_PHY_GET_REPLY                 = 0x2d
+	ETHTOOL_MSG_PHY_NTF                       = 0x2e
+	ETHTOOL_MSG_TSCONFIG_GET_REPLY            = 0x2f
+	ETHTOOL_MSG_TSCONFIG_SET_REPLY            = 0x30
+	ETHTOOL_MSG_KERNEL_MAX                    = 0x30
 	ETHTOOL_FLAG_COMPACT_BITSETS              = 0x1
 	ETHTOOL_FLAG_OMIT_REPLY                   = 0x2
 	ETHTOOL_FLAG_STATS                        = 0x4
@@ -3949,7 +3971,12 @@ const (
 	ETHTOOL_A_RINGS_TCP_DATA_SPLIT            = 0xb
 	ETHTOOL_A_RINGS_CQE_SIZE                  = 0xc
 	ETHTOOL_A_RINGS_TX_PUSH                   = 0xd
-	ETHTOOL_A_RINGS_MAX                       = 0x10
+	ETHTOOL_A_RINGS_RX_PUSH                   = 0xe
+	ETHTOOL_A_RINGS_TX_PUSH_BUF_LEN           = 0xf
+	ETHTOOL_A_RINGS_TX_PUSH_BUF_LEN_MAX       = 0x10
+	ETHTOOL_A_RINGS_HDS_THRESH                = 0x11
+	ETHTOOL_A_RINGS_HDS_THRESH_MAX            = 0x12
+	ETHTOOL_A_RINGS_MAX                       = 0x12
 	ETHTOOL_A_CHANNELS_UNSPEC                 = 0x0
 	ETHTOOL_A_CHANNELS_HEADER                 = 0x1
 	ETHTOOL_A_CHANNELS_RX_MAX                 = 0x2
@@ -4015,7 +4042,9 @@ const (
 	ETHTOOL_A_TSINFO_TX_TYPES                 = 0x3
 	ETHTOOL_A_TSINFO_RX_FILTERS               = 0x4
 	ETHTOOL_A_TSINFO_PHC_INDEX                = 0x5
-	ETHTOOL_A_TSINFO_MAX                      = 0x6
+	ETHTOOL_A_TSINFO_STATS                    = 0x6
+	ETHTOOL_A_TSINFO_HWTSTAMP_PROVIDER        = 0x7
+	ETHTOOL_A_TSINFO_MAX                      = 0x7
 	ETHTOOL_A_CABLE_TEST_UNSPEC               = 0x0
 	ETHTOOL_A_CABLE_TEST_HEADER               = 0x1
 	ETHTOOL_A_CABLE_TEST_MAX                  = 0x1
@@ -4613,6 +4642,7 @@ const (
 	NL80211_ATTR_AKM_SUITES                                 = 0x4c
 	NL80211_ATTR_AP_ISOLATE                                 = 0x60
 	NL80211_ATTR_AP_SETTINGS_FLAGS                          = 0x135
+	NL80211_ATTR_ASSOC_SPP_AMSDU                            = 0x14a
 	NL80211_ATTR_AUTH_DATA                                  = 0x9c
 	NL80211_ATTR_AUTH_TYPE                                  = 0x35
 	NL80211_ATTR_BANDS                                      = 0xef
@@ -4623,6 +4653,7 @@ const (
 	NL80211_ATTR_BSS_BASIC_RATES                            = 0x24
 	NL80211_ATTR_BSS                                        = 0x2f
 	NL80211_ATTR_BSS_CTS_PROT                               = 0x1c
+	NL80211_ATTR_BSS_DUMP_INCLUDE_USE_DATA                  = 0x147
 	NL80211_ATTR_BSS_HT_OPMODE                              = 0x6d
 	NL80211_ATTR_BSSID                                      = 0xf5
 	NL80211_ATTR_BSS_SELECT                                 = 0xe3
@@ -4682,6 +4713,7 @@ const (
 	NL80211_ATTR_DTIM_PERIOD                                = 0xd
 	NL80211_ATTR_DURATION                                   = 0x57
 	NL80211_ATTR_EHT_CAPABILITY                             = 0x136
+	NL80211_ATTR_EMA_RNR_ELEMS                              = 0x145
 	NL80211_ATTR_EML_CAPABILITY                             = 0x13d
 	NL80211_ATTR_EXT_CAPA                                   = 0xa9
 	NL80211_ATTR_EXT_CAPA_MASK                              = 0xaa
@@ -4717,6 +4749,7 @@ const (
 	NL80211_ATTR_HIDDEN_SSID                                = 0x7e
 	NL80211_ATTR_HT_CAPABILITY                              = 0x1f
 	NL80211_ATTR_HT_CAPABILITY_MASK                         = 0x94
+	NL80211_ATTR_HW_TIMESTAMP_ENABLED                       = 0x144
 	NL80211_ATTR_IE_ASSOC_RESP                              = 0x80
 	NL80211_ATTR_IE                                         = 0x2a
 	NL80211_ATTR_IE_PROBE_RESP                              = 0x7f
@@ -4747,9 +4780,10 @@ const (
 	NL80211_ATTR_MAC_HINT                                   = 0xc8
 	NL80211_ATTR_MAC_MASK                                   = 0xd7
 	NL80211_ATTR_MAX_AP_ASSOC_STA                           = 0xca
-	NL80211_ATTR_MAX                                        = 0x14d
+	NL80211_ATTR_MAX                                        = 0x150
 	NL80211_ATTR_MAX_CRIT_PROT_DURATION                     = 0xb4
 	NL80211_ATTR_MAX_CSA_COUNTERS                           = 0xce
+	NL80211_ATTR_MAX_HW_TIMESTAMP_PEERS                     = 0x143
 	NL80211_ATTR_MAX_MATCH_SETS                             = 0x85
 	NL80211_ATTR_MAX_NUM_AKM_SUITES                         = 0x13c
 	NL80211_ATTR_MAX_NUM_PMKIDS                             = 0x56
@@ -4774,9 +4808,12 @@ const (
 	NL80211_ATTR_MGMT_SUBTYPE                               = 0x29
 	NL80211_ATTR_MLD_ADDR                                   = 0x13a
 	NL80211_ATTR_MLD_CAPA_AND_OPS                           = 0x13e
+	NL80211_ATTR_MLO_LINK_DISABLED                          = 0x146
 	NL80211_ATTR_MLO_LINK_ID                                = 0x139
 	NL80211_ATTR_MLO_LINKS                                  = 0x138
 	NL80211_ATTR_MLO_SUPPORT                                = 0x13b
+	NL80211_ATTR_MLO_TTLM_DLINK                             = 0x148
+	NL80211_ATTR_MLO_TTLM_ULINK                             = 0x149
 	NL80211_ATTR_MNTR_FLAGS                                 = 0x17
 	NL80211_ATTR_MPATH_INFO                                 = 0x1b
 	NL80211_ATTR_MPATH_NEXT_HOP                             = 0x1a
@@ -4809,12 +4846,14 @@ const (
 	NL80211_ATTR_PORT_AUTHORIZED                            = 0x103
 	NL80211_ATTR_POWER_RULE_MAX_ANT_GAIN                    = 0x5
 	NL80211_ATTR_POWER_RULE_MAX_EIRP                        = 0x6
+	NL80211_ATTR_POWER_RULE_PSD                             = 0x8
 	NL80211_ATTR_PREV_BSSID                                 = 0x4f
 	NL80211_ATTR_PRIVACY                                    = 0x46
 	NL80211_ATTR_PROBE_RESP                                 = 0x91
 	NL80211_ATTR_PROBE_RESP_OFFLOAD                         = 0x90
 	NL80211_ATTR_PROTOCOL_FEATURES                          = 0xad
 	NL80211_ATTR_PS_STATE                                   = 0x5d
+	NL80211_ATTR_PUNCT_BITMAP                               = 0x142
 	NL80211_ATTR_QOS_MAP                                    = 0xc7
 	NL80211_ATTR_RADAR_BACKGROUND                           = 0x134
 	NL80211_ATTR_RADAR_EVENT                                = 0xa8
@@ -4943,7 +4982,9 @@ const (
 	NL80211_ATTR_WIPHY_FREQ                                 = 0x26
 	NL80211_ATTR_WIPHY_FREQ_HINT                            = 0xc9
 	NL80211_ATTR_WIPHY_FREQ_OFFSET                          = 0x122
+	NL80211_ATTR_WIPHY_INTERFACE_COMBINATIONS               = 0x14c
 	NL80211_ATTR_WIPHY_NAME                                 = 0x2
+	NL80211_ATTR_WIPHY_RADIOS                               = 0x14b
 	NL80211_ATTR_WIPHY_RETRY_LONG                           = 0x3e
 	NL80211_ATTR_WIPHY_RETRY_SHORT                          = 0x3d
 	NL80211_ATTR_WIPHY_RTS_THRESHOLD                        = 0x40
@@ -4978,6 +5019,8 @@ const (
 	NL80211_BAND_ATTR_IFTYPE_DATA                           = 0x9
 	NL80211_BAND_ATTR_MAX                                   = 0xd
 	NL80211_BAND_ATTR_RATES                                 = 0x2
+	NL80211_BAND_ATTR_S1G_CAPA                              = 0xd
+	NL80211_BAND_ATTR_S1G_MCS_NSS_SET                       = 0xc
 	NL80211_BAND_ATTR_VHT_CAPA                              = 0x8
 	NL80211_BAND_ATTR_VHT_MCS_SET                           = 0x7
 	NL80211_BAND_IFTYPE_ATTR_EHT_CAP_MAC                    = 0x8
@@ -5001,6 +5044,10 @@ const (
 	NL80211_BSS_BEACON_INTERVAL                             = 0x4
 	NL80211_BSS_BEACON_TSF                                  = 0xd
 	NL80211_BSS_BSSID                                       = 0x1
+	NL80211_BSS_CANNOT_USE_6GHZ_PWR_MISMATCH                = 0x2
+	NL80211_BSS_CANNOT_USE_NSTR_NONPRIMARY                  = 0x1
+	NL80211_BSS_CANNOT_USE_REASONS                          = 0x18
+	NL80211_BSS_CANNOT_USE_UHB_PWR_MISMATCH                 = 0x2
 	NL80211_BSS_CAPABILITY                                  = 0x5
 	NL80211_BSS_CHAIN_SIGNAL                                = 0x13
 	NL80211_BSS_CHAN_WIDTH_10                               = 0x1
@@ -5032,6 +5079,9 @@ const (
 	NL80211_BSS_STATUS                                      = 0x9
 	NL80211_BSS_STATUS_IBSS_JOINED                          = 0x2
 	NL80211_BSS_TSF                                         = 0x3
+	NL80211_BSS_USE_FOR                                     = 0x17
+	NL80211_BSS_USE_FOR_MLD_LINK                            = 0x2
+	NL80211_BSS_USE_FOR_NORMAL                              = 0x1
 	NL80211_CHAN_HT20                                       = 0x1
 	NL80211_CHAN_HT40MINUS                                  = 0x2
 	NL80211_CHAN_HT40PLUS                                   = 0x3
@@ -5117,7 +5167,8 @@ const (
 	NL80211_CMD_LEAVE_IBSS                                  = 0x2c
 	NL80211_CMD_LEAVE_MESH                                  = 0x45
 	NL80211_CMD_LEAVE_OCB                                   = 0x6d
-	NL80211_CMD_MAX                                         = 0x9b
+	NL80211_CMD_LINKS_REMOVED                               = 0x9a
+	NL80211_CMD_MAX                                         = 0x9d
 	NL80211_CMD_MICHAEL_MIC_FAILURE                         = 0x29
 	NL80211_CMD_MODIFY_LINK_STA                             = 0x97
 	NL80211_CMD_NAN_MATCH                                   = 0x78
@@ -5161,6 +5212,7 @@ const (
 	NL80211_CMD_SET_COALESCE                                = 0x65
 	NL80211_CMD_SET_CQM                                     = 0x3f
 	NL80211_CMD_SET_FILS_AAD                                = 0x92
+	NL80211_CMD_SET_HW_TIMESTAMP                            = 0x99
 	NL80211_CMD_SET_INTERFACE                               = 0x6
 	NL80211_CMD_SET_KEY                                     = 0xa
 	NL80211_CMD_SET_MAC_ACL                                 = 0x5d
@@ -5180,6 +5232,7 @@ const (
 	NL80211_CMD_SET_SAR_SPECS                               = 0x8c
 	NL80211_CMD_SET_STATION                                 = 0x12
 	NL80211_CMD_SET_TID_CONFIG                              = 0x89
+	NL80211_CMD_SET_TID_TO_LINK_MAPPING                     = 0x9b
 	NL80211_CMD_SET_TX_BITRATE_MASK                         = 0x39
 	NL80211_CMD_SET_WDS_PEER                                = 0x42
 	NL80211_CMD_SET_WIPHY                                   = 0x2
@@ -5247,6 +5300,7 @@ const (
 	NL80211_EXT_FEATURE_AIRTIME_FAIRNESS                    = 0x21
 	NL80211_EXT_FEATURE_AP_PMKSA_CACHING                    = 0x22
 	NL80211_EXT_FEATURE_AQL                                 = 0x28
+	NL80211_EXT_FEATURE_AUTH_AND_DEAUTH_RANDOM_TA           = 0x40
 	NL80211_EXT_FEATURE_BEACON_PROTECTION_CLIENT            = 0x2e
 	NL80211_EXT_FEATURE_BEACON_PROTECTION                   = 0x29
 	NL80211_EXT_FEATURE_BEACON_RATE_HE                      = 0x36
@@ -5262,6 +5316,7 @@ const (
 	NL80211_EXT_FEATURE_CQM_RSSI_LIST                       = 0xd
 	NL80211_EXT_FEATURE_DATA_ACK_SIGNAL_SUPPORT             = 0x1b
 	NL80211_EXT_FEATURE_DEL_IBSS_STA                        = 0x2c
+	NL80211_EXT_FEATURE_DFS_CONCURRENT                      = 0x43
 	NL80211_EXT_FEATURE_DFS_OFFLOAD                         = 0x19
 	NL80211_EXT_FEATURE_ENABLE_FTM_RESPONDER                = 0x20
 	NL80211_EXT_FEATURE_EXT_KEY_ID                          = 0x24
@@ -5281,9 +5336,12 @@ const (
 	NL80211_EXT_FEATURE_OCE_PROBE_REQ_DEFERRAL_SUPPRESSION  = 0x14
 	NL80211_EXT_FEATURE_OCE_PROBE_REQ_HIGH_TX_RATE          = 0x13
 	NL80211_EXT_FEATURE_OPERATING_CHANNEL_VALIDATION        = 0x31
+	NL80211_EXT_FEATURE_OWE_OFFLOAD_AP                      = 0x42
+	NL80211_EXT_FEATURE_OWE_OFFLOAD                         = 0x41
 	NL80211_EXT_FEATURE_POWERED_ADDR_CHANGE                 = 0x3d
 	NL80211_EXT_FEATURE_PROTECTED_TWT                       = 0x2b
 	NL80211_EXT_FEATURE_PROT_RANGE_NEGO_AND_MEASURE         = 0x39
+	NL80211_EXT_FEATURE_PUNCT                               = 0x3e
 	NL80211_EXT_FEATURE_RADAR_BACKGROUND                    = 0x3c
 	NL80211_EXT_FEATURE_RRM                                 = 0x1
 	NL80211_EXT_FEATURE_SAE_OFFLOAD_AP                      = 0x33
@@ -5295,8 +5353,10 @@ const (
 	NL80211_EXT_FEATURE_SCHED_SCAN_BAND_SPECIFIC_RSSI_THOLD = 0x23
 	NL80211_EXT_FEATURE_SCHED_SCAN_RELATIVE_RSSI            = 0xc
 	NL80211_EXT_FEATURE_SECURE_LTF                          = 0x37
+	NL80211_EXT_FEATURE_SECURE_NAN                          = 0x3f
 	NL80211_EXT_FEATURE_SECURE_RTT                          = 0x38
 	NL80211_EXT_FEATURE_SET_SCAN_DWELL                      = 0x5
+	NL80211_EXT_FEATURE_SPP_AMSDU_SUPPORT                   = 0x44
 	NL80211_EXT_FEATURE_STA_TX_PWR                          = 0x25
 	NL80211_EXT_FEATURE_TXQS                                = 0x1c
 	NL80211_EXT_FEATURE_UNSOL_BCAST_PROBE_RESP              = 0x35
@@ -5343,7 +5403,10 @@ const (
 	NL80211_FREQUENCY_ATTR_2MHZ                             = 0x16
 	NL80211_FREQUENCY_ATTR_4MHZ                             = 0x17
 	NL80211_FREQUENCY_ATTR_8MHZ                             = 0x18
+	NL80211_FREQUENCY_ATTR_ALLOW_6GHZ_VLP_AP                = 0x21
+	NL80211_FREQUENCY_ATTR_CAN_MONITOR                      = 0x20
 	NL80211_FREQUENCY_ATTR_DFS_CAC_TIME                     = 0xd
+	NL80211_FREQUENCY_ATTR_DFS_CONCURRENT                   = 0x1d
 	NL80211_FREQUENCY_ATTR_DFS_STATE                        = 0x7
 	NL80211_FREQUENCY_ATTR_DFS_TIME                         = 0x8
 	NL80211_FREQUENCY_ATTR_DISABLED                         = 0x2
@@ -5357,6 +5420,8 @@ const (
 	NL80211_FREQUENCY_ATTR_NO_160MHZ                        = 0xc
 	NL80211_FREQUENCY_ATTR_NO_20MHZ                         = 0x10
 	NL80211_FREQUENCY_ATTR_NO_320MHZ                        = 0x1a
+	NL80211_FREQUENCY_ATTR_NO_6GHZ_AFC_CLIENT               = 0x1f
+	NL80211_FREQUENCY_ATTR_NO_6GHZ_VLP_CLIENT               = 0x1e
 	NL80211_FREQUENCY_ATTR_NO_80MHZ                         = 0xb
 	NL80211_FREQUENCY_ATTR_NO_EHT                           = 0x1b
 	NL80211_FREQUENCY_ATTR_NO_HE                            = 0x13
@@ -5364,8 +5429,11 @@ const (
 	NL80211_FREQUENCY_ATTR_NO_HT40_PLUS                     = 0xa
 	NL80211_FREQUENCY_ATTR_NO_IBSS                          = 0x3
 	NL80211_FREQUENCY_ATTR_NO_IR                            = 0x3
+	NL80211_FREQUENCY_ATTR_NO_UHB_AFC_CLIENT                = 0x1f
+	NL80211_FREQUENCY_ATTR_NO_UHB_VLP_CLIENT                = 0x1e
 	NL80211_FREQUENCY_ATTR_OFFSET                           = 0x14
 	NL80211_FREQUENCY_ATTR_PASSIVE_SCAN                     = 0x3
+	NL80211_FREQUENCY_ATTR_PSD                              = 0x1c
 	NL80211_FREQUENCY_ATTR_RADAR                            = 0x5
 	NL80211_FREQUENCY_ATTR_WMM                              = 0x12
 	NL80211_FTM_RESP_ATTR_CIVICLOC                          = 0x3
@@ -5430,6 +5498,7 @@ const (
 	NL80211_IFTYPE_STATION                                  = 0x2
 	NL80211_IFTYPE_UNSPECIFIED                              = 0x0
 	NL80211_IFTYPE_WDS                                      = 0x5
+	NL80211_KCK_EXT_LEN_32                                  = 0x20
 	NL80211_KCK_EXT_LEN                                     = 0x18
 	NL80211_KCK_LEN                                         = 0x10
 	NL80211_KEK_EXT_LEN                                     = 0x20
@@ -5458,6 +5527,7 @@ const (
 	NL80211_MAX_SUPP_HT_RATES                               = 0x4d
 	NL80211_MAX_SUPP_RATES                                  = 0x20
 	NL80211_MAX_SUPP_REG_RULES                              = 0x80
+	NL80211_MAX_SUPP_SELECTORS                              = 0x80
 	NL80211_MBSSID_CONFIG_ATTR_EMA                          = 0x5
 	NL80211_MBSSID_CONFIG_ATTR_INDEX                        = 0x3
 	NL80211_MBSSID_CONFIG_ATTR_MAX                          = 0x5
@@ -5703,11 +5773,16 @@ const (
 	NL80211_RADAR_PRE_CAC_EXPIRED                           = 0x4
 	NL80211_RATE_INFO_10_MHZ_WIDTH                          = 0xb
 	NL80211_RATE_INFO_160_MHZ_WIDTH                         = 0xa
+	NL80211_RATE_INFO_16_MHZ_WIDTH                          = 0x1d
+	NL80211_RATE_INFO_1_MHZ_WIDTH                           = 0x19
+	NL80211_RATE_INFO_2_MHZ_WIDTH                           = 0x1a
 	NL80211_RATE_INFO_320_MHZ_WIDTH                         = 0x12
 	NL80211_RATE_INFO_40_MHZ_WIDTH                          = 0x3
+	NL80211_RATE_INFO_4_MHZ_WIDTH                           = 0x1b
 	NL80211_RATE_INFO_5_MHZ_WIDTH                           = 0xc
 	NL80211_RATE_INFO_80_MHZ_WIDTH                          = 0x8
 	NL80211_RATE_INFO_80P80_MHZ_WIDTH                       = 0x9
+	NL80211_RATE_INFO_8_MHZ_WIDTH                           = 0x1c
 	NL80211_RATE_INFO_BITRATE32                             = 0x5
 	NL80211_RATE_INFO_BITRATE                               = 0x1
 	NL80211_RATE_INFO_EHT_GI_0_8                            = 0x0
@@ -5753,6 +5828,8 @@ const (
 	NL80211_RATE_INFO_HE_RU_ALLOC                           = 0x11
 	NL80211_RATE_INFO_MAX                                   = 0x1d
 	NL80211_RATE_INFO_MCS                                   = 0x2
+	NL80211_RATE_INFO_S1G_MCS                               = 0x17
+	NL80211_RATE_INFO_S1G_NSS                               = 0x18
 	NL80211_RATE_INFO_SHORT_GI                              = 0x4
 	NL80211_RATE_INFO_VHT_MCS                               = 0x6
 	NL80211_RATE_INFO_VHT_NSS                               = 0x7
@@ -5770,14 +5847,19 @@ const (
 	NL80211_REKEY_DATA_KEK                                  = 0x1
 	NL80211_REKEY_DATA_REPLAY_CTR                           = 0x3
 	NL80211_REPLAY_CTR_LEN                                  = 0x8
+	NL80211_RRF_ALLOW_6GHZ_VLP_AP                           = 0x1000000
 	NL80211_RRF_AUTO_BW                                     = 0x800
 	NL80211_RRF_DFS                                         = 0x10
+	NL80211_RRF_DFS_CONCURRENT                              = 0x200000
 	NL80211_RRF_GO_CONCURRENT                               = 0x1000
 	NL80211_RRF_IR_CONCURRENT                               = 0x1000
 	NL80211_RRF_NO_160MHZ                                   = 0x10000
 	NL80211_RRF_NO_320MHZ                                   = 0x40000
+	NL80211_RRF_NO_6GHZ_AFC_CLIENT                          = 0x800000
+	NL80211_RRF_NO_6GHZ_VLP_CLIENT                          = 0x400000
 	NL80211_RRF_NO_80MHZ                                    = 0x8000
 	NL80211_RRF_NO_CCK                                      = 0x2
+	NL80211_RRF_NO_EHT                                      = 0x80000
 	NL80211_RRF_NO_HE                                       = 0x20000
 	NL80211_RRF_NO_HT40                                     = 0x6000
 	NL80211_RRF_NO_HT40MINUS                                = 0x2000
@@ -5788,7 +5870,10 @@ const (
 	NL80211_RRF_NO_IR                                       = 0x80
 	NL80211_RRF_NO_OFDM                                     = 0x1
 	NL80211_RRF_NO_OUTDOOR                                  = 0x8
+	NL80211_RRF_NO_UHB_AFC_CLIENT                           = 0x800000
+	NL80211_RRF_NO_UHB_VLP_CLIENT                           = 0x400000
 	NL80211_RRF_PASSIVE_SCAN                                = 0x80
+	NL80211_RRF_PSD                                         = 0x100000
 	NL80211_RRF_PTMP_ONLY                                   = 0x40
 	NL80211_RRF_PTP_ONLY                                    = 0x20
 	NL80211_RXMGMT_FLAG_ANSWERED                            = 0x1
@@ -5849,6 +5934,7 @@ const (
 	NL80211_STA_FLAG_MAX_OLD_API                            = 0x6
 	NL80211_STA_FLAG_MFP                                    = 0x4
 	NL80211_STA_FLAG_SHORT_PREAMBLE                         = 0x2
+	NL80211_STA_FLAG_SPP_AMSDU                              = 0x8
 	NL80211_STA_FLAG_TDLS_PEER                              = 0x6
 	NL80211_STA_FLAG_WME                                    = 0x3
 	NL80211_STA_INFO_ACK_SIGNAL_AVG                         = 0x23
@@ -6007,6 +6093,13 @@ const (
 	NL80211_VHT_CAPABILITY_LEN                              = 0xc
 	NL80211_VHT_NSS_MAX                                     = 0x8
 	NL80211_WIPHY_NAME_MAXLEN                               = 0x40
+	NL80211_WIPHY_RADIO_ATTR_FREQ_RANGE                     = 0x2
+	NL80211_WIPHY_RADIO_ATTR_INDEX                          = 0x1
+	NL80211_WIPHY_RADIO_ATTR_INTERFACE_COMBINATION          = 0x3
+	NL80211_WIPHY_RADIO_ATTR_MAX                            = 0x4
+	NL80211_WIPHY_RADIO_FREQ_ATTR_END                       = 0x2
+	NL80211_WIPHY_RADIO_FREQ_ATTR_MAX                       = 0x2
+	NL80211_WIPHY_RADIO_FREQ_ATTR_START                     = 0x1
 	NL80211_WMMR_AIFSN                                      = 0x3
 	NL80211_WMMR_CW_MAX                                     = 0x2
 	NL80211_WMMR_CW_MIN                                     = 0x1
@@ -6038,6 +6131,7 @@ const (
 	NL80211_WOWLAN_TRIG_PKT_PATTERN                         = 0x4
 	NL80211_WOWLAN_TRIG_RFKILL_RELEASE                      = 0x9
 	NL80211_WOWLAN_TRIG_TCP_CONNECTION                      = 0xe
+	NL80211_WOWLAN_TRIG_UNPROTECTED_DEAUTH_DISASSOC         = 0x14
 	NL80211_WOWLAN_TRIG_WAKEUP_PKT_80211                    = 0xa
 	NL80211_WOWLAN_TRIG_WAKEUP_PKT_80211_LEN                = 0xb
 	NL80211_WOWLAN_TRIG_WAKEUP_PKT_8023                     = 0xc

vendor/golang.org/x/sys/unix/ztypes_linux_386.go 🔗

@@ -285,10 +285,16 @@ type Taskstats struct {
 	_                         [4]byte
 	Cpu_count                 uint64
 	Cpu_delay_total           uint64
+	Cpu_delay_max             uint64
+	Cpu_delay_min             uint64
 	Blkio_count               uint64
 	Blkio_delay_total         uint64
+	Blkio_delay_max           uint64
+	Blkio_delay_min           uint64
 	Swapin_count              uint64
 	Swapin_delay_total        uint64
+	Swapin_delay_max          uint64
+	Swapin_delay_min          uint64
 	Cpu_run_real_total        uint64
 	Cpu_run_virtual_total     uint64
 	Ac_comm                   [32]int8
@@ -324,11 +330,17 @@ type Taskstats struct {
 	Cpu_scaled_run_real_total uint64
 	Freepages_count           uint64
 	Freepages_delay_total     uint64
+	Freepages_delay_max       uint64
+	Freepages_delay_min       uint64
 	Thrashing_count           uint64
 	Thrashing_delay_total     uint64
+	Thrashing_delay_max       uint64
+	Thrashing_delay_min       uint64
 	Ac_btime64                uint64
 	Compact_count             uint64
 	Compact_delay_total       uint64
+	Compact_delay_max         uint64
+	Compact_delay_min         uint64
 	Ac_tgid                   uint32
 	_                         [4]byte
 	Ac_tgetime                uint64
@@ -336,8 +348,12 @@ type Taskstats struct {
 	Ac_exe_inode              uint64
 	Wpcopy_count              uint64
 	Wpcopy_delay_total        uint64
+	Wpcopy_delay_max          uint64
+	Wpcopy_delay_min          uint64
 	Irq_count                 uint64
 	Irq_delay_total           uint64
+	Irq_delay_max             uint64
+	Irq_delay_min             uint64
 }
 
 type cpuMask uint32

vendor/golang.org/x/sys/unix/ztypes_linux_amd64.go 🔗

@@ -300,10 +300,16 @@ type Taskstats struct {
 	Ac_nice                   uint8
 	Cpu_count                 uint64
 	Cpu_delay_total           uint64
+	Cpu_delay_max             uint64
+	Cpu_delay_min             uint64
 	Blkio_count               uint64
 	Blkio_delay_total         uint64
+	Blkio_delay_max           uint64
+	Blkio_delay_min           uint64
 	Swapin_count              uint64
 	Swapin_delay_total        uint64
+	Swapin_delay_max          uint64
+	Swapin_delay_min          uint64
 	Cpu_run_real_total        uint64
 	Cpu_run_virtual_total     uint64
 	Ac_comm                   [32]int8
@@ -338,19 +344,29 @@ type Taskstats struct {
 	Cpu_scaled_run_real_total uint64
 	Freepages_count           uint64
 	Freepages_delay_total     uint64
+	Freepages_delay_max       uint64
+	Freepages_delay_min       uint64
 	Thrashing_count           uint64
 	Thrashing_delay_total     uint64
+	Thrashing_delay_max       uint64
+	Thrashing_delay_min       uint64
 	Ac_btime64                uint64
 	Compact_count             uint64
 	Compact_delay_total       uint64
+	Compact_delay_max         uint64
+	Compact_delay_min         uint64
 	Ac_tgid                   uint32
 	Ac_tgetime                uint64
 	Ac_exe_dev                uint64
 	Ac_exe_inode              uint64
 	Wpcopy_count              uint64
 	Wpcopy_delay_total        uint64
+	Wpcopy_delay_max          uint64
+	Wpcopy_delay_min          uint64
 	Irq_count                 uint64
 	Irq_delay_total           uint64
+	Irq_delay_max             uint64
+	Irq_delay_min             uint64
 }
 
 type cpuMask uint64

vendor/golang.org/x/sys/unix/ztypes_linux_arm.go 🔗

@@ -276,10 +276,16 @@ type Taskstats struct {
 	_                         [4]byte
 	Cpu_count                 uint64
 	Cpu_delay_total           uint64
+	Cpu_delay_max             uint64
+	Cpu_delay_min             uint64
 	Blkio_count               uint64
 	Blkio_delay_total         uint64
+	Blkio_delay_max           uint64
+	Blkio_delay_min           uint64
 	Swapin_count              uint64
 	Swapin_delay_total        uint64
+	Swapin_delay_max          uint64
+	Swapin_delay_min          uint64
 	Cpu_run_real_total        uint64
 	Cpu_run_virtual_total     uint64
 	Ac_comm                   [32]uint8
@@ -315,11 +321,17 @@ type Taskstats struct {
 	Cpu_scaled_run_real_total uint64
 	Freepages_count           uint64
 	Freepages_delay_total     uint64
+	Freepages_delay_max       uint64
+	Freepages_delay_min       uint64
 	Thrashing_count           uint64
 	Thrashing_delay_total     uint64
+	Thrashing_delay_max       uint64
+	Thrashing_delay_min       uint64
 	Ac_btime64                uint64
 	Compact_count             uint64
 	Compact_delay_total       uint64
+	Compact_delay_max         uint64
+	Compact_delay_min         uint64
 	Ac_tgid                   uint32
 	_                         [4]byte
 	Ac_tgetime                uint64
@@ -327,8 +339,12 @@ type Taskstats struct {
 	Ac_exe_inode              uint64
 	Wpcopy_count              uint64
 	Wpcopy_delay_total        uint64
+	Wpcopy_delay_max          uint64
+	Wpcopy_delay_min          uint64
 	Irq_count                 uint64
 	Irq_delay_total           uint64
+	Irq_delay_max             uint64
+	Irq_delay_min             uint64
 }
 
 type cpuMask uint32

vendor/golang.org/x/sys/unix/ztypes_linux_arm64.go 🔗

@@ -279,10 +279,16 @@ type Taskstats struct {
 	Ac_nice                   uint8
 	Cpu_count                 uint64
 	Cpu_delay_total           uint64
+	Cpu_delay_max             uint64
+	Cpu_delay_min             uint64
 	Blkio_count               uint64
 	Blkio_delay_total         uint64
+	Blkio_delay_max           uint64
+	Blkio_delay_min           uint64
 	Swapin_count              uint64
 	Swapin_delay_total        uint64
+	Swapin_delay_max          uint64
+	Swapin_delay_min          uint64
 	Cpu_run_real_total        uint64
 	Cpu_run_virtual_total     uint64
 	Ac_comm                   [32]int8
@@ -317,19 +323,29 @@ type Taskstats struct {
 	Cpu_scaled_run_real_total uint64
 	Freepages_count           uint64
 	Freepages_delay_total     uint64
+	Freepages_delay_max       uint64
+	Freepages_delay_min       uint64
 	Thrashing_count           uint64
 	Thrashing_delay_total     uint64
+	Thrashing_delay_max       uint64
+	Thrashing_delay_min       uint64
 	Ac_btime64                uint64
 	Compact_count             uint64
 	Compact_delay_total       uint64
+	Compact_delay_max         uint64
+	Compact_delay_min         uint64
 	Ac_tgid                   uint32
 	Ac_tgetime                uint64
 	Ac_exe_dev                uint64
 	Ac_exe_inode              uint64
 	Wpcopy_count              uint64
 	Wpcopy_delay_total        uint64
+	Wpcopy_delay_max          uint64
+	Wpcopy_delay_min          uint64
 	Irq_count                 uint64
 	Irq_delay_total           uint64
+	Irq_delay_max             uint64
+	Irq_delay_min             uint64
 }
 
 type cpuMask uint64

vendor/golang.org/x/sys/unix/ztypes_linux_loong64.go 🔗

@@ -280,10 +280,16 @@ type Taskstats struct {
 	Ac_nice                   uint8
 	Cpu_count                 uint64
 	Cpu_delay_total           uint64
+	Cpu_delay_max             uint64
+	Cpu_delay_min             uint64
 	Blkio_count               uint64
 	Blkio_delay_total         uint64
+	Blkio_delay_max           uint64
+	Blkio_delay_min           uint64
 	Swapin_count              uint64
 	Swapin_delay_total        uint64
+	Swapin_delay_max          uint64
+	Swapin_delay_min          uint64
 	Cpu_run_real_total        uint64
 	Cpu_run_virtual_total     uint64
 	Ac_comm                   [32]int8
@@ -318,19 +324,29 @@ type Taskstats struct {
 	Cpu_scaled_run_real_total uint64
 	Freepages_count           uint64
 	Freepages_delay_total     uint64
+	Freepages_delay_max       uint64
+	Freepages_delay_min       uint64
 	Thrashing_count           uint64
 	Thrashing_delay_total     uint64
+	Thrashing_delay_max       uint64
+	Thrashing_delay_min       uint64
 	Ac_btime64                uint64
 	Compact_count             uint64
 	Compact_delay_total       uint64
+	Compact_delay_max         uint64
+	Compact_delay_min         uint64
 	Ac_tgid                   uint32
 	Ac_tgetime                uint64
 	Ac_exe_dev                uint64
 	Ac_exe_inode              uint64
 	Wpcopy_count              uint64
 	Wpcopy_delay_total        uint64
+	Wpcopy_delay_max          uint64
+	Wpcopy_delay_min          uint64
 	Irq_count                 uint64
 	Irq_delay_total           uint64
+	Irq_delay_max             uint64
+	Irq_delay_min             uint64
 }
 
 type cpuMask uint64

vendor/golang.org/x/sys/unix/ztypes_linux_mips.go 🔗

@@ -281,10 +281,16 @@ type Taskstats struct {
 	_                         [4]byte
 	Cpu_count                 uint64
 	Cpu_delay_total           uint64
+	Cpu_delay_max             uint64
+	Cpu_delay_min             uint64
 	Blkio_count               uint64
 	Blkio_delay_total         uint64
+	Blkio_delay_max           uint64
+	Blkio_delay_min           uint64
 	Swapin_count              uint64
 	Swapin_delay_total        uint64
+	Swapin_delay_max          uint64
+	Swapin_delay_min          uint64
 	Cpu_run_real_total        uint64
 	Cpu_run_virtual_total     uint64
 	Ac_comm                   [32]int8
@@ -320,11 +326,17 @@ type Taskstats struct {
 	Cpu_scaled_run_real_total uint64
 	Freepages_count           uint64
 	Freepages_delay_total     uint64
+	Freepages_delay_max       uint64
+	Freepages_delay_min       uint64
 	Thrashing_count           uint64
 	Thrashing_delay_total     uint64
+	Thrashing_delay_max       uint64
+	Thrashing_delay_min       uint64
 	Ac_btime64                uint64
 	Compact_count             uint64
 	Compact_delay_total       uint64
+	Compact_delay_max         uint64
+	Compact_delay_min         uint64
 	Ac_tgid                   uint32
 	_                         [4]byte
 	Ac_tgetime                uint64
@@ -332,8 +344,12 @@ type Taskstats struct {
 	Ac_exe_inode              uint64
 	Wpcopy_count              uint64
 	Wpcopy_delay_total        uint64
+	Wpcopy_delay_max          uint64
+	Wpcopy_delay_min          uint64
 	Irq_count                 uint64
 	Irq_delay_total           uint64
+	Irq_delay_max             uint64
+	Irq_delay_min             uint64
 }
 
 type cpuMask uint32

vendor/golang.org/x/sys/unix/ztypes_linux_mips64.go 🔗

@@ -282,10 +282,16 @@ type Taskstats struct {
 	Ac_nice                   uint8
 	Cpu_count                 uint64
 	Cpu_delay_total           uint64
+	Cpu_delay_max             uint64
+	Cpu_delay_min             uint64
 	Blkio_count               uint64
 	Blkio_delay_total         uint64
+	Blkio_delay_max           uint64
+	Blkio_delay_min           uint64
 	Swapin_count              uint64
 	Swapin_delay_total        uint64
+	Swapin_delay_max          uint64
+	Swapin_delay_min          uint64
 	Cpu_run_real_total        uint64
 	Cpu_run_virtual_total     uint64
 	Ac_comm                   [32]int8
@@ -320,19 +326,29 @@ type Taskstats struct {
 	Cpu_scaled_run_real_total uint64
 	Freepages_count           uint64
 	Freepages_delay_total     uint64
+	Freepages_delay_max       uint64
+	Freepages_delay_min       uint64
 	Thrashing_count           uint64
 	Thrashing_delay_total     uint64
+	Thrashing_delay_max       uint64
+	Thrashing_delay_min       uint64
 	Ac_btime64                uint64
 	Compact_count             uint64
 	Compact_delay_total       uint64
+	Compact_delay_max         uint64
+	Compact_delay_min         uint64
 	Ac_tgid                   uint32
 	Ac_tgetime                uint64
 	Ac_exe_dev                uint64
 	Ac_exe_inode              uint64
 	Wpcopy_count              uint64
 	Wpcopy_delay_total        uint64
+	Wpcopy_delay_max          uint64
+	Wpcopy_delay_min          uint64
 	Irq_count                 uint64
 	Irq_delay_total           uint64
+	Irq_delay_max             uint64
+	Irq_delay_min             uint64
 }
 
 type cpuMask uint64

vendor/golang.org/x/sys/unix/ztypes_linux_mips64le.go 🔗

@@ -282,10 +282,16 @@ type Taskstats struct {
 	Ac_nice                   uint8
 	Cpu_count                 uint64
 	Cpu_delay_total           uint64
+	Cpu_delay_max             uint64
+	Cpu_delay_min             uint64
 	Blkio_count               uint64
 	Blkio_delay_total         uint64
+	Blkio_delay_max           uint64
+	Blkio_delay_min           uint64
 	Swapin_count              uint64
 	Swapin_delay_total        uint64
+	Swapin_delay_max          uint64
+	Swapin_delay_min          uint64
 	Cpu_run_real_total        uint64
 	Cpu_run_virtual_total     uint64
 	Ac_comm                   [32]int8
@@ -320,19 +326,29 @@ type Taskstats struct {
 	Cpu_scaled_run_real_total uint64
 	Freepages_count           uint64
 	Freepages_delay_total     uint64
+	Freepages_delay_max       uint64
+	Freepages_delay_min       uint64
 	Thrashing_count           uint64
 	Thrashing_delay_total     uint64
+	Thrashing_delay_max       uint64
+	Thrashing_delay_min       uint64
 	Ac_btime64                uint64
 	Compact_count             uint64
 	Compact_delay_total       uint64
+	Compact_delay_max         uint64
+	Compact_delay_min         uint64
 	Ac_tgid                   uint32
 	Ac_tgetime                uint64
 	Ac_exe_dev                uint64
 	Ac_exe_inode              uint64
 	Wpcopy_count              uint64
 	Wpcopy_delay_total        uint64
+	Wpcopy_delay_max          uint64
+	Wpcopy_delay_min          uint64
 	Irq_count                 uint64
 	Irq_delay_total           uint64
+	Irq_delay_max             uint64
+	Irq_delay_min             uint64
 }
 
 type cpuMask uint64

vendor/golang.org/x/sys/unix/ztypes_linux_mipsle.go 🔗

@@ -281,10 +281,16 @@ type Taskstats struct {
 	_                         [4]byte
 	Cpu_count                 uint64
 	Cpu_delay_total           uint64
+	Cpu_delay_max             uint64
+	Cpu_delay_min             uint64
 	Blkio_count               uint64
 	Blkio_delay_total         uint64
+	Blkio_delay_max           uint64
+	Blkio_delay_min           uint64
 	Swapin_count              uint64
 	Swapin_delay_total        uint64
+	Swapin_delay_max          uint64
+	Swapin_delay_min          uint64
 	Cpu_run_real_total        uint64
 	Cpu_run_virtual_total     uint64
 	Ac_comm                   [32]int8
@@ -320,11 +326,17 @@ type Taskstats struct {
 	Cpu_scaled_run_real_total uint64
 	Freepages_count           uint64
 	Freepages_delay_total     uint64
+	Freepages_delay_max       uint64
+	Freepages_delay_min       uint64
 	Thrashing_count           uint64
 	Thrashing_delay_total     uint64
+	Thrashing_delay_max       uint64
+	Thrashing_delay_min       uint64
 	Ac_btime64                uint64
 	Compact_count             uint64
 	Compact_delay_total       uint64
+	Compact_delay_max         uint64
+	Compact_delay_min         uint64
 	Ac_tgid                   uint32
 	_                         [4]byte
 	Ac_tgetime                uint64
@@ -332,8 +344,12 @@ type Taskstats struct {
 	Ac_exe_inode              uint64
 	Wpcopy_count              uint64
 	Wpcopy_delay_total        uint64
+	Wpcopy_delay_max          uint64
+	Wpcopy_delay_min          uint64
 	Irq_count                 uint64
 	Irq_delay_total           uint64
+	Irq_delay_max             uint64
+	Irq_delay_min             uint64
 }
 
 type cpuMask uint32

vendor/golang.org/x/sys/unix/ztypes_linux_ppc.go 🔗

@@ -288,10 +288,16 @@ type Taskstats struct {
 	_                         [4]byte
 	Cpu_count                 uint64
 	Cpu_delay_total           uint64
+	Cpu_delay_max             uint64
+	Cpu_delay_min             uint64
 	Blkio_count               uint64
 	Blkio_delay_total         uint64
+	Blkio_delay_max           uint64
+	Blkio_delay_min           uint64
 	Swapin_count              uint64
 	Swapin_delay_total        uint64
+	Swapin_delay_max          uint64
+	Swapin_delay_min          uint64
 	Cpu_run_real_total        uint64
 	Cpu_run_virtual_total     uint64
 	Ac_comm                   [32]uint8
@@ -327,11 +333,17 @@ type Taskstats struct {
 	Cpu_scaled_run_real_total uint64
 	Freepages_count           uint64
 	Freepages_delay_total     uint64
+	Freepages_delay_max       uint64
+	Freepages_delay_min       uint64
 	Thrashing_count           uint64
 	Thrashing_delay_total     uint64
+	Thrashing_delay_max       uint64
+	Thrashing_delay_min       uint64
 	Ac_btime64                uint64
 	Compact_count             uint64
 	Compact_delay_total       uint64
+	Compact_delay_max         uint64
+	Compact_delay_min         uint64
 	Ac_tgid                   uint32
 	_                         [4]byte
 	Ac_tgetime                uint64
@@ -339,8 +351,12 @@ type Taskstats struct {
 	Ac_exe_inode              uint64
 	Wpcopy_count              uint64
 	Wpcopy_delay_total        uint64
+	Wpcopy_delay_max          uint64
+	Wpcopy_delay_min          uint64
 	Irq_count                 uint64
 	Irq_delay_total           uint64
+	Irq_delay_max             uint64
+	Irq_delay_min             uint64
 }
 
 type cpuMask uint32

vendor/golang.org/x/sys/unix/ztypes_linux_ppc64.go 🔗

@@ -289,10 +289,16 @@ type Taskstats struct {
 	Ac_nice                   uint8
 	Cpu_count                 uint64
 	Cpu_delay_total           uint64
+	Cpu_delay_max             uint64
+	Cpu_delay_min             uint64
 	Blkio_count               uint64
 	Blkio_delay_total         uint64
+	Blkio_delay_max           uint64
+	Blkio_delay_min           uint64
 	Swapin_count              uint64
 	Swapin_delay_total        uint64
+	Swapin_delay_max          uint64
+	Swapin_delay_min          uint64
 	Cpu_run_real_total        uint64
 	Cpu_run_virtual_total     uint64
 	Ac_comm                   [32]uint8
@@ -327,19 +333,29 @@ type Taskstats struct {
 	Cpu_scaled_run_real_total uint64
 	Freepages_count           uint64
 	Freepages_delay_total     uint64
+	Freepages_delay_max       uint64
+	Freepages_delay_min       uint64
 	Thrashing_count           uint64
 	Thrashing_delay_total     uint64
+	Thrashing_delay_max       uint64
+	Thrashing_delay_min       uint64
 	Ac_btime64                uint64
 	Compact_count             uint64
 	Compact_delay_total       uint64
+	Compact_delay_max         uint64
+	Compact_delay_min         uint64
 	Ac_tgid                   uint32
 	Ac_tgetime                uint64
 	Ac_exe_dev                uint64
 	Ac_exe_inode              uint64
 	Wpcopy_count              uint64
 	Wpcopy_delay_total        uint64
+	Wpcopy_delay_max          uint64
+	Wpcopy_delay_min          uint64
 	Irq_count                 uint64
 	Irq_delay_total           uint64
+	Irq_delay_max             uint64
+	Irq_delay_min             uint64
 }
 
 type cpuMask uint64

vendor/golang.org/x/sys/unix/ztypes_linux_ppc64le.go 🔗

@@ -289,10 +289,16 @@ type Taskstats struct {
 	Ac_nice                   uint8
 	Cpu_count                 uint64
 	Cpu_delay_total           uint64
+	Cpu_delay_max             uint64
+	Cpu_delay_min             uint64
 	Blkio_count               uint64
 	Blkio_delay_total         uint64
+	Blkio_delay_max           uint64
+	Blkio_delay_min           uint64
 	Swapin_count              uint64
 	Swapin_delay_total        uint64
+	Swapin_delay_max          uint64
+	Swapin_delay_min          uint64
 	Cpu_run_real_total        uint64
 	Cpu_run_virtual_total     uint64
 	Ac_comm                   [32]uint8
@@ -327,19 +333,29 @@ type Taskstats struct {
 	Cpu_scaled_run_real_total uint64
 	Freepages_count           uint64
 	Freepages_delay_total     uint64
+	Freepages_delay_max       uint64
+	Freepages_delay_min       uint64
 	Thrashing_count           uint64
 	Thrashing_delay_total     uint64
+	Thrashing_delay_max       uint64
+	Thrashing_delay_min       uint64
 	Ac_btime64                uint64
 	Compact_count             uint64
 	Compact_delay_total       uint64
+	Compact_delay_max         uint64
+	Compact_delay_min         uint64
 	Ac_tgid                   uint32
 	Ac_tgetime                uint64
 	Ac_exe_dev                uint64
 	Ac_exe_inode              uint64
 	Wpcopy_count              uint64
 	Wpcopy_delay_total        uint64
+	Wpcopy_delay_max          uint64
+	Wpcopy_delay_min          uint64
 	Irq_count                 uint64
 	Irq_delay_total           uint64
+	Irq_delay_max             uint64
+	Irq_delay_min             uint64
 }
 
 type cpuMask uint64

vendor/golang.org/x/sys/unix/ztypes_linux_riscv64.go 🔗

@@ -307,10 +307,16 @@ type Taskstats struct {
 	Ac_nice                   uint8
 	Cpu_count                 uint64
 	Cpu_delay_total           uint64
+	Cpu_delay_max             uint64
+	Cpu_delay_min             uint64
 	Blkio_count               uint64
 	Blkio_delay_total         uint64
+	Blkio_delay_max           uint64
+	Blkio_delay_min           uint64
 	Swapin_count              uint64
 	Swapin_delay_total        uint64
+	Swapin_delay_max          uint64
+	Swapin_delay_min          uint64
 	Cpu_run_real_total        uint64
 	Cpu_run_virtual_total     uint64
 	Ac_comm                   [32]uint8
@@ -345,19 +351,29 @@ type Taskstats struct {
 	Cpu_scaled_run_real_total uint64
 	Freepages_count           uint64
 	Freepages_delay_total     uint64
+	Freepages_delay_max       uint64
+	Freepages_delay_min       uint64
 	Thrashing_count           uint64
 	Thrashing_delay_total     uint64
+	Thrashing_delay_max       uint64
+	Thrashing_delay_min       uint64
 	Ac_btime64                uint64
 	Compact_count             uint64
 	Compact_delay_total       uint64
+	Compact_delay_max         uint64
+	Compact_delay_min         uint64
 	Ac_tgid                   uint32
 	Ac_tgetime                uint64
 	Ac_exe_dev                uint64
 	Ac_exe_inode              uint64
 	Wpcopy_count              uint64
 	Wpcopy_delay_total        uint64
+	Wpcopy_delay_max          uint64
+	Wpcopy_delay_min          uint64
 	Irq_count                 uint64
 	Irq_delay_total           uint64
+	Irq_delay_max             uint64
+	Irq_delay_min             uint64
 }
 
 type cpuMask uint64

vendor/golang.org/x/sys/unix/ztypes_linux_s390x.go 🔗

@@ -302,10 +302,16 @@ type Taskstats struct {
 	Ac_nice                   uint8
 	Cpu_count                 uint64
 	Cpu_delay_total           uint64
+	Cpu_delay_max             uint64
+	Cpu_delay_min             uint64
 	Blkio_count               uint64
 	Blkio_delay_total         uint64
+	Blkio_delay_max           uint64
+	Blkio_delay_min           uint64
 	Swapin_count              uint64
 	Swapin_delay_total        uint64
+	Swapin_delay_max          uint64
+	Swapin_delay_min          uint64
 	Cpu_run_real_total        uint64
 	Cpu_run_virtual_total     uint64
 	Ac_comm                   [32]int8
@@ -340,19 +346,29 @@ type Taskstats struct {
 	Cpu_scaled_run_real_total uint64
 	Freepages_count           uint64
 	Freepages_delay_total     uint64
+	Freepages_delay_max       uint64
+	Freepages_delay_min       uint64
 	Thrashing_count           uint64
 	Thrashing_delay_total     uint64
+	Thrashing_delay_max       uint64
+	Thrashing_delay_min       uint64
 	Ac_btime64                uint64
 	Compact_count             uint64
 	Compact_delay_total       uint64
+	Compact_delay_max         uint64
+	Compact_delay_min         uint64
 	Ac_tgid                   uint32
 	Ac_tgetime                uint64
 	Ac_exe_dev                uint64
 	Ac_exe_inode              uint64
 	Wpcopy_count              uint64
 	Wpcopy_delay_total        uint64
+	Wpcopy_delay_max          uint64
+	Wpcopy_delay_min          uint64
 	Irq_count                 uint64
 	Irq_delay_total           uint64
+	Irq_delay_max             uint64
+	Irq_delay_min             uint64
 }
 
 type cpuMask uint64

vendor/golang.org/x/sys/unix/ztypes_linux_sparc64.go 🔗

@@ -284,10 +284,16 @@ type Taskstats struct {
 	Ac_nice                   uint8
 	Cpu_count                 uint64
 	Cpu_delay_total           uint64
+	Cpu_delay_max             uint64
+	Cpu_delay_min             uint64
 	Blkio_count               uint64
 	Blkio_delay_total         uint64
+	Blkio_delay_max           uint64
+	Blkio_delay_min           uint64
 	Swapin_count              uint64
 	Swapin_delay_total        uint64
+	Swapin_delay_max          uint64
+	Swapin_delay_min          uint64
 	Cpu_run_real_total        uint64
 	Cpu_run_virtual_total     uint64
 	Ac_comm                   [32]int8
@@ -322,19 +328,29 @@ type Taskstats struct {
 	Cpu_scaled_run_real_total uint64
 	Freepages_count           uint64
 	Freepages_delay_total     uint64
+	Freepages_delay_max       uint64
+	Freepages_delay_min       uint64
 	Thrashing_count           uint64
 	Thrashing_delay_total     uint64
+	Thrashing_delay_max       uint64
+	Thrashing_delay_min       uint64
 	Ac_btime64                uint64
 	Compact_count             uint64
 	Compact_delay_total       uint64
+	Compact_delay_max         uint64
+	Compact_delay_min         uint64
 	Ac_tgid                   uint32
 	Ac_tgetime                uint64
 	Ac_exe_dev                uint64
 	Ac_exe_inode              uint64
 	Wpcopy_count              uint64
 	Wpcopy_delay_total        uint64
+	Wpcopy_delay_max          uint64
+	Wpcopy_delay_min          uint64
 	Irq_count                 uint64
 	Irq_delay_total           uint64
+	Irq_delay_max             uint64
+	Irq_delay_min             uint64
 }
 
 type cpuMask uint64

vendor/modules.txt 🔗

@@ -229,7 +229,6 @@ github.com/aymanbagabas/go-osc52/v2
 ## explicit; go 1.23
 github.com/aymanbagabas/go-udiff
 github.com/aymanbagabas/go-udiff/lcs
-github.com/aymanbagabas/go-udiff/myers
 # github.com/aymerick/douceur v0.2.0
 ## explicit
 github.com/aymerick/douceur/css
@@ -254,7 +253,7 @@ github.com/charmbracelet/bubbles/v2/spinner
 github.com/charmbracelet/bubbles/v2/textarea
 github.com/charmbracelet/bubbles/v2/textinput
 github.com/charmbracelet/bubbles/v2/viewport
-# github.com/charmbracelet/bubbletea/v2 v2.0.0-beta.1 => github.com/charmbracelet/bubbletea-internal/v2 v2.0.0-20250710185017-3c0ffd25e595
+# github.com/charmbracelet/bubbletea/v2 v2.0.0-beta.1 => github.com/charmbracelet/bubbletea-internal/v2 v2.0.0-20250716142813-5d1379f56ba2
 ## explicit; go 1.24.0
 github.com/charmbracelet/bubbletea/v2
 # github.com/charmbracelet/colorprofile v0.3.1
@@ -269,7 +268,7 @@ github.com/charmbracelet/glamour/v2
 github.com/charmbracelet/glamour/v2/ansi
 github.com/charmbracelet/glamour/v2/internal/autolink
 github.com/charmbracelet/glamour/v2/styles
-# github.com/charmbracelet/lipgloss/v2 v2.0.0-beta.3 => github.com/charmbracelet/lipgloss-internal/v2 v2.0.0-20250710185058-03664cb9cecb
+# github.com/charmbracelet/lipgloss/v2 v2.0.0-beta.3 => github.com/charmbracelet/lipgloss-internal/v2 v2.0.0-20250716143013-48f34cb75679
 ## explicit; go 1.24.2
 github.com/charmbracelet/lipgloss/v2
 github.com/charmbracelet/lipgloss/v2/table
@@ -277,7 +276,7 @@ github.com/charmbracelet/lipgloss/v2/tree
 # github.com/charmbracelet/log/v2 v2.0.0-20250226163916-c379e29ff706
 ## explicit; go 1.19
 github.com/charmbracelet/log/v2
-# github.com/charmbracelet/ultraviolet v0.0.0-20250708152637-0fe0235c8db9
+# github.com/charmbracelet/ultraviolet v0.0.0-20250716141307-2fae2b322afb
 ## explicit; go 1.24.0
 github.com/charmbracelet/ultraviolet
 # github.com/charmbracelet/x/ansi v0.9.3
@@ -682,10 +681,10 @@ golang.org/x/net/idna
 golang.org/x/net/internal/httpcommon
 golang.org/x/net/internal/timeseries
 golang.org/x/net/trace
-# golang.org/x/sync v0.15.0
+# golang.org/x/sync v0.16.0
 ## explicit; go 1.23.0
 golang.org/x/sync/errgroup
-# golang.org/x/sys v0.33.0
+# golang.org/x/sys v0.34.0
 ## explicit; go 1.23.0
 golang.org/x/sys/cpu
 golang.org/x/sys/plan9
@@ -838,5 +837,5 @@ mvdan.cc/sh/v3/fileutil
 mvdan.cc/sh/v3/interp
 mvdan.cc/sh/v3/pattern
 mvdan.cc/sh/v3/syntax
-# github.com/charmbracelet/bubbletea/v2 => github.com/charmbracelet/bubbletea-internal/v2 v2.0.0-20250710185017-3c0ffd25e595
-# github.com/charmbracelet/lipgloss/v2 => github.com/charmbracelet/lipgloss-internal/v2 v2.0.0-20250710185058-03664cb9cecb
+# github.com/charmbracelet/bubbletea/v2 => github.com/charmbracelet/bubbletea-internal/v2 v2.0.0-20250716142813-5d1379f56ba2
+# github.com/charmbracelet/lipgloss/v2 => github.com/charmbracelet/lipgloss-internal/v2 v2.0.0-20250716143013-48f34cb75679