GitHub Tools

Let agents create branches, commit and push code, and open Pull Requests on GitHub

GitHub tools wrap local git and the GitHub CLI (gh) so agents can branch, commit, push, and open pull requests from a repo checkout.

python
from praisonaiagents import Agent
from praisonaiagents.tools import (
    github_create_branch,
    github_commit_and_push,
    github_create_pull_request,
)

agent = Agent(
    name="GitHub Release Agent",
    instructions="Create a branch, commit staged work, and open a pull request.",
    tools=[github_create_branch, github_commit_and_push, github_create_pull_request],
)

agent.start(
    "Create branch 'feature/auth', commit with message 'Add JWT auth', "
    "then open a PR titled 'Add user authentication feature' targeting main."
)

The user describes a git workflow; the agent runs GitHub tools to branch, commit, push, and open a pull request.

Diagram

GitHub Tools Flow

Agent

Create Branch

Commit and Push

Open PR

PR URL

Quick Start

  1. Simple Usage
    python
    from praisonaiagents import Agent
    from praisonaiagents.tools import (
        github_create_branch,
        github_commit_and_push,
        github_create_pull_request,
    )
  2. With Configuration
    python
    agent = Agent(
        name="GitHub Agent",
        tools=[github_create_branch, github_commit_and_push, github_create_pull_request],
    )

How It Works

Diagram
GitHubgit / gh CLIAgentUserGitHubgit / gh CLIAgentUserTaskgithub_create_branchgithub_commit_and_pushgithub_create_pull_requestpush + gh pr createPR URLResult

You don’t have to think about branch safety — the tool quietly does the right thing, and only your tracked edits (plus any files you list in paths) are committed — untracked files like a new .env or build output are never swept in.

User: “Commit and push what I have.” Agent: (tool auto-creates praisonai/fix-login-a1b2c3, pushes there, and reports the branch name back)

Tools

github_create_branch(branch_name, repo_path=None) -> str

Creates and checks out a new branch (git checkout -B).

ParameterTypeDefaultDescription
branch_namestr—Branch to create and check out
repo_pathOptional[str]NoneRepository directory to run git in. Defaults to the current working directory.

Returns a success message or an error string.

github_commit_and_push(commit_message, branch=None, allow_unsafe_branch=False, repo_path=None, paths=None) -> str

Stages tracked changes (plus any paths you list), commits, and pushes to origin — with branch-safety guardrails that refuse unsafe pushes and never force-push.

ParameterTypeDefaultDescription
commit_messagestr—Commit message
branchOptional[str]NoneTarget branch. Defaults to the current branch.
allow_unsafe_branchboolFalseOpt-in override for default-branch and foreign-author refusals. Divergence is never overridable.
repo_pathOptional[str]NoneRepository directory to run git in. Defaults to the current working directory.
pathsOptional[List[str]]NoneExtra (e.g. untracked) files to stage alongside tracked changes.

Returns a success message, "No changes to commit.", or an error string. Untracked files alone don’t count as changes — list them in paths to commit them.

Branch-safety rules

The tool checks these rules before staging anything, so a refusal leaves your index and working tree untouched.

  1. Agent branches are always allowed. Any branch whose name starts with praisonai/ skips the default and foreign-author checks.
  2. The default branch is never pushed directly. On main (or when branch="main"), the tool creates a fresh praisonai/{slug}-{6hex} branch from HEAD and pushes there instead.
  3. Foreign commits are refused. If the target branch carries commits authored by someone other than your git config user.email, the push stops.
  4. Divergence is refused, never forced. If the remote branch is not an ancestor of HEAD, the push stops — this is never overridable.
  5. Override with care. allow_unsafe_branch=True (or env PRAISONAI_GIT_ALLOW_UNSAFE_BRANCH=true) bypasses rules 2 and 3, but never rule 4.
python
from praisonaiagents.tools import github_commit_and_push

# On main: direct push refused, tool creates and pushes an agent branch.
github_commit_and_push("docs: clarify setup steps")
# -> "Successfully committed and pushed changes to new agent branch
#     'praisonai/docs-clarify-setup-steps-a1b2c3' (refused direct push
#     to default branch 'main')"
Diagram

No

Yes

Yes

No

Yes

No

Yes

No

Yes

No

github_commit_and_push

Changes?

Return: No changes to commit

Target = default branch?

Auto-create praisonai/slug-hex

Starts with praisonai/?

Remote diverged?

Foreign authors on branch?

Refuse – unless allow_unsafe_branch=True

Refuse – never overridable

Stage tracked changes + paths

Commit and push

github_create_pull_request(title, body, head_branch, base_branch="main", repo_path=None) -> str

Creates a pull request via gh pr create.

ParameterTypeDefaultDescription
titlestr—PR title in the GitHub UI — descriptive and concise
bodystr—PR description (markdown, issue refs such as #123)
head_branchstr—Source branch with your changes
base_branchstr"main"Target branch (main, master, develop, …)
repo_pathOptional[str]NoneRepository directory to run gh in. Defaults to the current working directory.
python
github_create_pull_request(
    title="Add user authentication feature",
    body="Implements secure login with JWT tokens\n\nFixes #123",
    head_branch="feature/auth",
    base_branch="main",
)
# -> 'Successfully created Pull Request:\nhttps://github.com/user/repo/pull/456'

Returns a success message with the PR URL, or an error if gh is missing or unauthenticated.

Common patterns

Auto-branch (safest)

python
from praisonaiagents.tools import github_commit_and_push

# Agent is on main; direct push refused, tool creates praisonai/<slug>-<hex>.
github_commit_and_push("docs: clarify setup steps")
# -> "Successfully committed and pushed changes to new agent branch
#     'praisonai/docs-clarify-setup-steps-a1b2c3' (refused direct push
#     to default branch 'main')"

# Explicit target — same auto-branch behaviour if it is the default.
github_commit_and_push("fix parser", branch="main")

# Opt in to push a non-default, non-agent branch you own.
github_commit_and_push(
    "release notes",
    branch="release/v2",
    allow_unsafe_branch=True,
)

Full PR flow

python
github_create_branch("praisonai/docs-update")
github_commit_and_push("docs: update README")
github_create_pull_request(
    title="Update README",
    body="Clarifies setup steps.",
    head_branch="praisonai/docs-update",
)

Any branch name starting with praisonai/ skips the default and foreign-author refusals.

Non-main base

python
github_create_pull_request(
    title="Hotfix for release branch",
    body="Patch login timeout.",
    head_branch="hotfix/login",
    base_branch="release/2.1",
)

Specific repository

python
from praisonaiagents.tools import github_commit_and_push

github_commit_and_push(
    "docs: fix typo",
    repo_path="/workspace/my-repo",
)

Use repo_path when the agent’s working directory isn’t the repo it should push to — common for gateway, hook, and scheduler agents.

Committing new files

python
from praisonaiagents.tools import github_commit_and_push

# Tracked edits plus brand-new files
github_commit_and_push(
    "feat: add Dockerfile",
    paths=["Dockerfile", "docker/entrypoint.sh"],
)

New files that aren’t listed in paths (and aren’t already tracked) are not committed.

PR only

python
# After you have already pushed head_branch
github_create_pull_request(
    title="Fix login validation bug",
    body="Validates email before sign-in.",
    head_branch="fix/login-validation",
)

Best practices

Authenticate gh once

Run gh auth login before agents call github_create_pull_request. The tool checks gh auth status first.

Keep commits scoped

github_commit_and_push stages only tracked changes (git add -u -- .). New (untracked) files are committed only when you list them in the paths argument — an untracked .env or build output is never swept in. Staging also runs after the branch-safety checks, so a refused push leaves the index untouched.

Prefer the auto-branch flow

Leaving branch=None while on the default branch is the safest, one-line way to ship a change. The tool picks a praisonai/… name, pushes there, and reports the name in its return string.

When to set allow_unsafe_branch

Only for a branch you own (e.g. release/v2) that carries commits by someone other than the current committer — never for the default branch as a habit. Set env PRAISONAI_GIT_ALLOW_UNSAFE_BRANCH=true for CI. Divergence (force-push) is never allowed regardless.

Write descriptive PR bodies

Include what changed, why, and linked issues — the body field supports markdown.

Handle missing CLI gracefully

Tools return error strings rather than raising — check return values in hooks if you need hard failures.

Related