Initial commit
diff --git a/claudetool/edit_test.go b/claudetool/edit_test.go
new file mode 100644
index 0000000..fe3d66c
--- /dev/null
+++ b/claudetool/edit_test.go
@@ -0,0 +1,399 @@
+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 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)
+	}
+}