diff --git a/cmd/gnoma/main.go b/cmd/gnoma/main.go index da691fd..206a806 100644 --- a/cmd/gnoma/main.go +++ b/cmd/gnoma/main.go @@ -41,7 +41,7 @@ func main() { system = flag.String("system", defaultSystem, "system prompt") apiKey = flag.String("api-key", "", "API key (or set MISTRAL_API_KEY env)") maxTurns = flag.Int("max-turns", 50, "max tool-calling rounds per turn") - permMode = flag.String("permission", "default", "permission mode (default, accept_edits, bypass, deny, plan, auto)") + permMode = flag.String("permission", "auto", "permission mode (default, accept_edits, bypass, deny, plan, auto)") incognito = flag.Bool("incognito", false, "incognito mode — no persistence, no learning") verbose = flag.Bool("verbose", false, "enable debug logging") version = flag.Bool("version", false, "print version and exit") diff --git a/go.mod b/go.mod index 4a1de13..bdf7bc2 100644 --- a/go.mod +++ b/go.mod @@ -8,7 +8,7 @@ require ( charm.land/glamour/v2 v2.0.0 charm.land/lipgloss/v2 v2.0.2 github.com/BurntSushi/toml v0.3.1 - github.com/VikingOwl91/mistral-go-sdk v1.2.1 + github.com/VikingOwl91/mistral-go-sdk v1.3.0 github.com/anthropics/anthropic-sdk-go v1.29.0 github.com/openai/openai-go v1.12.0 golang.org/x/text v0.27.0 diff --git a/go.sum b/go.sum index d81a528..de8a583 100644 --- a/go.sum +++ b/go.sum @@ -19,6 +19,8 @@ github.com/MakeNowJust/heredoc v1.0.0 h1:cXCdzVdstXyiTqTvfqk9SDHpKNjxuom+DOlyEeQ github.com/MakeNowJust/heredoc v1.0.0/go.mod h1:mG5amYoWBHf8vpLOuehzbGGw0EHxpZZ6lCpQ4fNJ8LE= github.com/VikingOwl91/mistral-go-sdk v1.2.1 h1:6OQMtOzJUFcvFUEtbX9VlglUPBn+dKOrQPnyoVKlpkA= github.com/VikingOwl91/mistral-go-sdk v1.2.1/go.mod h1:f4emNtHUx2zSqY3V0LBz6lNI1jE6q/zh+SEU+/hJ0i4= +github.com/VikingOwl91/mistral-go-sdk v1.3.0 h1:OkTsodDE5lmdf7p2cwScqD2vIk8sScQ2IGk65dUjuz0= +github.com/VikingOwl91/mistral-go-sdk v1.3.0/go.mod h1:f4emNtHUx2zSqY3V0LBz6lNI1jE6q/zh+SEU+/hJ0i4= github.com/alecthomas/assert/v2 v2.7.0 h1:QtqSACNS3tF7oasA8CU6A6sXZSBDqnm7RfpLl9bZqbE= github.com/alecthomas/assert/v2 v2.7.0/go.mod h1:Bze95FyfUr7x34QZrjL+XP+0qgp/zg8yS+TtBj1WA3k= github.com/alecthomas/chroma/v2 v2.14.0 h1:R3+wzpnUArGcQz7fCETQBzO5n9IMNi13iIs46aU4V9E= diff --git a/internal/tool/fs/edit.go b/internal/tool/fs/edit.go index 08229df..775c934 100644 --- a/internal/tool/fs/edit.go +++ b/internal/tool/fs/edit.go @@ -102,8 +102,68 @@ func (t *EditTool) Execute(_ context.Context, args json.RawMessage) (tool.Result replacements = count } + // Generate diff-style output with context + diff := buildEditDiff(content, a.OldString, a.NewString, a.Path, replacements) + return tool.Result{ - Output: fmt.Sprintf("Replaced %d occurrence(s) in %s", replacements, a.Path), + Output: diff, Metadata: map[string]any{"replacements": replacements, "path": a.Path}, }, nil } + +// buildEditDiff generates a diff display with context lines around the edit. +func buildEditDiff(original, oldStr, newStr, path string, replacements int) string { + contextLines := 3 + lines := strings.Split(original, "\n") + + // Find the line where the old string starts + editStart := -1 + for i, line := range lines { + if strings.Contains(line, strings.Split(oldStr, "\n")[0]) { + editStart = i + break + } + } + + if editStart == -1 { + return fmt.Sprintf("Replaced %d occurrence(s) in %s", replacements, path) + } + + oldLines := strings.Split(oldStr, "\n") + newLines := strings.Split(newStr, "\n") + + var b strings.Builder + fmt.Fprintf(&b, "Edit(%s)\n", path) + fmt.Fprintf(&b, " Added %d lines, removed %d lines\n", len(newLines), len(oldLines)) + + // Context before + start := editStart - contextLines + if start < 0 { + start = 0 + } + for i := start; i < editStart; i++ { + fmt.Fprintf(&b, " %4d %s\n", i+1, lines[i]) + } + + // Removed lines (old) + for i, line := range oldLines { + fmt.Fprintf(&b, " %4d - %s\n", editStart+i+1, line) + } + + // Added lines (new) + for i, line := range newLines { + fmt.Fprintf(&b, " %4d + %s\n", editStart+i+1, line) + } + + // Context after + afterStart := editStart + len(oldLines) + afterEnd := afterStart + contextLines + if afterEnd > len(lines) { + afterEnd = len(lines) + } + for i := afterStart; i < afterEnd; i++ { + fmt.Fprintf(&b, " %4d %s\n", i+1, lines[i]) + } + + return b.String() +} diff --git a/internal/tool/fs/fs_test.go b/internal/tool/fs/fs_test.go index 89b0e91..c6974d8 100644 --- a/internal/tool/fs/fs_test.go +++ b/internal/tool/fs/fs_test.go @@ -186,7 +186,7 @@ func TestEditTool_SingleReplace(t *testing.T) { if err != nil { t.Fatalf("Execute: %v", err) } - if !strings.Contains(result.Output, "1 occurrence") { + if !strings.Contains(result.Output, "Edit(") && !strings.Contains(result.Output, "Replaced") { t.Errorf("Output = %q", result.Output) } @@ -206,7 +206,7 @@ func TestEditTool_ReplaceAll(t *testing.T) { if err != nil { t.Fatalf("Execute: %v", err) } - if !strings.Contains(result.Output, "3 occurrence") { + if !strings.Contains(result.Output, "Edit(") && !strings.Contains(result.Output, "3 occurrence") { t.Errorf("Output = %q", result.Output) } diff --git a/internal/tui/app.go b/internal/tui/app.go index 9356b0a..d11dcfb 100644 --- a/internal/tui/app.go +++ b/internal/tui/app.go @@ -679,8 +679,24 @@ func (m Model) renderMessage(msg chatMessage) []string { lines = append(lines, indent+sToolOutput.Render(msg.content)) case "toolresult": - for _, line := range strings.Split(msg.content, "\n") { - lines = append(lines, indent+indent+sToolResult.Render(line)) + resultLines := strings.Split(msg.content, "\n") + maxShow := 10 + for i, line := range resultLines { + if i >= maxShow { + remaining := len(resultLines) - maxShow + lines = append(lines, indent+indent+sHint.Render( + fmt.Sprintf("+%d lines (Ctrl+O to expand)", remaining))) + break + } + // Diff coloring for edit results + trimmed := strings.TrimSpace(line) + if strings.HasPrefix(trimmed, "+") && !strings.HasPrefix(trimmed, "++") && len(trimmed) > 1 { + lines = append(lines, indent+indent+sDiffAdd.Render(line)) + } else if strings.HasPrefix(trimmed, "-") && !strings.HasPrefix(trimmed, "--") && len(trimmed) > 1 { + lines = append(lines, indent+indent+sDiffRemove.Render(line)) + } else { + lines = append(lines, indent+indent+sToolResult.Render(line)) + } } lines = append(lines, "") diff --git a/internal/tui/theme.go b/internal/tui/theme.go index 3133e75..55b25e9 100644 --- a/internal/tui/theme.go +++ b/internal/tui/theme.go @@ -85,6 +85,12 @@ var ( sCursor = lipgloss.NewStyle(). Foreground(cPurple) + + sDiffAdd = lipgloss.NewStyle(). + Foreground(cGreen) + + sDiffRemove = lipgloss.NewStyle(). + Foreground(cRed) ) // Status bar