@@ -210,3 +210,79 @@ func TestHooksInstallFromWorktree(t *testing.T) {
210210 "hook %s should NOT be written to worktree-private hooks dir" , name )
211211 }
212212}
213+
214+ // Agent hooks run from the directory the session started in, which is not
215+ // necessarily the checkout that owns the file being edited (PFM-7412), so the
216+ // lookup has to start from an explicit directory rather than cwd.
217+ func TestFindGitDirAndRootFrom (t * testing.T ) {
218+ if _ , err := exec .LookPath ("git" ); err != nil {
219+ t .Skip ("git binary not found, skipping worktree test" )
220+ }
221+
222+ mainDir , err := filepath .EvalSymlinks (t .TempDir ())
223+ require .NoError (t , err )
224+ wtDir := filepath .Join (mainDir , ".claude" , "worktrees" , "feature" )
225+
226+ run := func (args ... string ) {
227+ cmd := exec .Command ("git" , args ... )
228+ cmd .Dir = mainDir
229+ out , err := cmd .CombinedOutput ()
230+ require .NoError (t , err , "git %v failed: %s" , args , string (out ))
231+ }
232+ run ("init" , "-b" , "main" )
233+ run ("config" , "user.email" , "test@test.com" )
234+ run ("config" , "user.name" , "Test" )
235+ run ("commit" , "--allow-empty" , "-m" , "init" )
236+ run ("worktree" , "add" , wtDir , "-b" , "feature" )
237+
238+ // cwd is the main checkout throughout, as it is for an agent hook.
239+ t .Chdir (mainDir )
240+
241+ tests := []struct {
242+ name string
243+ dir string
244+ wantGitDir string
245+ wantRoot string
246+ errNotARepo bool
247+ }{
248+ {
249+ name : "main checkout" ,
250+ dir : mainDir ,
251+ wantGitDir : filepath .Join (mainDir , ".git" ),
252+ wantRoot : mainDir ,
253+ },
254+ {
255+ name : "worktree nested in the main checkout" ,
256+ dir : wtDir ,
257+ wantGitDir : filepath .Join (mainDir , ".git" , "worktrees" , "feature" ),
258+ wantRoot : wtDir ,
259+ },
260+ {
261+ // A Write creating a file in a new directory: the directory does
262+ // not exist yet when the pre-tool-use hook runs.
263+ name : "not yet created directory inside a worktree" ,
264+ dir : filepath .Join (wtDir , "new" , "pkg" ),
265+ wantGitDir : filepath .Join (mainDir , ".git" , "worktrees" , "feature" ),
266+ wantRoot : wtDir ,
267+ },
268+ {
269+ name : "outside any repository" ,
270+ dir : t .TempDir (),
271+ errNotARepo : true ,
272+ },
273+ }
274+
275+ for _ , tc := range tests {
276+ t .Run (tc .name , func (t * testing.T ) {
277+ gitDir , root , err := FindGitDirAndRootFrom (tc .dir )
278+ if tc .errNotARepo {
279+ assert .ErrorIs (t , err , ErrNotARepository )
280+ return
281+ }
282+
283+ require .NoError (t , err )
284+ assert .Equal (t , tc .wantGitDir , gitDir )
285+ assert .Equal (t , tc .wantRoot , root )
286+ })
287+ }
288+ }
0 commit comments