all: remove anthropic edit tool support

The Anthropic str_replace_editor tool implementation has rotted;
it would require a bit of work to upgrade it for Claude 4.

Remove it for now.

Co-Authored-By: sketch <hello@sketch.dev>
Change-ID: s6129e8703301600dk
diff --git a/claudetool/edit.go b/claudetool/edit.go
deleted file mode 100644
index b539cd6..0000000
--- a/claudetool/edit.go
+++ /dev/null
@@ -1,471 +0,0 @@
-package claudetool
-
-/*
-
-Note: sketch wrote this based on translating https://raw.githubusercontent.com/anthropics/anthropic-quickstarts/refs/heads/main/computer-use-demo/computer_use_demo/tools/edit.py
-
-## Implementation Notes
-This tool is based on Anthropic's Python implementation of the `text_editor_20250124` tool. It maintains a history of file edits to support the undo functionality, and verifies text uniqueness for the str_replace operation to ensure safe edits.
-
-*/
-
-import (
-	"context"
-	"encoding/json"
-	"fmt"
-	"os"
-	"os/exec"
-	"path/filepath"
-	"strings"
-
-	"sketch.dev/llm"
-)
-
-// Constants for the AnthropicEditTool
-const (
-	editName = "str_replace_editor"
-)
-
-// Constants used by the tool
-const (
-	snippetLines     = 4
-	maxResponseLen   = 16000
-	truncatedMessage = "<response clipped><NOTE>To save on context only part of this file has been shown to you. You should retry this tool after you have searched inside the file with `grep -n` in order to find the line numbers of what you are looking for.</NOTE>"
-)
-
-// Command represents the type of operation to perform
-type editCommand string
-
-const (
-	viewCommand       editCommand = "view"
-	createCommand     editCommand = "create"
-	strReplaceCommand editCommand = "str_replace"
-	insertCommand     editCommand = "insert"
-	undoEditCommand   editCommand = "undo_edit"
-)
-
-// editInput represents the expected input format for the edit tool
-type editInput struct {
-	Command    string  `json:"command"`
-	Path       string  `json:"path"`
-	FileText   *string `json:"file_text,omitempty"`
-	ViewRange  []int   `json:"view_range,omitempty"`
-	OldStr     *string `json:"old_str,omitempty"`
-	NewStr     *string `json:"new_str,omitempty"`
-	InsertLine *int    `json:"insert_line,omitempty"`
-}
-
-// fileHistory maintains a history of edits for each file to support undo functionality
-var fileHistory = make(map[string][]string)
-
-// AnthropicEditTool is a tool for viewing, creating, and editing files
-var AnthropicEditTool = &llm.Tool{
-	// Note that Type is model-dependent, and would be different for Claude 3.5, for example.
-	Type: "text_editor_20250124",
-	Name: editName,
-	Run:  EditRun,
-}
-
-// EditRun is the implementation of the edit tool
-func EditRun(ctx context.Context, input json.RawMessage) ([]llm.Content, error) {
-	var editRequest editInput
-	if err := json.Unmarshal(input, &editRequest); err != nil {
-		return nil, fmt.Errorf("failed to parse edit input: %v", err)
-	}
-
-	// Validate the command
-	cmd := editCommand(editRequest.Command)
-	if !isValidCommand(cmd) {
-		return nil, fmt.Errorf("unrecognized command %s. The allowed commands are: view, create, str_replace, insert, undo_edit", cmd)
-	}
-
-	path := editRequest.Path
-
-	// Validate the path
-	if err := validatePath(cmd, path); err != nil {
-		return nil, err
-	}
-
-	// Execute the appropriate command
-	switch cmd {
-	case viewCommand:
-		result, err := handleView(ctx, path, editRequest.ViewRange)
-		if err != nil {
-			return nil, err
-		}
-		return llm.TextContent(result), nil
-	case createCommand:
-		if editRequest.FileText == nil {
-			return nil, fmt.Errorf("parameter file_text is required for command: create")
-		}
-		result, err := handleCreate(path, *editRequest.FileText)
-		if err != nil {
-			return nil, err
-		}
-		return llm.TextContent(result), nil
-	case strReplaceCommand:
-		if editRequest.OldStr == nil {
-			return nil, fmt.Errorf("parameter old_str is required for command: str_replace")
-		}
-		newStr := ""
-		if editRequest.NewStr != nil {
-			newStr = *editRequest.NewStr
-		}
-		result, err := handleStrReplace(path, *editRequest.OldStr, newStr)
-		if err != nil {
-			return nil, err
-		}
-		return llm.TextContent(result), nil
-	case insertCommand:
-		if editRequest.InsertLine == nil {
-			return nil, fmt.Errorf("parameter insert_line is required for command: insert")
-		}
-		if editRequest.NewStr == nil {
-			return nil, fmt.Errorf("parameter new_str is required for command: insert")
-		}
-		result, err := handleInsert(path, *editRequest.InsertLine, *editRequest.NewStr)
-		if err != nil {
-			return nil, err
-		}
-		return llm.TextContent(result), nil
-	case undoEditCommand:
-		result, err := handleUndoEdit(path)
-		if err != nil {
-			return nil, err
-		}
-		return llm.TextContent(result), nil
-	default:
-		return nil, fmt.Errorf("command %s is not implemented", cmd)
-	}
-}
-
-// Utility function to check if a command is valid
-func isValidCommand(cmd editCommand) bool {
-	switch cmd {
-	case viewCommand, createCommand, strReplaceCommand, insertCommand, undoEditCommand:
-		return true
-	default:
-		return false
-	}
-}
-
-// validatePath checks if the path/command combination is valid
-func validatePath(cmd editCommand, path string) error {
-	// Check if it's an absolute path
-	if !filepath.IsAbs(path) {
-		suggestedPath := "/" + path
-		return fmt.Errorf("the path %s is not an absolute path, it should start with '/'. Maybe you meant %s?", path, suggestedPath)
-	}
-
-	// Get file info
-	info, err := os.Stat(path)
-
-	// Check if path exists (except for create command)
-	if err != nil {
-		if os.IsNotExist(err) && cmd != createCommand {
-			return fmt.Errorf("the path %s does not exist. Please provide a valid path", path)
-		} else if !os.IsNotExist(err) {
-			return fmt.Errorf("error accessing path %s: %v", path, err)
-		}
-	} else {
-		// Path exists, check if it's a directory
-		if info.IsDir() && cmd != viewCommand {
-			return fmt.Errorf("the path %s is a directory and only the 'view' command can be used on directories", path)
-		}
-
-		// For create command, check if file already exists
-		if cmd == createCommand {
-			return fmt.Errorf("file already exists at: %s. Cannot overwrite files using command 'create'", path)
-		}
-	}
-
-	return nil
-}
-
-// handleView implements the view command
-func handleView(ctx context.Context, path string, viewRange []int) (string, error) {
-	info, err := os.Stat(path)
-	if err != nil {
-		return "", fmt.Errorf("error accessing path %s: %v", path, err)
-	}
-
-	// Handle directory view
-	if info.IsDir() {
-		if viewRange != nil {
-			return "", fmt.Errorf("the view_range parameter is not allowed when path points to a directory")
-		}
-
-		// List files in the directory (up to 2 levels deep)
-		return listDirectory(ctx, path)
-	}
-
-	// Handle file view
-	fileContent, err := readFile(path)
-	if err != nil {
-		return "", err
-	}
-
-	initLine := 1
-	if viewRange != nil {
-		if len(viewRange) != 2 {
-			return "", fmt.Errorf("invalid view_range. It should be a list of two integers")
-		}
-
-		fileLines := strings.Split(fileContent, "\n")
-		nLinesFile := len(fileLines)
-		initLine, finalLine := viewRange[0], viewRange[1]
-
-		if initLine < 1 || initLine > nLinesFile {
-			return "", fmt.Errorf("invalid view_range: %v. Its first element %d should be within the range of lines of the file: [1, %d]",
-				viewRange, initLine, nLinesFile)
-		}
-
-		if finalLine != -1 && finalLine < initLine {
-			return "", fmt.Errorf("invalid view_range: %v. Its second element %d should be larger or equal than its first %d",
-				viewRange, finalLine, initLine)
-		}
-
-		if finalLine > nLinesFile {
-			return "", fmt.Errorf("invalid view_range: %v. Its second element %d should be smaller than the number of lines in the file: %d",
-				viewRange, finalLine, nLinesFile)
-		}
-
-		if finalLine == -1 {
-			fileContent = strings.Join(fileLines[initLine-1:], "\n")
-		} else {
-			fileContent = strings.Join(fileLines[initLine-1:finalLine], "\n")
-		}
-	}
-
-	return makeOutput(fileContent, path, initLine), nil
-}
-
-// handleCreate implements the create command
-func handleCreate(path string, fileText string) (string, error) {
-	// Ensure the directory exists
-	dir := filepath.Dir(path)
-	if err := os.MkdirAll(dir, 0o755); err != nil {
-		return "", fmt.Errorf("failed to create directory %s: %v", dir, err)
-	}
-
-	// Write the file
-	if err := writeFile(path, fileText); err != nil {
-		return "", err
-	}
-
-	// Save to history
-	fileHistory[path] = append(fileHistory[path], fileText)
-
-	return fmt.Sprintf("File created successfully at: %s", path), nil
-}
-
-// handleStrReplace implements the str_replace command
-func handleStrReplace(path, oldStr, newStr string) (string, error) {
-	// Read the file content
-	fileContent, err := readFile(path)
-	if err != nil {
-		return "", err
-	}
-
-	// Replace tabs with spaces
-	fileContent = maybeExpandTabs(path, fileContent)
-	oldStr = maybeExpandTabs(path, oldStr)
-	newStr = maybeExpandTabs(path, newStr)
-
-	// Check if oldStr is unique in the file
-	occurrences := strings.Count(fileContent, oldStr)
-	if occurrences == 0 {
-		return "", fmt.Errorf("no replacement was performed, old_str %q did not appear verbatim in %s", oldStr, path)
-	} else if occurrences > 1 {
-		// Find line numbers where oldStr appears
-		fileContentLines := strings.Split(fileContent, "\n")
-		var lines []int
-		for idx, line := range fileContentLines {
-			if strings.Contains(line, oldStr) {
-				lines = append(lines, idx+1)
-			}
-		}
-		return "", fmt.Errorf("no replacement was performed. Multiple occurrences of old_str %q in lines %v. Please ensure it is unique", oldStr, lines)
-	}
-
-	// Save the current content to history
-	fileHistory[path] = append(fileHistory[path], fileContent)
-
-	// Replace oldStr with newStr
-	newFileContent := strings.Replace(fileContent, oldStr, newStr, 1)
-
-	// Write the new content to the file
-	if err := writeFile(path, newFileContent); err != nil {
-		return "", err
-	}
-
-	// Create a snippet of the edited section
-	parts := strings.Split(fileContent, oldStr)
-	if len(parts) == 0 {
-		// This should never happen due to the earlier check, but let's be safe
-		parts = []string{""}
-	}
-	replacementLine := strings.Count(parts[0], "\n")
-	startLine := max(0, replacementLine-snippetLines)
-	endLine := replacementLine + snippetLines + strings.Count(newStr, "\n")
-	fileLines := strings.Split(newFileContent, "\n")
-	if len(fileLines) == 0 {
-		fileLines = []string{""}
-	}
-	endLine = min(endLine+1, len(fileLines))
-	snippet := strings.Join(fileLines[startLine:endLine], "\n")
-
-	// Prepare the success message
-	successMsg := fmt.Sprintf("The file %s has been edited. ", path)
-	successMsg += makeOutput(snippet, fmt.Sprintf("a snippet of %s", path), startLine+1)
-	successMsg += "Review the changes and make sure they are as expected. Edit the file again if necessary."
-
-	return successMsg, nil
-}
-
-// handleInsert implements the insert command
-func handleInsert(path string, insertLine int, newStr string) (string, error) {
-	// Read the file content
-	fileContent, err := readFile(path)
-	if err != nil {
-		return "", err
-	}
-
-	// Replace tabs with spaces
-	fileContent = maybeExpandTabs(path, fileContent)
-	newStr = maybeExpandTabs(path, newStr)
-
-	// Split the file content into lines
-	fileTextLines := strings.Split(fileContent, "\n")
-	nLinesFile := len(fileTextLines)
-
-	// Validate insert line
-	if insertLine < 0 || insertLine > nLinesFile {
-		return "", fmt.Errorf("invalid insert_line parameter: %d. It should be within the range of lines of the file: [0, %d]",
-			insertLine, nLinesFile)
-	}
-
-	// Save the current content to history
-	fileHistory[path] = append(fileHistory[path], fileContent)
-
-	// Split the new string into lines
-	newStrLines := strings.Split(newStr, "\n")
-
-	// Create new content by inserting the new lines
-	newFileTextLines := make([]string, 0, nLinesFile+len(newStrLines))
-	newFileTextLines = append(newFileTextLines, fileTextLines[:insertLine]...)
-	newFileTextLines = append(newFileTextLines, newStrLines...)
-	newFileTextLines = append(newFileTextLines, fileTextLines[insertLine:]...)
-
-	// Create a snippet of the edited section
-	snippetStart := max(0, insertLine-snippetLines)
-	snippetEnd := min(insertLine+snippetLines, nLinesFile)
-
-	snippetLines := make([]string, 0)
-	snippetLines = append(snippetLines, fileTextLines[snippetStart:insertLine]...)
-	snippetLines = append(snippetLines, newStrLines...)
-	snippetLines = append(snippetLines, fileTextLines[insertLine:snippetEnd]...)
-	snippet := strings.Join(snippetLines, "\n")
-
-	// Write the new content to the file
-	newFileText := strings.Join(newFileTextLines, "\n")
-	if err := writeFile(path, newFileText); err != nil {
-		return "", err
-	}
-
-	// Prepare the success message
-	successMsg := fmt.Sprintf("The file %s has been edited. ", path)
-	successMsg += makeOutput(snippet, "a snippet of the edited file", max(1, insertLine-4+1))
-	successMsg += "Review the changes and make sure they are as expected (correct indentation, no duplicate lines, etc). Edit the file again if necessary."
-
-	return successMsg, nil
-}
-
-// handleUndoEdit implements the undo_edit command
-func handleUndoEdit(path string) (string, error) {
-	history, exists := fileHistory[path]
-	if !exists || len(history) == 0 {
-		return "", fmt.Errorf("no edit history found for %s", path)
-	}
-
-	// Get the last edit and remove it from history
-	lastIdx := len(history) - 1
-	oldText := history[lastIdx]
-	fileHistory[path] = history[:lastIdx]
-
-	// Write the old content back to the file
-	if err := writeFile(path, oldText); err != nil {
-		return "", err
-	}
-
-	return fmt.Sprintf("Last edit to %s undone successfully. %s", path, makeOutput(oldText, path, 1)), nil
-}
-
-// listDirectory lists files and directories up to 2 levels deep
-func listDirectory(ctx context.Context, path string) (string, error) {
-	cmd := fmt.Sprintf("find %s -maxdepth 2 -not -path '*/\\.*'", path)
-	output, err := executeCommand(ctx, cmd)
-	if err != nil {
-		return "", fmt.Errorf("failed to list directory: %v", err)
-	}
-
-	return fmt.Sprintf("Here's the files and directories up to 2 levels deep in %s, excluding hidden items:\n%s\n", path, output), nil
-}
-
-// executeCommand executes a shell command and returns its output
-func executeCommand(ctx context.Context, cmd string) (string, error) {
-	// This is a simplified version without timeouts for now
-	bash := exec.CommandContext(ctx, "bash", "-c", cmd)
-	bash.Dir = WorkingDir(ctx)
-	output, err := bash.CombinedOutput()
-	if err != nil {
-		return "", fmt.Errorf("command execution failed: %v: %s", err, string(output))
-	}
-	return maybetruncate(string(output)), nil
-}
-
-// readFile reads the content of a file
-func readFile(path string) (string, error) {
-	content, err := os.ReadFile(path)
-	if err != nil {
-		return "", fmt.Errorf("failed to read file %s: %v", path, err)
-	}
-	return string(content), nil
-}
-
-// writeFile writes content to a file
-func writeFile(path, content string) error {
-	if err := os.WriteFile(path, []byte(content), 0o644); err != nil {
-		return fmt.Errorf("failed to write to file %s: %v", path, err)
-	}
-	return nil
-}
-
-// makeOutput generates a formatted output for the CLI
-func makeOutput(fileContent, fileDescriptor string, initLine int) string {
-	fileContent = maybetruncate(fileContent)
-	fileContent = maybeExpandTabs(fileDescriptor, fileContent)
-
-	var lines []string
-	for i, line := range strings.Split(fileContent, "\n") {
-		lines = append(lines, fmt.Sprintf("%6d\t%s", i+initLine, line))
-	}
-
-	return fmt.Sprintf("Here's the result of running `cat -n` on %s:\n%s\n", fileDescriptor, strings.Join(lines, "\n"))
-}
-
-// maybetruncate truncates content and appends a notice if content exceeds the specified length
-func maybetruncate(content string) string {
-	if len(content) <= maxResponseLen {
-		return content
-	}
-	return content[:maxResponseLen] + truncatedMessage
-}
-
-// maybeExpandTabs is currently a no-op. The python
-// implementation replaces tabs with spaces, but this strikes
-// me as unwise for our tool.
-func maybeExpandTabs(path, s string) string {
-	// return strings.ReplaceAll(s, "\t", "    ")
-	return s
-}
diff --git a/claudetool/edit_regression_test.go b/claudetool/edit_regression_test.go
deleted file mode 100644
index cb859fe..0000000
--- a/claudetool/edit_regression_test.go
+++ /dev/null
@@ -1,152 +0,0 @@
-package claudetool
-
-import (
-	"context"
-	"encoding/json"
-	"strings"
-	"testing"
-)
-
-// TestEmptyContentHandling tests handling of empty content in str_replace and related operations
-// This test specifically reproduces conditions that might lead to "index out of range [0]" panic
-func TestEmptyContentHandling(t *testing.T) {
-	// Create a file with empty content
-	emptyFile := setupTestFile(t, "")
-
-	// Test running EditRun directly with empty content
-	// This more closely simulates the actual call flow that led to the panic
-	input := map[string]any{
-		"command": "str_replace",
-		"path":    emptyFile,
-		"old_str": "nonexistent text",
-		"new_str": "new content",
-	}
-
-	inputJSON, err := json.Marshal(input)
-	if err != nil {
-		t.Fatalf("Failed to marshal input: %v", err)
-	}
-
-	// This should not panic but return an error
-	_, err = EditRun(context.Background(), inputJSON)
-	if err == nil {
-		t.Fatalf("Expected error for empty file with str_replace but got none")
-	}
-
-	// Make sure the error message is as expected
-	if !strings.Contains(err.Error(), "did not appear verbatim") {
-		t.Errorf("Expected error message to indicate missing string, got: %s", err.Error())
-	}
-}
-
-// TestNilParameterHandling tests error cases with nil parameters
-// This test validates proper error handling when nil or invalid parameters are provided
-func TestNilParameterHandling(t *testing.T) {
-	// Create a test file
-	testFile := setupTestFile(t, "test content")
-
-	// Test case 1: nil old_str in str_replace
-	input1 := map[string]any{
-		"command": "str_replace",
-		"path":    testFile,
-		// old_str is deliberately missing
-		"new_str": "replacement",
-	}
-
-	inputJSON1, err := json.Marshal(input1)
-	if err != nil {
-		t.Fatalf("Failed to marshal input: %v", err)
-	}
-
-	_, err = EditRun(context.Background(), inputJSON1)
-	if err == nil {
-		t.Fatalf("Expected error for missing old_str but got none")
-	}
-	if !strings.Contains(err.Error(), "parameter old_str is required") {
-		t.Errorf("Expected error message to indicate missing old_str, got: %s", err.Error())
-	}
-
-	// Test case 2: nil new_str in insert
-	input2 := map[string]any{
-		"command":     "insert",
-		"path":        testFile,
-		"insert_line": 1,
-		// new_str is deliberately missing
-	}
-
-	inputJSON2, err := json.Marshal(input2)
-	if err != nil {
-		t.Fatalf("Failed to marshal input: %v", err)
-	}
-
-	_, err = EditRun(context.Background(), inputJSON2)
-	if err == nil {
-		t.Fatalf("Expected error for missing new_str but got none")
-	}
-	if !strings.Contains(err.Error(), "parameter new_str is required") {
-		t.Errorf("Expected error message to indicate missing new_str, got: %s", err.Error())
-	}
-
-	// Test case 3: nil view_range in view
-	// This doesn't cause an error, but tests the code path
-	input3 := map[string]any{
-		"command": "view",
-		"path":    testFile,
-		// No view_range
-	}
-
-	inputJSON3, err := json.Marshal(input3)
-	if err != nil {
-		t.Fatalf("Failed to marshal input: %v", err)
-	}
-
-	// This should not result in an error
-	_, err = EditRun(context.Background(), inputJSON3)
-	if err != nil {
-		t.Fatalf("Unexpected error for nil view_range: %v", err)
-	}
-}
-
-// TestEmptySplitResult tests the specific scenario where strings.Split might return empty results
-// This directly reproduces conditions that might have led to the "index out of range [0]" panic
-func TestEmptySplitResult(t *testing.T) {
-	// Direct test of strings.Split behavior and our handling of it
-	emptyCases := []struct {
-		content string
-		oldStr  string
-	}{
-		{"", "any string"},
-		{"content", "not in string"},
-		{"\n\n", "also not here"},
-	}
-
-	for _, tc := range emptyCases {
-		parts := strings.Split(tc.content, tc.oldStr)
-
-		// Verify that strings.Split with non-matching separator returns a slice with original content
-		if len(parts) != 1 {
-			t.Errorf("Expected strings.Split to return a slice with 1 element when separator isn't found, got %d elements", len(parts))
-		}
-
-		// Double check the content
-		if len(parts) > 0 && parts[0] != tc.content {
-			t.Errorf("Expected parts[0] to be original content %q, got %q", tc.content, parts[0])
-		}
-	}
-
-	// Test the actual unsafe scenario with empty content
-	emptyFile := setupTestFile(t, "")
-
-	// Get the content and simulate the internal string splitting
-	content, _ := readFile(emptyFile)
-	oldStr := "nonexistent"
-	parts := strings.Split(content, oldStr)
-
-	// Validate that the defensive code would work
-	if len(parts) == 0 {
-		parts = []string{""} // This is the fix
-	}
-
-	// This would have panicked without the fix
-	_ = strings.Count(parts[0], "\n")
-}
diff --git a/claudetool/edit_test.go b/claudetool/edit_test.go
deleted file mode 100644
index ab687fa..0000000
--- a/claudetool/edit_test.go
+++ /dev/null
@@ -1,399 +0,0 @@
-package claudetool
-
-import (
-	"context"
-	"encoding/json"
-	"os"
-	"path/filepath"
-	"strings"
-	"testing"
-)
-
-// setupTestFile creates a temporary file with given content for testing
-func setupTestFile(t *testing.T, content string) string {
-	t.Helper()
-
-	// Create a temporary directory
-	tempDir, err := os.MkdirTemp("", "anthropic_edit_test_*")
-	if err != nil {
-		t.Fatalf("Failed to create temp directory: %v", err)
-	}
-
-	// Create a test file in the temp directory
-	testFile := filepath.Join(tempDir, "test_file.txt")
-	if err := os.WriteFile(testFile, []byte(content), 0o644); err != nil {
-		os.RemoveAll(tempDir)
-		t.Fatalf("Failed to write test file: %v", err)
-	}
-
-	// Register cleanup function
-	t.Cleanup(func() {
-		os.RemoveAll(tempDir)
-	})
-
-	return testFile
-}
-
-// callEditTool is a helper to call the edit tool with specific parameters
-func callEditTool(t *testing.T, input map[string]any) string {
-	t.Helper()
-
-	// Convert input to JSON
-	inputJSON, err := json.Marshal(input)
-	if err != nil {
-		t.Fatalf("Failed to marshal input: %v", err)
-	}
-
-	// Call the tool
-	result, err := EditRun(context.Background(), inputJSON)
-	if err != nil {
-		t.Fatalf("Tool execution failed: %v", err)
-	}
-
-	return ContentToString(result)
-}
-
-// TestEditToolView tests the view command functionality
-func TestEditToolView(t *testing.T) {
-	content := "Line 1\nLine 2\nLine 3\nLine 4\nLine 5"
-	testFile := setupTestFile(t, content)
-
-	// Test the view command
-	result := callEditTool(t, map[string]any{
-		"command": "view",
-		"path":    testFile,
-	})
-
-	// Verify results
-	if !strings.Contains(result, "Line 1") {
-		t.Errorf("View result should contain the file content, got: %s", result)
-	}
-
-	// Test view with range
-	result = callEditTool(t, map[string]any{
-		"command":    "view",
-		"path":       testFile,
-		"view_range": []int{2, 4},
-	})
-
-	// Verify range results
-	if strings.Contains(result, "Line 1") || !strings.Contains(result, "Line 2") {
-		t.Errorf("View with range should show only specified lines, got: %s", result)
-	}
-}
-
-// TestEditToolStrReplace tests the str_replace command functionality
-func TestEditToolStrReplace(t *testing.T) {
-	content := "Line 1\nLine 2\nLine 3\nLine 4\nLine 5"
-	testFile := setupTestFile(t, content)
-
-	// Test the str_replace command
-	result := callEditTool(t, map[string]any{
-		"command": "str_replace",
-		"path":    testFile,
-		"old_str": "Line 3",
-		"new_str": "Modified Line 3",
-	})
-
-	// Verify the file was modified
-	modifiedContent, err := os.ReadFile(testFile)
-	if err != nil {
-		t.Fatalf("Failed to read test file: %v", err)
-	}
-
-	if !strings.Contains(string(modifiedContent), "Modified Line 3") {
-		t.Errorf("File content should be modified, got: %s", string(modifiedContent))
-	}
-
-	// Verify the result contains a snippet
-	if !strings.Contains(result, "Modified Line 3") {
-		t.Errorf("Result should contain the modified content, got: %s", result)
-	}
-}
-
-// TestEditToolInsert tests the insert command functionality
-func TestEditToolInsert(t *testing.T) {
-	content := "Line 1\nLine 2\nLine 3\nLine 4\nLine 5"
-	testFile := setupTestFile(t, content)
-
-	// Test the insert command
-	result := callEditTool(t, map[string]any{
-		"command":     "insert",
-		"path":        testFile,
-		"insert_line": 2,
-		"new_str":     "Inserted Line",
-	})
-
-	// Verify the file was modified
-	modifiedContent, err := os.ReadFile(testFile)
-	if err != nil {
-		t.Fatalf("Failed to read test file: %v", err)
-	}
-
-	expected := "Line 1\nLine 2\nInserted Line\nLine 3\nLine 4\nLine 5"
-	if string(modifiedContent) != expected {
-		t.Errorf("File content incorrect after insert. Expected:\n%s\nGot:\n%s", expected, string(modifiedContent))
-	}
-
-	// Verify the result contains a snippet
-	if !strings.Contains(result, "Inserted Line") {
-		t.Errorf("Result should contain the inserted content, got: %s", result)
-	}
-}
-
-// TestEditToolCreate tests the create command functionality
-func TestEditToolCreate(t *testing.T) {
-	tempDir, err := os.MkdirTemp("", "anthropic_edit_test_create_*")
-	if err != nil {
-		t.Fatalf("Failed to create temp directory: %v", err)
-	}
-
-	t.Cleanup(func() {
-		os.RemoveAll(tempDir)
-	})
-
-	newFilePath := filepath.Join(tempDir, "new_file.txt")
-	content := "This is a new file\nWith multiple lines"
-
-	// Test the create command
-	result := callEditTool(t, map[string]any{
-		"command":   "create",
-		"path":      newFilePath,
-		"file_text": content,
-	})
-
-	// Verify the file was created with the right content
-	createdContent, err := os.ReadFile(newFilePath)
-	if err != nil {
-		t.Fatalf("Failed to read created file: %v", err)
-	}
-
-	if string(createdContent) != content {
-		t.Errorf("Created file content incorrect. Expected:\n%s\nGot:\n%s", content, string(createdContent))
-	}
-
-	// Verify the result message
-	if !strings.Contains(result, "File created successfully") {
-		t.Errorf("Result should confirm file creation, got: %s", result)
-	}
-}
-
-// TestEditToolUndoEdit tests the undo_edit command functionality
-func TestEditToolUndoEdit(t *testing.T) {
-	originalContent := "Line 1\nLine 2\nLine 3\nLine 4\nLine 5"
-	testFile := setupTestFile(t, originalContent)
-
-	// First modify the file
-	callEditTool(t, map[string]any{
-		"command": "str_replace",
-		"path":    testFile,
-		"old_str": "Line 3",
-		"new_str": "Modified Line 3",
-	})
-
-	// Then undo the edit
-	result := callEditTool(t, map[string]any{
-		"command": "undo_edit",
-		"path":    testFile,
-	})
-
-	// Verify the file was restored to original content
-	restoredContent, err := os.ReadFile(testFile)
-	if err != nil {
-		t.Fatalf("Failed to read test file: %v", err)
-	}
-
-	if string(restoredContent) != originalContent {
-		t.Errorf("File content should be restored to original, got: %s", string(restoredContent))
-	}
-
-	// Verify the result message
-	if !strings.Contains(result, "undone successfully") {
-		t.Errorf("Result should confirm undo operation, got: %s", result)
-	}
-}
-
-// TestEditToolErrors tests various error conditions
-func TestEditToolErrors(t *testing.T) {
-	content := "Line 1\nLine 2\nLine 3\nLine 4\nLine 5"
-	testFile := setupTestFile(t, content)
-
-	testCases := []struct {
-		name   string
-		input  map[string]any
-		errMsg string
-	}{
-		{
-			name: "Invalid command",
-			input: map[string]any{
-				"command": "invalid_command",
-				"path":    testFile,
-			},
-			errMsg: "unrecognized command",
-		},
-		{
-			name: "Non-existent file",
-			input: map[string]any{
-				"command": "view",
-				"path":    "/non/existent/file.txt",
-			},
-			errMsg: "does not exist",
-		},
-		{
-			name: "Missing required parameter",
-			input: map[string]any{
-				"command": "str_replace",
-				"path":    testFile,
-				// Missing old_str
-			},
-			errMsg: "parameter old_str is required",
-		},
-		{
-			name: "Multiple occurrences in str_replace",
-			input: map[string]any{
-				"command": "str_replace",
-				"path":    testFile,
-				"old_str": "Line", // Appears multiple times
-				"new_str": "Modified Line",
-			},
-			errMsg: "Multiple occurrences",
-		},
-		{
-			name: "Invalid view range",
-			input: map[string]any{
-				"command":    "view",
-				"path":       testFile,
-				"view_range": []int{10, 20}, // Out of range
-			},
-			errMsg: "invalid view_range",
-		},
-	}
-
-	for _, tc := range testCases {
-		t.Run(tc.name, func(t *testing.T) {
-			inputJSON, err := json.Marshal(tc.input)
-			if err != nil {
-				t.Fatalf("Failed to marshal input: %v", err)
-			}
-
-			_, err = EditRun(context.Background(), inputJSON)
-			if err == nil {
-				t.Fatalf("Expected error but got none")
-			}
-
-			if !strings.Contains(err.Error(), tc.errMsg) {
-				t.Errorf("Error message does not contain expected text. Expected to contain: %q, Got: %q", tc.errMsg, err.Error())
-			}
-		})
-	}
-}
-
-// TestHandleStrReplaceEdgeCases tests the handleStrReplace function specifically for edge cases
-// that could cause panics like "index out of range [0] with length 0"
-func TestHandleStrReplaceEdgeCases(t *testing.T) {
-	// The issue was with strings.Split returning an empty slice when the separator wasn't found
-	// This test directly tests the internal implementation with conditions that might cause this
-
-	// Create a test file with empty content
-	emptyFile := setupTestFile(t, "")
-
-	// Test with empty file content and arbitrary oldStr
-	_, err := handleStrReplace(emptyFile, "some string that doesn't exist", "new content")
-	if err == nil {
-		t.Fatal("Expected error for empty file but got none")
-	}
-	if !strings.Contains(err.Error(), "did not appear verbatim") {
-		t.Errorf("Expected error message to indicate missing string, got: %s", err.Error())
-	}
-
-	// Create a file with content that doesn't match oldStr
-	nonMatchingFile := setupTestFile(t, "This is some content\nthat doesn't contain the target string")
-
-	// Test with content that doesn't contain oldStr
-	_, err = handleStrReplace(nonMatchingFile, "target string not present", "replacement")
-	if err == nil {
-		t.Fatal("Expected error for non-matching content but got none")
-	}
-	if !strings.Contains(err.Error(), "did not appear verbatim") {
-		t.Errorf("Expected error message to indicate missing string, got: %s", err.Error())
-	}
-
-	// Test handling of the edge case that could potentially cause the "index out of range" panic
-	// This directly verifies that the handleStrReplace function properly handles the case where
-	// strings.Split returns an empty or unexpected result
-
-	// Verify that the protection against empty parts slice works
-	fileContent := ""
-	oldStr := "some string"
-	parts := strings.Split(fileContent, oldStr)
-	if len(parts) == 0 {
-		// This should match the protection in the code
-		parts = []string{""}
-	}
-
-	// This should not panic with the fix in place
-	_ = strings.Count(parts[0], "\n") // This line would have panicked without the fix
-}
-
-// TestViewRangeWithStrReplace tests that the view_range parameter works correctly
-// with the str_replace command (tests the full workflow)
-func TestViewRangeWithStrReplace(t *testing.T) {
-	// Create test file with multiple lines
-	content := "Line 1: First line\nLine 2: Second line\nLine 3: Third line\nLine 4: Fourth line\nLine 5: Fifth line"
-	testFile := setupTestFile(t, content)
-
-	// First view a subset of the file using view_range
-	viewResult := callEditTool(t, map[string]any{
-		"command":    "view",
-		"path":       testFile,
-		"view_range": []int{2, 4}, // Only lines 2-4
-	})
-
-	// Verify that we only see the specified lines
-	if strings.Contains(viewResult, "Line 1:") || strings.Contains(viewResult, "Line 5:") {
-		t.Errorf("View with range should only show lines 2-4, got: %s", viewResult)
-	}
-	if !strings.Contains(viewResult, "Line 2:") || !strings.Contains(viewResult, "Line 4:") {
-		t.Errorf("View with range should show lines 2-4, got: %s", viewResult)
-	}
-
-	// Now perform a str_replace on one of the lines we viewed
-	replaceResult := callEditTool(t, map[string]any{
-		"command": "str_replace",
-		"path":    testFile,
-		"old_str": "Line 3: Third line",
-		"new_str": "Line 3: MODIFIED Third line",
-	})
-
-	// Check that the replacement was successful
-	if !strings.Contains(replaceResult, "Line 3: MODIFIED Third line") {
-		t.Errorf("Replace result should contain the modified line, got: %s", replaceResult)
-	}
-
-	// Verify the file content was updated correctly
-	modifiedContent, err := os.ReadFile(testFile)
-	if err != nil {
-		t.Fatalf("Failed to read test file after modification: %v", err)
-	}
-
-	expectedContent := "Line 1: First line\nLine 2: Second line\nLine 3: MODIFIED Third line\nLine 4: Fourth line\nLine 5: Fifth line"
-	if string(modifiedContent) != expectedContent {
-		t.Errorf("File content after replacement is incorrect.\nExpected:\n%s\nGot:\n%s",
-			expectedContent, string(modifiedContent))
-	}
-
-	// View the modified file with a different view_range
-	finalViewResult := callEditTool(t, map[string]any{
-		"command":    "view",
-		"path":       testFile,
-		"view_range": []int{3, 3}, // Only the modified line
-	})
-
-	// Verify we can see only the modified line
-	if !strings.Contains(finalViewResult, "Line 3: MODIFIED Third line") {
-		t.Errorf("Final view should show the modified line, got: %s", finalViewResult)
-	}
-	if strings.Contains(finalViewResult, "Line 2:") || strings.Contains(finalViewResult, "Line 4:") {
-		t.Errorf("Final view should only show line 3, got: %s", finalViewResult)
-	}
-}
diff --git a/cmd/sketch/main.go b/cmd/sketch/main.go
index 775da54..786d376 100644
--- a/cmd/sketch/main.go
+++ b/cmd/sketch/main.go
@@ -429,15 +429,14 @@
 	}
 
 	agentConfig := loop.AgentConfig{
-		Context:           ctx,
-		Service:           llmService,
-		Budget:            budget,
-		GitUsername:       flags.gitUsername,
-		GitEmail:          flags.gitEmail,
-		SessionID:         flags.sessionID,
-		ClientGOOS:        runtime.GOOS,
-		ClientGOARCH:      runtime.GOARCH,
-		UseAnthropicEdit:  os.Getenv("SKETCH_ANTHROPIC_EDIT") == "1",
+		Context:      ctx,
+		Service:      llmService,
+		Budget:       budget,
+		GitUsername:  flags.gitUsername,
+		GitEmail:     flags.gitEmail,
+		SessionID:    flags.sessionID,
+		ClientGOOS:   runtime.GOOS,
+		ClientGOARCH: runtime.GOARCH,
 		OutsideHostname:   flags.outsideHostname,
 		OutsideOS:         flags.outsideOS,
 		OutsideWorkingDir: flags.outsideWorkingDir,
diff --git a/loop/agent.go b/loop/agent.go
index ce7d8e3..1527681 100644
--- a/loop/agent.go
+++ b/loop/agent.go
@@ -770,18 +770,17 @@
 
 // AgentConfig contains configuration for creating a new Agent.
 type AgentConfig struct {
-	Context          context.Context
-	Service          llm.Service
-	Budget           conversation.Budget
-	GitUsername      string
-	GitEmail         string
-	SessionID        string
-	ClientGOOS       string
-	ClientGOARCH     string
-	InDocker         bool
-	UseAnthropicEdit bool
-	OneShot          bool
-	WorkingDir       string
+	Context      context.Context
+	Service      llm.Service
+	Budget       conversation.Budget
+	GitUsername  string
+	GitEmail     string
+	SessionID    string
+	ClientGOOS   string
+	ClientGOARCH string
+	InDocker     bool
+	OneShot      bool
+	WorkingDir   string
 	// Outside information
 	OutsideHostname   string
 	OutsideOS         string
@@ -1002,7 +1001,7 @@
 	browserTools = bTools
 
 	convo.Tools = []*llm.Tool{
-		bashTool, claudetool.Keyword,
+		bashTool, claudetool.Keyword, claudetool.Patch,
 		claudetool.Think, claudetool.TodoRead, claudetool.TodoWrite, a.titleTool(), a.precommitTool(), makeDoneTool(a.codereview),
 		a.codereview.Tool(), claudetool.AboutSketch,
 	}
@@ -1013,11 +1012,6 @@
 	}
 
 	convo.Tools = append(convo.Tools, browserTools...)
-	if a.config.UseAnthropicEdit {
-		convo.Tools = append(convo.Tools, claudetool.AnthropicEditTool)
-	} else {
-		convo.Tools = append(convo.Tools, claudetool.Patch)
-	}
 	convo.Listener = a
 	return convo
 }
@@ -1916,7 +1910,6 @@
 
 // systemPromptData contains the data used to render the system prompt template
 type systemPromptData struct {
-	EditPrompt    string
 	ClientGOOS    string
 	ClientGOARCH  string
 	WorkingDir    string
@@ -1927,16 +1920,7 @@
 
 // renderSystemPrompt renders the system prompt template.
 func (a *Agent) renderSystemPrompt() string {
-	// Determine the appropriate edit prompt based on config
-	var editPrompt string
-	if a.config.UseAnthropicEdit {
-		editPrompt = "Then use the str_replace_editor tool to make those edits. For short complete file replacements, you may use the bash tool with cat and heredoc stdin."
-	} else {
-		editPrompt = "Then use the patch tool to make those edits. Combine all edits to any given file into a single patch tool call."
-	}
-
 	data := systemPromptData{
-		EditPrompt:    editPrompt,
 		ClientGOOS:    a.config.ClientGOOS,
 		ClientGOARCH:  a.config.ClientGOARCH,
 		WorkingDir:    a.workingDir,
diff --git a/loop/agent_system_prompt.txt b/loop/agent_system_prompt.txt
index 3831e98..cc4ac06 100644
--- a/loop/agent_system_prompt.txt
+++ b/loop/agent_system_prompt.txt
@@ -24,7 +24,7 @@
 
 To make edits reliably and efficiently, first think about the intent of the edit,
 and what set of patches will achieve that intent.
-{{.EditPrompt}}
+Then use the patch tool to make those edits. Combine all edits to any given file into a single patch tool call.
 
 Complete every task exhaustively - no matter how repetitive or tedious.
 Partial work, pattern demonstrations, or stubs with TODOs are not acceptable, unless explicitly permitted by the user.
diff --git a/loop/testdata/agent_loop.httprr b/loop/testdata/agent_loop.httprr
index fbafd31..d86594e 100644
--- a/loop/testdata/agent_loop.httprr
+++ b/loop/testdata/agent_loop.httprr
@@ -1,5 +1,5 @@
 httprr trace v1
-20299 2495
+20299 2778
 POST https://api.anthropic.com/v1/messages HTTP/1.1

 Host: api.anthropic.com

 User-Agent: Go-http-client/1.1

@@ -74,6 +74,54 @@
    }
   },
   {
+   "name": "patch",
+   "description": "File modification tool for precise text edits.\n\nOperations:\n- replace: Substitute text with new content\n- append_eof: Append new text at the end of the file\n- prepend_bof: Insert new text at the beginning of the file\n- overwrite: Replace the entire file with new content (automatically creates the file)\n\nUsage notes:\n- All inputs are interpreted literally (no automatic newline or whitespace handling)\n- For replace operations, oldText must appear EXACTLY ONCE in the file",
+   "input_schema": {
+    "type": "object",
+    "required": [
+     "path",
+     "patches"
+    ],
+    "properties": {
+     "path": {
+      "type": "string",
+      "description": "Absolute path to the file to patch"
+     },
+     "patches": {
+      "type": "array",
+      "description": "List of patch requests to apply",
+      "items": {
+       "type": "object",
+       "required": [
+        "operation",
+        "newText"
+       ],
+       "properties": {
+        "operation": {
+         "type": "string",
+         "enum": [
+          "replace",
+          "append_eof",
+          "prepend_bof",
+          "overwrite"
+         ],
+         "description": "Type of operation to perform"
+        },
+        "oldText": {
+         "type": "string",
+         "description": "Text to locate for the operation (must be unique in file, required for replace)"
+        },
+        "newText": {
+         "type": "string",
+         "description": "The new text to use (empty for deletions)"
+        }
+       }
+      }
+     }
+    }
+   }
+  },
+  {
    "name": "think",
    "description": "Think out loud, take notes, form plans. Has no external effects.",
    "input_schema": {
@@ -542,54 +590,6 @@
      "path"
     ]
    }
-  },
-  {
-   "name": "patch",
-   "description": "File modification tool for precise text edits.\n\nOperations:\n- replace: Substitute text with new content\n- append_eof: Append new text at the end of the file\n- prepend_bof: Insert new text at the beginning of the file\n- overwrite: Replace the entire file with new content (automatically creates the file)\n\nUsage notes:\n- All inputs are interpreted literally (no automatic newline or whitespace handling)\n- For replace operations, oldText must appear EXACTLY ONCE in the file",
-   "input_schema": {
-    "type": "object",
-    "required": [
-     "path",
-     "patches"
-    ],
-    "properties": {
-     "path": {
-      "type": "string",
-      "description": "Absolute path to the file to patch"
-     },
-     "patches": {
-      "type": "array",
-      "description": "List of patch requests to apply",
-      "items": {
-       "type": "object",
-       "required": [
-        "operation",
-        "newText"
-       ],
-       "properties": {
-        "operation": {
-         "type": "string",
-         "enum": [
-          "replace",
-          "append_eof",
-          "prepend_bof",
-          "overwrite"
-         ],
-         "description": "Type of operation to perform"
-        },
-        "oldText": {
-         "type": "string",
-         "description": "Text to locate for the operation (must be unique in file, required for replace)"
-        },
-        "newText": {
-         "type": "string",
-         "description": "The new text to use (empty for deletions)"
-        }
-       }
-      }
-     }
-    }
-   }
   }
  ],
  "system": [
@@ -605,24 +605,24 @@
 Anthropic-Organization-Id: 3c473a21-7208-450a-a9f8-80aebda45c1b

 Anthropic-Ratelimit-Input-Tokens-Limit: 200000

 Anthropic-Ratelimit-Input-Tokens-Remaining: 200000

-Anthropic-Ratelimit-Input-Tokens-Reset: 2025-05-29T17:27:03Z

+Anthropic-Ratelimit-Input-Tokens-Reset: 2025-05-30T15:57:41Z

 Anthropic-Ratelimit-Output-Tokens-Limit: 80000

 Anthropic-Ratelimit-Output-Tokens-Remaining: 80000

-Anthropic-Ratelimit-Output-Tokens-Reset: 2025-05-29T17:27:08Z

+Anthropic-Ratelimit-Output-Tokens-Reset: 2025-05-30T15:57:47Z

 Anthropic-Ratelimit-Requests-Limit: 4000

 Anthropic-Ratelimit-Requests-Remaining: 3999

-Anthropic-Ratelimit-Requests-Reset: 2025-05-29T17:27:01Z

+Anthropic-Ratelimit-Requests-Reset: 2025-05-30T15:57:39Z

 Anthropic-Ratelimit-Tokens-Limit: 280000

 Anthropic-Ratelimit-Tokens-Remaining: 280000

-Anthropic-Ratelimit-Tokens-Reset: 2025-05-29T17:27:03Z

+Anthropic-Ratelimit-Tokens-Reset: 2025-05-30T15:57:41Z

 Cf-Cache-Status: DYNAMIC

-Cf-Ray: 9477b339e89417ee-SJC

+Cf-Ray: 947f6db1dc082349-SJC

 Content-Type: application/json

-Date: Thu, 29 May 2025 17:27:09 GMT

-Request-Id: req_011CPcDNHrEZY5uavYRd4Wsk

+Date: Fri, 30 May 2025 15:57:47 GMT

+Request-Id: req_011CPdzMxBJ1hScfsMghVkJA

 Server: cloudflare

 Strict-Transport-Security: max-age=31536000; includeSubDomains; preload

 Via: 1.1 google

 X-Robots-Tag: none

 

-{"id":"msg_01PbteLNxXTMFWyxMUWxhpPM","type":"message","role":"assistant","model":"claude-sonnet-4-20250514","content":[{"type":"text","text":"Here are the tools available to me:\n\n**File & Code Operations:**\n- `patch` - Modify files with precise text edits\n- `keyword_search` - Search codebase with keywords\n- `bash` - Execute shell commands\n- `codereview` - Run automated code review\n\n**Project Management:**\n- `todo_read` / `todo_write` - Manage task lists\n- `think` - Record thoughts and plans\n- `precommit` - Create git branch for work\n- `done` - Complete work with checklist verification\n\n**Browser Automation:**\n- `browser_navigate` - Navigate to URLs\n- `browser_click` - Click elements\n- `browser_type` - Type into inputs\n- `browser_wait_for` - Wait for elements\n- `browser_get_text` - Read page text\n- `browser_eval` - Execute JavaScript\n- `browser_scroll_into_view` - Scroll elements into view\n- `browser_resize` - Resize browser window\n- `browser_take_screenshot` - Capture screenshots\n- `browser_read_image` - Read image files\n- `browser_recent_console_logs` / `browser_clear_console_logs` - Manage console logs\n\n**User Interface:**\n- `title` - Set conversation title\n- `multiplechoice` - Present multiple choice questions\n- `about_sketch` - Get Sketch platform information"}],"stop_reason":"end_turn","stop_sequence":null,"usage":{"input_tokens":3,"cache_creation_input_tokens":4648,"cache_read_input_tokens":0,"output_tokens":320,"service_tier":"standard"}}
\ No newline at end of file
+{"id":"msg_01WmU5PvHrPn5NU59qBJUsu8","type":"message","role":"assistant","model":"claude-sonnet-4-20250514","content":[{"type":"text","text":"Here are the tools available to me:\n\n**File Operations:**\n- `patch` - Modify files with precise text edits (replace, append, prepend, overwrite)\n- `keyword_search` - Search for files using keywords and queries\n\n**Shell & System:**\n- `bash` - Execute shell commands with optional background execution\n- `think` - Internal note-taking and planning tool\n\n**Task Management:**\n- `todo_read` - Read current todo list\n- `todo_write` - Create and manage structured task lists\n\n**Git & Code Review:**\n- `precommit` - Create git branch and get commit message guidance\n- `codereview` - Run automated code review\n\n**Browser Automation:**\n- `browser_navigate` - Navigate to URLs\n- `browser_click` - Click elements by CSS selector\n- `browser_type` - Type text into input fields\n- `browser_wait_for` - Wait for elements to appear\n- `browser_get_text` - Read text from elements\n- `browser_eval` - Execute JavaScript\n- `browser_scroll_into_view` - Scroll elements into view\n- `browser_resize` - Resize browser window\n- `browser_take_screenshot` - Capture screenshots\n- `browser_read_image` - Read image files\n- `browser_recent_console_logs` - Get console logs\n- `browser_clear_console_logs` - Clear console logs\n\n**Meta Tools:**\n- `title` - Set conversation title\n- `done` - Complete work with verification checklist\n- `about_sketch` - Get help about Sketch functionality\n- `multiplechoice` - Present multiple choice questions to user"}],"stop_reason":"end_turn","stop_sequence":null,"usage":{"input_tokens":3,"cache_creation_input_tokens":4648,"cache_read_input_tokens":0,"output_tokens":376,"service_tier":"standard"}}
\ No newline at end of file
diff --git a/termui/termui.go b/termui/termui.go
index 0146190..b14593d 100644
--- a/termui/termui.go
+++ b/termui/termui.go
@@ -50,8 +50,6 @@
 🌱 git branch: sketch/{{.input.branch_name}}
 {{else if eq .msg.ToolName "about_sketch" -}}
 📚 About Sketch
-{{else if eq .msg.ToolName "str_replace_editor" -}}
- ✏️  {{.input.file_path -}}
 {{else if eq .msg.ToolName "codereview" -}}
  🐛  Running automated code review, may be slow
 {{else if eq .msg.ToolName "multiplechoice" -}}