From 692147d5e63855dc6cb7fcbdb0b7e2b4e4a96f66 Mon Sep 17 00:00:00 2001 From: dj-oyu <68707227+dj-oyu@users.noreply.github.com> Date: Tue, 24 Feb 2026 18:04:16 +0900 Subject: [PATCH] fix: ActivateWorktree uses plan project directory instead of workspace FindRepoRoot was called on ai.Workspace (picoclaw home), but git init is done in the plan's project directory. This caused "workspace is not a git repository" and silently skipped worktree creation. Now accepts projectDir parameter and falls back to workspace if empty. Call sites pass GetPlanWorkDir() so worktrees are created inside the actual project repository. Co-Authored-By: Claude Opus 4.6 --- pkg/agent/instance.go | 15 ++++++++++----- pkg/agent/loop.go | 6 ++++-- 2 files changed, 14 insertions(+), 7 deletions(-) diff --git a/pkg/agent/instance.go b/pkg/agent/instance.go index 110dc11a7..358a38a19 100644 --- a/pkg/agent/instance.go +++ b/pkg/agent/instance.go @@ -206,16 +206,21 @@ func resolvePlanFallbacks(agentCfg *config.AgentConfig, defaults *config.AgentDe } // ActivateWorktree creates a worktree for a session. -// Path: /.picoclaw/worktrees// -func (ai *AgentInstance) ActivateWorktree(sessionKey, taskName string) (*git.WorktreeInfo, error) { - repoRoot := git.FindRepoRoot(ai.Workspace) +// projectDir is the git repository to create the worktree in. +// If empty, falls back to ai.Workspace. +// Path: /.picoclaw/worktrees// +func (ai *AgentInstance) ActivateWorktree(sessionKey, taskName, projectDir string) (*git.WorktreeInfo, error) { + if projectDir == "" { + projectDir = ai.Workspace + } + repoRoot := git.FindRepoRoot(projectDir) if repoRoot == "" { - return nil, fmt.Errorf("workspace is not a git repository") + return nil, fmt.Errorf("directory is not a git repository: %s", projectDir) } branchName := git.SanitizeBranchName(taskName) baseName := git.BranchBaseName(branchName) - wtPath := filepath.Join(ai.Workspace, ".picoclaw", "worktrees", baseName) + wtPath := filepath.Join(repoRoot, ".picoclaw", "worktrees", baseName) wt, err := git.CreateWorktree(repoRoot, wtPath, branchName) if err != nil { diff --git a/pkg/agent/loop.go b/pkg/agent/loop.go index 5f5c759e7..a49a74eb0 100644 --- a/pkg/agent/loop.go +++ b/pkg/agent/loop.go @@ -307,7 +307,8 @@ func (al *AgentLoop) Run(ctx context.Context) error { if taskName == "" { taskName = "plan-execution" } - if wt, err := agent.ActivateWorktree(msg.SessionKey, taskName); err != nil { + planDir := agent.ContextBuilder.GetPlanWorkDir() + if wt, err := agent.ActivateWorktree(msg.SessionKey, taskName, planDir); err != nil { logger.WarnCF("agent", "Worktree activation skipped", map[string]any{"error": err.Error()}) } else { logger.InfoCF("agent", "Worktree activated", map[string]any{"branch": wt.Branch}) @@ -2111,7 +2112,8 @@ func (al *AgentLoop) runLLMIteration( // Heartbeat lazy worktree: create worktree on first write-tool call if opts.Background && isWriteTool(tc.Name) && !agent.IsInWorktree(opts.SessionKey) { taskName := "heartbeat-" + time.Now().Format("20060102") - if wt, err := agent.ActivateWorktree(opts.SessionKey, taskName); err == nil { + hbDir := agent.ContextBuilder.GetPlanWorkDir() + if wt, err := agent.ActivateWorktree(opts.SessionKey, taskName, hbDir); err == nil { logger.InfoCF("agent", "Heartbeat worktree created", map[string]any{"branch": wt.Branch}) } }