Add permission callback to Bash tool to enforce title setting before git commits

- Convert Bash tool from a plain variable to a struct with a permission callback function
- Add a constructor for creating Bash tools with custom callback functions
- Implement a permission check in agent.go that verifies a branch name has been set before allowing git commits
- If no branch name is set and a git commit command is attempted, return an error instructing to use the title tool first
- Maintain backward compatibility for tests with BashRun function

Co-Authored-By: sketch <hello@sketch.dev>
diff --git a/loop/agent.go b/loop/agent.go
index 0b8d593..9b391d0 100644
--- a/loop/agent.go
+++ b/loop/agent.go
@@ -18,6 +18,7 @@
 
 	"sketch.dev/ant"
 	"sketch.dev/claudetool"
+	"sketch.dev/claudetool/bashkit"
 )
 
 const (
@@ -646,11 +647,41 @@
 
 	convo.SystemPrompt = fmt.Sprintf(agentSystemPrompt, editPrompt, a.config.ClientGOOS, a.config.ClientGOARCH, a.workingDir, a.repoRoot, a.initialCommit)
 
+	// Define a permission callback for the bash tool to check if the branch name is set before allowing git commits
+	bashPermissionCheck := func(command string) error {
+		// Check if branch name is set
+		a.mu.Lock()
+		branchSet := a.branchName != ""
+		a.mu.Unlock()
+
+		// If branch is set, all commands are allowed
+		if branchSet {
+			return nil
+		}
+
+		// If branch is not set, check if this is a git commit command
+		willCommit, err := bashkit.WillRunGitCommit(command)
+		if err != nil {
+			// If there's an error checking, we should allow the command to proceed
+			return nil
+		}
+
+		// If it's a git commit and branch is not set, return an error
+		if willCommit {
+			return fmt.Errorf("you must use the title tool before making git commits")
+		}
+
+		return nil
+	}
+
+	// Create a custom bash tool with the permission check
+	bashTool := claudetool.NewBashTool(bashPermissionCheck)
+
 	// Register all tools with the conversation
 	// When adding, removing, or modifying tools here, double-check that the termui tool display
 	// template in termui/termui.go has pretty-printing support for all tools.
 	convo.Tools = []*ant.Tool{
-		claudetool.Bash, claudetool.Keyword,
+		bashTool, claudetool.Keyword,
 		claudetool.Think, a.titleTool(), makeDoneTool(a.codereview, a.config.GitUsername, a.config.GitEmail),
 		a.codereview.Tool(),
 	}