Skip to content

Worktree add

worktree_add

Worktree-add hook action.

Creates a git worktree at /tmp/axm-worktrees/<ticket_id>/ with a branch derived from ticket metadata via branch_name_from_ticket().

WorktreeAddHook dataclass

Create a worktree + branch for a ticket.

Reads ticket_id, ticket_title, ticket_labels, and repo_path from context. The worktree is placed under /tmp/axm-worktrees/<ticket_id>/.

Skips gracefully when the working directory is not a git repository or the worktree already exists.

Source code in packages/axm-git/src/axm_git/hooks/worktree_add.py
@dataclass
class WorktreeAddHook:
    """Create a worktree + branch for a ticket.

    Reads ``ticket_id``, ``ticket_title``, ``ticket_labels``, and
    ``repo_path`` from *context*.  The worktree is placed under
    ``/tmp/axm-worktrees/<ticket_id>/``.

    Skips gracefully when the working directory is not a git repository
    or the worktree already exists.
    """

    def execute(self, context: dict[str, Any], **params: Any) -> HookResult:
        """Execute the hook action.

        Args:
            context: Session context dictionary.
            **params: Optional ``repo_path``, ``ticket_id``,
                ``ticket_title``, ``ticket_labels``, ``enabled``
                (default ``True``).  Params take precedence over context.

        Returns:
            HookResult with ``worktree_path`` and ``branch`` in metadata.
        """
        if not params.get("enabled", True):
            return HookResult.ok(skipped=True, reason="git disabled")

        repo_path = Path(params.get("repo_path", context.get("repo_path", ".")))

        if find_git_root(repo_path) is None:
            return HookResult.ok(skipped=True, reason="not a git repo")

        ticket_id: str = (
            params["ticket_id"] if "ticket_id" in params else context["ticket_id"]
        )
        title: str = (
            params["ticket_title"]
            if "ticket_title" in params
            else context["ticket_title"]
        )
        labels: list[str] = params.get(
            "ticket_labels", context.get("ticket_labels", [])
        )

        branch = branch_name_from_ticket(ticket_id, title, labels)
        worktree_path = Path("/tmp/axm-worktrees") / ticket_id  # noqa: S108
        worktree_path.parent.mkdir(parents=True, exist_ok=True)

        if worktree_path.exists():
            return HookResult.ok(
                skipped=True,
                reason=f"worktree already exists: {worktree_path}",
            )

        result = run_git(
            ["worktree", "add", "-b", branch, str(worktree_path), "main"],
            repo_path,
        )
        if result.returncode != 0:
            return HookResult.fail(f"git worktree add failed: {result.stderr}")

        return HookResult.ok(
            worktree_path=str(worktree_path),
            branch=branch,
        )
execute(context, **params)

Execute the hook action.

Parameters:

Name Type Description Default
context dict[str, Any]

Session context dictionary.

required
**params Any

Optional repo_path, ticket_id, ticket_title, ticket_labels, enabled (default True). Params take precedence over context.

{}

Returns:

Type Description
HookResult

HookResult with worktree_path and branch in metadata.

Source code in packages/axm-git/src/axm_git/hooks/worktree_add.py
def execute(self, context: dict[str, Any], **params: Any) -> HookResult:
    """Execute the hook action.

    Args:
        context: Session context dictionary.
        **params: Optional ``repo_path``, ``ticket_id``,
            ``ticket_title``, ``ticket_labels``, ``enabled``
            (default ``True``).  Params take precedence over context.

    Returns:
        HookResult with ``worktree_path`` and ``branch`` in metadata.
    """
    if not params.get("enabled", True):
        return HookResult.ok(skipped=True, reason="git disabled")

    repo_path = Path(params.get("repo_path", context.get("repo_path", ".")))

    if find_git_root(repo_path) is None:
        return HookResult.ok(skipped=True, reason="not a git repo")

    ticket_id: str = (
        params["ticket_id"] if "ticket_id" in params else context["ticket_id"]
    )
    title: str = (
        params["ticket_title"]
        if "ticket_title" in params
        else context["ticket_title"]
    )
    labels: list[str] = params.get(
        "ticket_labels", context.get("ticket_labels", [])
    )

    branch = branch_name_from_ticket(ticket_id, title, labels)
    worktree_path = Path("/tmp/axm-worktrees") / ticket_id  # noqa: S108
    worktree_path.parent.mkdir(parents=True, exist_ok=True)

    if worktree_path.exists():
        return HookResult.ok(
            skipped=True,
            reason=f"worktree already exists: {worktree_path}",
        )

    result = run_git(
        ["worktree", "add", "-b", branch, str(worktree_path), "main"],
        repo_path,
    )
    if result.returncode != 0:
        return HookResult.fail(f"git worktree add failed: {result.stderr}")

    return HookResult.ok(
        worktree_path=str(worktree_path),
        branch=branch,
    )