From 51c58ba79560219c684297a97efa95efddaefde2 Mon Sep 17 00:00:00 2001 From: Tanuj Date: Mon, 2 Mar 2026 15:34:45 -0800 Subject: [PATCH 1/2] Fix Droid file edit hook not extracting edit content MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Factory Droid sends old_str/new_str in tool_input for the Edit tool, while Claude Code sends old_string/new_string. The droid-after-file-edit handler only checked for Claude Code's field names, causing empty edits to be sent to the security check — which then returned safe:true without actually running the check. --- unified.go | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/unified.go b/unified.go index 45076bd..c32e8eb 100644 --- a/unified.go +++ b/unified.go @@ -565,17 +565,28 @@ func OnAfterFileEdit(handler FileEditHandler) { } // Extract file path and edits from tool input + // Factory Droid uses "old_str"/"new_str" while Claude Code uses "old_string"/"new_string" var toolInput struct { FilePath string `json:"file_path"` Content string `json:"content"` OldString string `json:"old_string"` NewString string `json:"new_string"` + OldStr string `json:"old_str"` + NewStr string `json:"new_str"` } json.Unmarshal(input.ToolInput, &toolInput) + // Prefer old_str/new_str (Factory) over old_string/new_string (Claude Code) + oldString := toolInput.OldString + newString := toolInput.NewString + if toolInput.OldStr != "" || toolInput.NewStr != "" { + oldString = toolInput.OldStr + newString = toolInput.NewStr + } + var edits []FileEdit if input.ToolName == "Edit" { - edits = []FileEdit{{OldString: toolInput.OldString, NewString: toolInput.NewString}} + edits = []FileEdit{{OldString: oldString, NewString: newString}} } else { edits = []FileEdit{{OldString: "", NewString: toolInput.Content}} } From e5dfe47c150d03d9737b3903b7d943cfff9a2eda Mon Sep 17 00:00:00 2001 From: Tanuj Date: Mon, 2 Mar 2026 15:51:30 -0800 Subject: [PATCH 2/2] Simplify Droid edit field handling The Droid handler is a separate code path that only receives Droid input, so it will never see Claude's old_string/new_string fields. Remove the unnecessary dual-field struct and fallback logic. --- unified.go | 22 ++++++---------------- 1 file changed, 6 insertions(+), 16 deletions(-) diff --git a/unified.go b/unified.go index c32e8eb..f3282f6 100644 --- a/unified.go +++ b/unified.go @@ -565,28 +565,18 @@ func OnAfterFileEdit(handler FileEditHandler) { } // Extract file path and edits from tool input - // Factory Droid uses "old_str"/"new_str" while Claude Code uses "old_string"/"new_string" + // Factory Droid uses "old_str"/"new_str" (not "old_string"/"new_string" like Claude Code) var toolInput struct { - FilePath string `json:"file_path"` - Content string `json:"content"` - OldString string `json:"old_string"` - NewString string `json:"new_string"` - OldStr string `json:"old_str"` - NewStr string `json:"new_str"` + FilePath string `json:"file_path"` + Content string `json:"content"` + OldStr string `json:"old_str"` + NewStr string `json:"new_str"` } json.Unmarshal(input.ToolInput, &toolInput) - // Prefer old_str/new_str (Factory) over old_string/new_string (Claude Code) - oldString := toolInput.OldString - newString := toolInput.NewString - if toolInput.OldStr != "" || toolInput.NewStr != "" { - oldString = toolInput.OldStr - newString = toolInput.NewStr - } - var edits []FileEdit if input.ToolName == "Edit" { - edits = []FileEdit{{OldString: oldString, NewString: newString}} + edits = []FileEdit{{OldString: toolInput.OldStr, NewString: toolInput.NewStr}} } else { edits = []FileEdit{{OldString: "", NewString: toolInput.Content}} }