Skip to content

Automate multi-repo changes: clone repos, apply edits via AI agents, and open GitHub PRs.

Notifications You must be signed in to change notification settings

LeonPatmore/pr-creator

Repository files navigation

PR Creator

Simple workflow runner that clones target repos, applies changes via a change agent, and submits PRs.

Workflow Architecture

PR Creator Workflows

The system consists of two main workflows:

  • Orchestrator Workflow: Manages multi-repo coordination, discovers repos, evaluates relevance, and processes repos in parallel (beta pydantic-graph API)
  • Repo Change Workflow: Handles individual repo changes with AI review and CI integration

Parallel Processing: When multiple repositories are provided, the orchestrator processes them concurrently with automatic resource management:

  • Relevance evaluation runs in parallel for all discovered repos
  • Repo changes (apply, review, submit) are executed in parallel with a configurable concurrency limit
  • Default limit: 3 concurrent repos (configurable via MAX_PARALLEL_REPOS environment variable)
  • Thread-safe state aggregation ensures reliable PR tracking across parallel executions
  • Discovery mode (no repos specified) runs sequentially for safety

Use cases

  • Multi-repo rollouts: apply the same change across many repos (dependency bumps, config standardization, lint/format rules, CI updates).
  • Safe iteration on the same branch: use --change-id so reruns target a stable branch name and you can iterate until the PR is clean.
  • Prompt-config driven automation: store prompts in a separate “prompt config” repo so changes are reviewed/versioned and rerunnable.
  • Jira-driven prompts: point at a Jira ticket to build prompts from its summary/description (useful when the ticket is the source of truth).
  • Human-in-the-loop or fully automated: use human-written prompts, AI change agents, and optional evaluation steps to skip/stop when not relevant.

Installation

Install from PyPI (multi-repo-pr-creator):

pip install -U multi-repo-pr-creator
pr-creator --help

If you prefer isolated installs:

pipx install multi-repo-pr-creator
pr-creator --help

Quick start (CLI prompts):

export CURSOR_API_KEY=...
export GITHUB_TOKEN=...

pr-creator \
  --prompt "Update dependency X to version Y." \
  --repo https://github.com/<owner>/<repo> \
  --working-dir ~/.pr-creator/repos

Or run via Docker (see example below).

Docker images

  • pr-creator: leonpatmore2/pr-creator:latest (Docker Hub)
  • cursor-agent: leonpatmore2/cursor-agent:latest (Docker Hub)

Required tools

  • Either:
    • Docker (to run cursor-agent in a container), or
    • A local cursor-agent binary on your PATH (to run Cursor via CLI)
  • No git or GitHub CLI needed (Dulwich + GitHub API handle clone/push/PR)

Prompt loading

You choose exactly one base prompt source:

  • Inline prompts (CLI): pass --prompt (required) and optionally --relevance-prompt.
    • If --relevance-prompt is empty/missing, all repos are treated as relevant.
  • Prompt config YAML (GitHub): pass --prompt-config-owner, --prompt-config-repo, --prompt-config-path (and optionally --prompt-config-ref).
    • The YAML must include change_prompt and relevance_prompt (and may include change_id).
    • The relevance_prompt comes from the YAML (CLI --relevance-prompt is ignored in this mode).
  • Jira ticket: pass --jira-ticket (and --jira-base-url/--jira-email/--jira-api-token or env fallbacks).
    • The prompt is built from the ticket summary + description.
    • In this mode, --relevance-prompt still applies (optional).

Notes

  • Mutual exclusion: you can’t use Jira (--jira-ticket) and prompt config (--prompt-config-*) together.
  • Prompt “tail”: if you use prompt config or Jira and also pass --prompt, the CLI prompt is treated as higher priority and is placed in a top “Highest priority instructions (CLI)” section.
  • Exit status: the CLI exits with code 1 if any errors are recorded in orchestrator_errors (even if some PRs were successfully created).

Multi-repo changes

Target repos in one (or more) of these ways:

  • Explicit list: pass --repo multiple times. Each value can be a full URL, an owner/repo slug, or (with GITHUB_DEFAULT_ORG) a bare repo name.
  • Datadog discovery: pass --datadog-team to discover repos and add them to the list (requires DATADOG_API_KEY + DATADOG_APP_KEY).
  • MCP-driven discovery (NEW): if --mcp-config is provided and no --repo is specified, the orchestrator agent will discover the target repository using available MCP tools (e.g., GitHub MCP server) based on the prompt.
    • If the orchestrator cannot determine which repository is required, it will add an error to the workflow state (orchestrator_errors list) instead of proceeding with changes.
    • If the orchestrator calls repo_change and the repo-change workflow fails for a repo (e.g., clone/apply/review/submit error), the error is captured and added to orchestrator_errors instead of crashing the entire run.
    • If MCP tool calls fail (e.g., invalid GitHub search syntax, API errors), the error is captured and added to orchestrator_errors instead of crashing the entire run.

Repo processing behavior:

  • Dedup + normalization: repo inputs are normalized to GitHub HTTPS URLs and deduplicated.
  • Optional relevance filter: if a relevance_prompt is present, each repo is evaluated and only relevant repos get changes applied.
    • Relevance decisions are cached on disk at ~/.pr-creator/relevance-cache.json, keyed by (repo_identifier, evaluated_sha, prompt_hash), to avoid repeating the same checks across runs.
  • Review before commit: after changes are applied, the Cursor agent reviews the repo's uncommitted state and may request a single follow-up apply pass before committing.
  • Repeatable reruns: set --change-id to use stable branch naming (<change_id>/<slug>) and a stable workspace path under --working-dir (useful for rerunning after fixes).

Environment variables

GitHub auth (required for PR creation)

  • GITHUB_TOKEN — GitHub token used for clone/push/PR creation (used when --github-token is omitted).
    • If unset, the workflow can still run, but push/PR creation is skipped.

Agent selection

  • CHANGE_AGENT — choose change agent; default cursor.
  • EVALUATE_AGENT — choose evaluate agent; default cursor.
  • REVIEW_AGENT — choose review agent; default cursor.

Cursor agent runtime (how the change agent is executed)

Common (Docker + CLI):

  • CURSOR_API_KEY — passed to the cursor agent.
  • CURSOR_RUNNER — how to run cursor-agent; docker or cli (default: docker).
  • CURSOR_ENV_KEYS — comma-separated env keys forwarded to the agent; default CURSOR_API_KEY.
  • CURSOR_MODEL — cursor model to use; default gpt-5.2.
  • CURSOR_MODEL_<INTENT> — override model for a specific Cursor usage intent (e.g. CURSOR_MODEL_CHANGE to use a different model just for the change/apply agent). If unset, falls back to CURSOR_MODEL.
  • CURSOR_STREAM_OUTPUT — enable streaming cursor output to console; set to true to enable verbose streaming (default: false).
  • CURSOR_STREAM_SHOW_THINKING — when streaming output is enabled, include thinking output; set to 1|true|yes|on to enable (default: off).
  • PR_CREATOR_CURSOR_OUTPUT_LOG_DIR — directory to write per-run full raw cursor-agent output logs (default: ~/.pr-creator/cursor-output-logs).

Docker runner only (CURSOR_RUNNER=docker):

  • CURSOR_IMAGE — docker image for cursor agent; default leonpatmore2/cursor-agent:latest.

CLI runner only (CURSOR_RUNNER=cli):

  • CURSOR_CLI_BIN — cursor-agent binary name/path (default: cursor-agent).
  • CURSOR_WORKSPACE_ROOT — workspace root passed to cursor-agent (default: common path of repo + context roots).
  • CURSOR_AGENT_TIMEOUT_SECONDS — hard timeout for a single cursor-agent run; if exceeded the process is killed. Default: 1200 (20 minutes). Set to 0 to disable.
  • CURSOR_AGENT_HEARTBEAT_SECONDS — emit a “still running” message when no output is seen for this many seconds (default: 30).

Orchestration

  • ORCHESTRATOR_MODEL — pydantic-ai model used by the orchestration step; default openai:gpt-5.2.
  • MAX_PARALLEL_REPOS — maximum number of repositories to process concurrently during orchestration; default 3. Increase for faster processing of many repos, decrease to reduce resource usage.

LiteLLM (optional)

  • LITELLM_API_BASE — LiteLLM API base URL (e.g., http://localhost:4000). If set, pydantic-ai agents (e.g., orchestrator, CI summarizer, naming agent) will use LiteLLM as a proxy to access various LLM providers.
  • LITELLM_API_KEY — LiteLLM API key for authentication. Required when LITELLM_API_BASE is set.

MCP servers (optional)

  • MCP_CONFIG — path to MCP servers configuration file (JSON format). Defaults to ~/.pr-creator/mcp-servers.json if that file exists. If provided (via env or CLI), the orchestrator will load MCP servers as tools, enabling it to access external resources like GitHub repositories for planning context.
  • ORCHESTRATOR_MCP_MAX_RETRIES — max retries for MCP tool calls (default: 5). Useful for flaky networks or transient GitHub/MCP failures.

Agent context (optional)

  • AGENT_CONTEXT_ROOTS — comma-separated absolute paths on your machine to mount read-only into the agent workspace for extra repo context (available under /workspace/context/<n> inside the agent).

Apply safety (optional)

  • CHANGE_ALLOWED_PATHS — comma-separated glob patterns of paths the change agent is allowed to modify. If set, any changes outside this allowlist are reverted after the change agent runs (useful for strict tasks like “only edit README.md”).

Prompt sources (optional)

  • PROMPT_CONFIG_OWNER — GitHub owner for prompt config loading when --prompt-config-owner is omitted.
  • JIRA_BASE_URL — Jira base URL (e.g., https://your-org.atlassian.net) when using --jira-ticket.
  • JIRA_EMAIL — Jira account email for API token auth when using --jira-ticket.
  • JIRA_API_TOKEN — Jira API token when using --jira-ticket.

Repo discovery (optional)

  • DATADOG_API_KEY / DATADOG_APP_KEY — required if using Datadog repo discovery.
  • GITHUB_DEFAULT_ORG — default GitHub org/owner to prepend when repo args are provided without an owner (e.g., --repo my-repo -> github.com/<org>/my-repo.git). Also provided as context to the orchestrator agent when discovering repositories.

PR submission & branch naming

  • SUBMIT_CHANGE — submitter; default github.
  • SUBMIT_PR_BASE — target base branch; default repo default.
  • SUBMIT_PR_BODY — PR body prefix; default Automated changes generated by pr-creator. The full PR description will include this prefix followed by the original user prompt (under "## Original Request") and any repo-specific context from the orchestrator (under "## Repository-Specific Context").
  • DEFAULT_BRANCH_PREFIX — branch name prefix used when no change_id is provided; default auto/pr.

Naming generation

  • NAMING_MODEL — pydantic-ai model used to generate branch/PR names (default: openai:gpt-5.2).
  • NAMING_MAX_ATTEMPTS — max number of naming generation attempts per repo (default: 4).
  • NAMING_BACKOFF_BASE — exponential backoff base for naming retries (default: 3.0).
  • NAMING_BACKOFF_MIN — minimum backoff time in seconds for naming retries (default: 5.0).
  • NAMING_BACKOFF_MAX — maximum backoff time in seconds for naming retries (default: 60.0).

Relevance evaluation retries

  • EVALUATE_MAX_ATTEMPTS — max number of relevance evaluation retry attempts per repo (default: 4).
  • EVALUATE_BACKOFF_BASE — exponential backoff base for evaluation retries (default: 3.0).
  • EVALUATE_BACKOFF_MIN — minimum backoff time in seconds for evaluation retries (default: 5.0).
  • EVALUATE_BACKOFF_MAX — maximum backoff time in seconds for evaluation retries (default: 60.0).

Change agent (apply step) retries

  • APPLY_MAX_ATTEMPTS — max number of change agent retry attempts per repo (default: 4).
  • APPLY_BACKOFF_BASE — exponential backoff base for change agent retries (default: 3.0).
  • APPLY_BACKOFF_MIN — minimum backoff time in seconds for change agent retries (default: 5.0).
  • APPLY_BACKOFF_MAX — maximum backoff time in seconds for change agent retries (default: 60.0).

Note: The backoff formula is min * (base^attempt), capped at max. With the defaults above, retries occur at: 5s → 15s → 45s → 60s.

Review loop

  • REVIEW_MAX_ATTEMPTS — max number of review→apply retries per repo when the review step returns CHANGES_REQUIRED (capped at 2, default: 2).

Review step retries (unexpected errors)

  • REVIEW_STEP_MAX_ATTEMPTS — max number of in-step retries when the review agent errors (default: 4).
  • REVIEW_STEP_BACKOFF_BASE — exponential backoff base for review step retries (default: 3.0).
  • REVIEW_STEP_BACKOFF_MIN — minimum backoff time in seconds for review step retries (default: 5.0).
  • REVIEW_STEP_BACKOFF_MAX — maximum backoff time in seconds for review step retries (default: 60.0).

CI / GitHub Actions (post-submit wait + auto-fix loop)

  • CI_FIX_MAX_ATTEMPTS — max number of CI-fix→apply retries per repo when checks fail (default: 2).
  • CI_WAIT_TIMEOUT_SECONDS — max time to wait for checks per attempt (default: 1800).
  • CI_WAIT_POLL_SECONDS — poll interval while waiting (default: 15).
  • CI_WAIT_HEARTBEAT_SECONDS — heartbeat log interval while waiting for checks (default: 120).
  • CI_PENDING_NO_CHECKS_GRACE_SECONDS — if GitHub reports pending but no check-runs or commit-status contexts appear, stop waiting after this many seconds (default: 60).
  • CI_ACCEPTABLE_CONCLUSIONS — comma-separated conclusions treated as “passing” (default: success,skipped,neutral).
  • CI_MAX_LOG_BYTES — max bytes to download from a logs archive (default: 5000000).
  • CI_MAX_LOG_CHARS — max characters of extracted logs included in the prompt (default: 30000).
  • CI_SUMMARY_MODEL — pydantic-ai model used to summarize CI failures (one per failed check) after CI retries are exhausted (default: openai:gpt-5.2).

Logging & git identity

  • LOG_LEVEL — logging level; default INFO.
  • GIT_AUTHOR_NAME / GIT_AUTHOR_EMAIL — author/committer; defaults to pr-creator placeholders if unset.

CLI arguments

GitHub auth

  • --github-token — GitHub token used for clone/push/PR creation (overrides env GITHUB_TOKEN).

Prompts

  • --prompt — main prompt text. Required unless using prompt config.
  • --relevance-prompt — relevance filter prompt. Required unless using prompt config.

Jira prompt source

  • --jira-ticket — Jira ticket id (e.g., ENG-123) to build the prompt from its summary/description.
  • --jira-base-url — Jira base URL (env fallback: JIRA_BASE_URL).
  • --jira-email — Jira user email for API token auth (env fallback: JIRA_EMAIL).
  • --jira-api-token — Jira API token (env fallback: JIRA_API_TOKEN).
  • When using --jira-ticket, the change id is automatically set to the Jira ticket id for stable branch names.

Prompt config (alternative to passing prompts directly)

  • --prompt-config-owner — GitHub owner of the prompt config repo (env fallback: PROMPT_CONFIG_OWNER). Must be set with --prompt-config-repo and --prompt-config-path.
  • --prompt-config-repo — GitHub repo name containing the prompt config file.
  • --prompt-config-ref — git ref for the prompt config file; default main.
  • --prompt-config-path — path to the YAML prompt config file inside the repo.

Change ID (for static branches)

  • --change-id — Change ID to use for static branch names. When provided, ensures re-runs use the same branch name (format: {branch_prefix}-{change_id}). Can also be set in prompt config YAML (takes precedence over CLI arg).

Repositories

  • --repo — repository URL to process. Can be passed multiple times. Optional; if omitted, orchestrator will attempt to discover repos.

Datadog discovery

  • --datadog-team — Datadog team name to discover repos (requires DATADOG_API_KEY and DATADOG_APP_KEY).
  • --datadog-site — Datadog API base URL; default https://api.datadoghq.com.

MCP integration

  • --mcp-config — path to MCP servers configuration file (JSON format). Defaults to ~/.pr-creator/mcp-servers.json if that file exists. Enables the orchestrator to:
    • Access external tools (e.g., GitHub API for reading repos)
    • Discover target repositories when --repo is not specified
    • Gather context from multiple sources before planning changes
    • See pydantic-ai MCP docs for config format.

Runtime

  • --working-dir — where repos are cloned; default ~/.pr-creator/repos.
  • --log-level — logging level; default INFO.
  • --context-root — host directory to mount (read-only) into the agent workspace for extra context; can be passed multiple times (env equivalent: AGENT_CONTEXT_ROOTS).
  • --secret — forward a secret to the change agent as an env var (KEY=VALUE); can be passed multiple times.
  • --secret-env — forward an env var (by name) from the current process into the change agent; can be passed multiple times.

Workspace management

  • Workspaces live under --working-dir (default ~/.pr-creator/repos); directories are auto-created per repo.
  • When --change-id is set, the workspace path is deterministic (<repo>-<change_id>) and reused across runs so the same branch can be reapplied.
  • Without --change-id, a fresh workspace with a random suffix is created and cleaned up after each repo finishes.
  • To start fresh, remove the working directory (e.g., rm -rf ~/.pr-creator/repos).

Orchestration (default)

By default, pr-creator runs a per-repo orchestration step before applying changes:

  • Repo discovery
  • For each repo:
    • Workspace + relevance check
    • Orchestrate: derive a repo-specific prompt (read-only analysis)
    • Apply changes + review + submit PR + wait for CI + cleanup Orchestration is always enabled.

Example (Docker)

docker run --rm \
  -e CURSOR_API_KEY \
  -e GITHUB_TOKEN \
  leonpatmore2/pr-creator:latest \
  --prompt-config-owner LeonPatmore \
  --prompt-config-repo pr-creator \
  --prompt-config-ref main \
  --prompt-config-path examples/prompt-config.yaml \
  --repo https://github.com/LeonPatmore/cheap-ai-agents-aws \
  --working-dir /tmp/repos \
  --log-level INFO

MCP Server Integration (Optional)

The orchestrator can use Model Context Protocol (MCP) servers to access external tools and context. The most common use case is the GitHub MCP server for repository discovery and exploration.

Use Cases

  1. Automatic repository discovery: Run pr-creator without specifying --repo, and let the orchestrator find the target repo based on the prompt
  2. Context gathering: Orchestrator can explore repositories, check README files, search for patterns, etc., before planning changes

Setup

  1. Create an MCP configuration file (mcp-servers.json):
{
  "mcpServers": {
    "github": {
      "command": "docker",
      "args": [
        "run", "-i", "--rm",
        "-e", "GITHUB_PERSONAL_ACCESS_TOKEN",
        "ghcr.io/github/github-mcp-server"
      ],
      "env": {
        "GITHUB_PERSONAL_ACCESS_TOKEN": "${GITHUB_TOKEN}"
      }
    }
  }
}
  1. Run pr-creator with MCP enabled:
# Example 1: Let orchestrator discover the repo
pr-creator \
  --prompt "Add a health check endpoint to the main API service" \
  --mcp-config mcp-servers.json \
  --working-dir ~/.pr-creator/repos

# Example 2: Use MCP for context gathering with explicit repo
pr-creator \
  --prompt "Update dependencies based on the README requirements" \
  --repo https://github.com/owner/repo \
  --mcp-config mcp-servers.json \
  --working-dir ~/.pr-creator/repos

Note: The orchestrator agent will use GitHub MCP tools to search repositories, read files, and gather context before planning changes.

CLI Output

The CLI outputs a JSON summary to stdout with the following structure:

{
  "irrelevant_repos": ["https://github.com/owner/repo1"],
  "created_prs": [
    {
      "repo_url": "https://github.com/owner/repo2",
      "branch": "auto/pr-update-deps",
      "pr_url": "https://github.com/owner/repo2/pull/123",
      "pushed_sha": "abc123..."
    }
  ],
  "orchestrator_errors": [
    "Could not determine target repository from prompt..."
  ]
}

Fields:

  • irrelevant_repos: List of repository URLs that were filtered out by the relevance check
  • created_prs: List of PR records for repositories where changes were successfully applied (at most one record per repo; retries update/overwrite the record instead of appending duplicates)
  • orchestrator_errors: List of error messages from the orchestrator (e.g., when it cannot determine which repositories to target). Empty list if no errors occurred.

Developer

Commands

  • pipenv run python -m pr_creator.cli --prompt "<prompt>" --relevance-prompt "<relevance>" --repo <repo_url> --working-dir ~/.pr-creator/repos
  • make test-e2e — run the e2e pytest (requires env vars set; pytest will load repo-root .env if present).
  • make lint — flake8.
  • make format — black (requires Python ≥3.12.6).

About

Automate multi-repo changes: clone repos, apply edits via AI agents, and open GitHub PRs.

Topics

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages