Initial commit
diff --git a/claudetool/bash_test.go b/claudetool/bash_test.go
new file mode 100644
index 0000000..8fe4b5c
--- /dev/null
+++ b/claudetool/bash_test.go
@@ -0,0 +1,186 @@
+package claudetool
+
+import (
+	"context"
+	"encoding/json"
+	"strings"
+	"testing"
+	"time"
+)
+
+func TestBashRun(t *testing.T) {
+	// Test basic functionality
+	t.Run("Basic Command", func(t *testing.T) {
+		input := json.RawMessage(`{"command":"echo 'Hello, world!'"}`)
+
+		result, err := BashRun(context.Background(), input)
+		if err != nil {
+			t.Fatalf("Unexpected error: %v", err)
+		}
+
+		expected := "Hello, world!\n"
+		if result != expected {
+			t.Errorf("Expected %q, got %q", expected, result)
+		}
+	})
+
+	// Test with arguments
+	t.Run("Command With Arguments", func(t *testing.T) {
+		input := json.RawMessage(`{"command":"echo -n foo && echo -n bar"}`)
+
+		result, err := BashRun(context.Background(), input)
+		if err != nil {
+			t.Fatalf("Unexpected error: %v", err)
+		}
+
+		expected := "foobar"
+		if result != expected {
+			t.Errorf("Expected %q, got %q", expected, result)
+		}
+	})
+
+	// Test with timeout parameter
+	t.Run("With Timeout", func(t *testing.T) {
+		inputObj := struct {
+			Command string `json:"command"`
+			Timeout string `json:"timeout"`
+		}{
+			Command: "sleep 0.1 && echo 'Completed'",
+			Timeout: "5s",
+		}
+		inputJSON, err := json.Marshal(inputObj)
+		if err != nil {
+			t.Fatalf("Failed to marshal input: %v", err)
+		}
+
+		result, err := BashRun(context.Background(), inputJSON)
+		if err != nil {
+			t.Fatalf("Unexpected error: %v", err)
+		}
+
+		expected := "Completed\n"
+		if result != expected {
+			t.Errorf("Expected %q, got %q", expected, result)
+		}
+	})
+
+	// Test command timeout
+	t.Run("Command Timeout", func(t *testing.T) {
+		inputObj := struct {
+			Command string `json:"command"`
+			Timeout string `json:"timeout"`
+		}{
+			Command: "sleep 0.5 && echo 'Should not see this'",
+			Timeout: "100ms",
+		}
+		inputJSON, err := json.Marshal(inputObj)
+		if err != nil {
+			t.Fatalf("Failed to marshal input: %v", err)
+		}
+
+		_, err = BashRun(context.Background(), inputJSON)
+		if err == nil {
+			t.Errorf("Expected timeout error, got none")
+		} else if !strings.Contains(err.Error(), "timed out") {
+			t.Errorf("Expected timeout error, got: %v", err)
+		}
+	})
+
+	// Test command that fails
+	t.Run("Failed Command", func(t *testing.T) {
+		input := json.RawMessage(`{"command":"exit 1"}`)
+
+		_, err := BashRun(context.Background(), input)
+		if err == nil {
+			t.Errorf("Expected error for failed command, got none")
+		}
+	})
+
+	// Test invalid input
+	t.Run("Invalid JSON Input", func(t *testing.T) {
+		input := json.RawMessage(`{"command":123}`) // Invalid JSON (command must be string)
+
+		_, err := BashRun(context.Background(), input)
+		if err == nil {
+			t.Errorf("Expected error for invalid input, got none")
+		}
+	})
+}
+
+func TestExecuteBash(t *testing.T) {
+	ctx := context.Background()
+
+	// Test successful command
+	t.Run("Successful Command", func(t *testing.T) {
+		req := bashInput{
+			Command: "echo 'Success'",
+			Timeout: "5s",
+		}
+
+		output, err := executeBash(ctx, req)
+		if err != nil {
+			t.Fatalf("Unexpected error: %v", err)
+		}
+
+		want := "Success\n"
+		if output != want {
+			t.Errorf("Expected %q, got %q", want, output)
+		}
+	})
+
+	// Test command with output to stderr
+	t.Run("Command with stderr", func(t *testing.T) {
+		req := bashInput{
+			Command: "echo 'Error message' >&2 && echo 'Success'",
+			Timeout: "5s",
+		}
+
+		output, err := executeBash(ctx, req)
+		if err != nil {
+			t.Fatalf("Unexpected error: %v", err)
+		}
+
+		want := "Error message\nSuccess\n"
+		if output != want {
+			t.Errorf("Expected %q, got %q", want, output)
+		}
+	})
+
+	// Test command that fails with stderr
+	t.Run("Failed Command with stderr", func(t *testing.T) {
+		req := bashInput{
+			Command: "echo 'Error message' >&2 && exit 1",
+			Timeout: "5s",
+		}
+
+		_, err := executeBash(ctx, req)
+		if err == nil {
+			t.Errorf("Expected error for failed command, got none")
+		} else if !strings.Contains(err.Error(), "Error message") {
+			t.Errorf("Expected stderr in error message, got: %v", err)
+		}
+	})
+
+	// Test timeout
+	t.Run("Command Timeout", func(t *testing.T) {
+		req := bashInput{
+			Command: "sleep 1 && echo 'Should not see this'",
+			Timeout: "100ms",
+		}
+
+		start := time.Now()
+		_, err := executeBash(ctx, req)
+		elapsed := time.Since(start)
+
+		// Command should time out after ~100ms, not wait for full 1 second
+		if elapsed >= 1*time.Second {
+			t.Errorf("Command did not respect timeout, took %v", elapsed)
+		}
+
+		if err == nil {
+			t.Errorf("Expected timeout error, got none")
+		} else if !strings.Contains(err.Error(), "timed out") {
+			t.Errorf("Expected timeout error, got: %v", err)
+		}
+	})
+}