@@ -3,7 +3,9 @@ package initcmd
33import (
44 "encoding/json"
55 "os"
6+ "os/exec"
67 "path/filepath"
8+ "strings"
79 "testing"
810)
911
@@ -194,6 +196,58 @@ func TestUnpatchPreservesOtherHooks(t *testing.T) {
194196 }
195197}
196198
199+ // TestHookScriptMultilineCommand verifies that the installed hook script handles
200+ // multiline commands (e.g. git commit with a heredoc) without error.
201+ // Previously, xargs was used to trim whitespace from FIRST_CMD and would fail
202+ // with exit 1 on unmatched quotes present in heredoc body lines.
203+ func TestHookScriptMultilineCommand (t * testing.T ) {
204+ if _ , err := exec .LookPath ("bash" ); err != nil {
205+ t .Skip ("bash not available" )
206+ }
207+ if _ , err := exec .LookPath ("jq" ); err != nil {
208+ t .Skip ("jq not available" )
209+ }
210+ if _ , err := exec .LookPath ("snip" ); err != nil {
211+ t .Skip ("snip not available" )
212+ }
213+
214+ // Write the hook to a temp file (simulates snip init).
215+ dir := t .TempDir ()
216+ hookPath := filepath .Join (dir , "snip-rewrite.sh" )
217+ if err := os .WriteFile (hookPath , []byte (hookScript ), 0755 ); err != nil {
218+ t .Fatalf ("write hook: %v" , err )
219+ }
220+
221+ // Simulate the JSON Claude Code sends for a heredoc-style git commit.
222+ // The multiline command contains an unmatched `)"` on the last line,
223+ // which caused xargs to exit 1 (unmatched double quote).
224+ cmd := "git add file.go && git commit -m \" $(cat <<'EOF'\n fix: something\n \n Co-Authored-By: Bot <bot@example.com>\n EOF\n )\" "
225+ payload , _ := json .Marshal (map [string ]any {
226+ "tool_name" : "Bash" ,
227+ "tool_input" : map [string ]any {"command" : cmd },
228+ })
229+
230+ proc := exec .Command ("bash" , hookPath )
231+ proc .Stdin = strings .NewReader (string (payload ))
232+ output , runErr := proc .Output ()
233+ if runErr != nil {
234+ t .Fatalf ("hook exited non-zero: %v" , runErr )
235+ }
236+
237+ var result map [string ]any
238+ if err := json .Unmarshal (output , & result ); err != nil {
239+ t .Fatalf ("hook output is not valid JSON: %v\n output: %s" , err , output )
240+ }
241+
242+ hookOut , _ := result ["hookSpecificOutput" ].(map [string ]any )
243+ updated , _ := hookOut ["updatedInput" ].(map [string ]any )
244+ rewritten , _ := updated ["command" ].(string )
245+
246+ if ! strings .HasPrefix (rewritten , "snip git add " ) {
247+ t .Errorf ("expected rewritten command to start with 'snip git add', got: %s" , rewritten )
248+ }
249+ }
250+
197251func readSettings (t * testing.T , path string ) map [string ]any {
198252 t .Helper ()
199253 data , err := os .ReadFile (path )
0 commit comments