claudetool: add just-in-time tool installation for bash commands

Implements automatic tool installation when bash commands use missing tools,
providing a seamless experience for the LLM to Just Use tools it knows ought to exist.

Core Features:

1. Command Analysis Pipeline:
   - Parse bash commands to extract individual tools/programs
   - Use exec.LookPath to check if tools exist in PATH
   - Handle shell built-ins, absolute/relative paths correctly
   - Support complex command chaining with &&, ||, ;, and |

2. Subagent Tool Installation:
   - Spawn dedicated subagents to install missing tools

The system preserves existing bash tool behavior while adding invisible
tool installation. Original commands run regardless of installation
success/failure, avoiding agent confusion.

Co-Authored-By: sketch <hello@sketch.dev>
Change-ID: s226cd6260a6469e9k
diff --git a/llm/conversation/convo_test.go b/llm/conversation/convo_test.go
index af8a904..cf4f1c2 100644
--- a/llm/conversation/convo_test.go
+++ b/llm/conversation/convo_test.go
@@ -102,9 +102,9 @@
 					cancelledWithErr = err
 				}
 
-				convo.muToolUseCancel.Lock()
+				convo.toolUseCancelMu.Lock()
 				convo.toolUseCancel[tt.toolUseID] = mockCancel
-				convo.muToolUseCancel.Unlock()
+				convo.toolUseCancelMu.Unlock()
 			}
 
 			err := convo.CancelToolUse(tt.toolUseID, tt.cancelErr)
@@ -126,9 +126,9 @@
 
 			// Verify the toolUseID was removed from the map if it was initially added
 			if tt.setupToolUse {
-				convo.muToolUseCancel.Lock()
+				convo.toolUseCancelMu.Lock()
 				_, exists := convo.toolUseCancel[tt.toolUseID]
-				convo.muToolUseCancel.Unlock()
+				convo.toolUseCancelMu.Unlock()
 
 				if exists {
 					t.Errorf("toolUseID %s still exists in the map after cancellation", tt.toolUseID)