--- name: agent_launch_worktree kind: function lang: go domain: infra version: "1.0.0" purity: impure signature: "func AgentLaunchWorktree(cfg WorktreeLaunchConfig) WorktreeLaunchResult" description: "Crea un git worktree nuevo en una rama derivada de master y lanza `claude -p ` headless dentro de ese worktree, redirigiendo stdout+stderr a un log file. Devuelve inmediatamente con el PID — el proceso queda corriendo en background. Si `ResetIfExists=true` y la rama existe, borra rama + worktree previos (best-effort) antes de recrear. Si `claude` no esta en PATH, hace fallback a `echo` como stub para que los tests puedan correr sin el binario real. Usa exec.LookPath, NO hardcodea paths. Cleanup del worktree + branch se hace con `agent_cleanup_worktree_go_infra`." tags: [agents, worktree, claude, git, headless] uses_functions: [] uses_types: [] returns: [] returns_optional: false error_type: "error_go_core" imports: ["fmt", "os", "os/exec", "strings", "time"] params: - name: cfg desc: "WorktreeLaunchConfig con RepoRoot (path absoluto al repo principal), Branch (ej. auto/0115-foo), WorktreePath (path absoluto donde crear el worktree), Prompt (texto pasado a claude -p), LogPath (archivo de log), Env opcional (env vars extra), SkipPerms (pasa --dangerously-skip-permissions), ResetIfExists (nuke previo de rama+worktree)." output: "WorktreeLaunchResult con PID (claude process id), Branch/WorktreePath/LogPath (eco de inputs), StartedAt (unix seconds) y Error (string vacio en exito; mensaje en fallo). PID=0 cuando Error!='' . El campo Error usa string en vez de error nativo Go para poder serializarse a JSON desde agent_runner_api." tested: true tests: - "creates worktree dir and branch off master" - "ResetIfExists=true on existing branch+worktree succeeds" - "returns Error when RepoRoot/Branch/WorktreePath missing" test_file_path: "functions/infra/agent_launch_worktree_test.go" file_path: "functions/infra/agent_launch_worktree.go" --- ## Ejemplo ```go res := infra.AgentLaunchWorktree(infra.WorktreeLaunchConfig{ RepoRoot: "$HOME/fn_registry", Branch: "auto/0115-worktree-launcher-fn", WorktreePath: "$HOME/fn_registry/worktrees/0115-worktree-launcher-fn", Prompt: "Implement issue 0115 — worktree launcher Go function", LogPath: "/tmp/claude-0115.log", SkipPerms: true, ResetIfExists: true, }) if res.Error != "" { log.Fatal(res.Error) } fmt.Printf("claude PID=%d branch=%s log=%s\n", res.PID, res.Branch, res.LogPath) // ... agente trabaja ... infra.AgentCleanupWorktree(res.WorktreePath, res.Branch, "$HOME/fn_registry", res.PID) ``` ## Cuando usarla Cuando una app (`agent_runner_api`, `fn-orquestador`) o un script necesite lanzar Claude headless en un sandbox aislado: ramas `auto/` o `issue/`. Reemplaza el bash inline que vivia en `.claude/skills/parallel-fix-issues/` y en el agente `fn-orquestador`. Si lo que quieres es ejecutar Claude en foreground sin worktree, NO uses esta — usa un `exec.Command` directo. ## Gotchas - **Spawn solo, no Wait**: la funcion hace `cmd.Start()` y vuelve. Si el caller necesita esperar al final, debe trackear el PID y hacer `syscall.Wait4` o consultar `/proc/`. Para cleanup ordenado, usa `agent_cleanup_worktree_go_infra`. - **Master debe existir** en `RepoRoot` — la rama se crea con `git worktree add ... -b master`. Si tu repo usa `main`, fork la funcion o renombra la rama localmente. - **`ResetIfExists` es best-effort**: si el worktree previo tiene cambios sin commitear o procesos atados, `git worktree remove --force` puede ignorar ciertos errores; siempre revisa el dir final. - **Log file truncado**: cada launch reabre `LogPath` con `O_TRUNC`. Si quieres preservar el log de runs anteriores, rota el archivo antes de llamar. - **Fallback `echo` stub** se activa cuando `exec.LookPath("claude")` falla; en ese caso el "proceso claude" imprime `STUB: claude not in PATH, prompt was: ` y termina inmediatamente. Util en CI/tests, no en produccion. - **PID en Windows**: `syscall.Kill` no existe en Windows — `agent_cleanup_worktree` solo funciona en Linux/Darwin. Documentado alli. - **Env**: los valores de `cfg.Env` se hacen append a `os.Environ()` — si quieres anular una var existente, en Go la ultima asignacion gana, asi que basta con incluirla en `cfg.Env`.