github-issue-agent
A small, workflow-driven AI coding agent that reads a GitHub issue, writes the code to resolve it (plus tests), verifies the tests pass, and opens a pull request — autonomously. Instead of hardcoding behaviour into the agent, it is driven by:
- Slash-style commands that map to markdown workflow files in
.ai/workflows/. - The target repository's own instruction files (
AGENTS.md,README.md,CONTRIBUTING.md,.github/copilot-instructions.md,.ai/rules/*) — these are the source of truth for how the agent should behave.
- Python import name:
github_issue_agent - Pip / distribution name:
github-issue-agent - CLI command:
github-issue-agent(the shorterai-agentis kept as an alias)
github-issue-agent work-issue https://github.com/org/repo/issues/123
does exactly what you sketched out:
Read issue
↓
Read repo instructions (AGENTS.md, README, CONTRIBUTING, .ai/rules/*)
↓
Build a code map (file tree + selected files)
↓
Plan (LLM)
↓
Implement file edits (LLM)
↓
Run tests ──fail──▶ feed failure back to LLM (up to N times)
↓ pass
Commit ▸ Push ▸ Open PR
Why this design
Commands are data, not code. Every .ai/workflows/<name>.md automatically
becomes a runnable command, so adding /fix-bug, /add-feature, /review-pr,
etc. is just adding a markdown file — no code changes. The repo itself becomes the
behaviour spec, which scales far better than stuffing everything into one prompt.
Demo (live run)
A real run against this repository, using the OpenRouter provider with the free
openai/gpt-oss-120b:free model:
LLM_PROVIDER=openrouter OPENROUTER_MODEL=openai/gpt-oss-120b:free \
ai-agent work-issue https://github.com/amey1234444/work-issue-agent/issues/2 --path .
Output:
Fetched issue #2: Add a CONTRIBUTING.md with contribution guidelines
Loaded 2 instruction file(s), 2 rule(s).
== Planning (openrouter) ==
Understanding:
The issue requests adding a new CONTRIBUTING.md file at the repository root ...
Plan:
1. Create a new file CONTRIBUTING.md at the repository root ...
2. Ensure the markdown follows the style of existing docs and is concise.
3. Run the test suite (pytest -q) and lint (ruff check .) ...
4. Provide the test command (pytest -q) for the PR metadata.
== Implementing (attempt 1/3) ==
Changes:
created CONTRIBUTING.md
Running: ['pytest -q']
Tests passed.
Summary: Added CONTRIBUTING.md with concise contribution guidelines
Opened PR: https://github.com/amey1234444/work-issue-agent/pull/3
The agent read issue #2,
planned, wrote CONTRIBUTING.md, ran the test suite, and opened
PR #3 — fully autonomously:
Verification
Lint, type-check and the test suite all pass:
$ ruff check .
All checks passed!
$ mypy github_issue_agent
Success: no issues found in 12 source files
$ pytest -q
....................... [100%]
23 passed in 0.16s
(The screenshot below is from an earlier run; the suite has since grown as features/tests were added.)
Install
From a local clone (for development):
git clone https://github.com/amey1234444/work-issue-agent.git
cd work-issue-agent
python -m venv .venv && source .venv/bin/activate
pip install -e ".[all]" # or .[anthropic] / .[openai]
Directly from GitHub (e.g. in Google Colab or any fresh environment — no clone needed):
pip install "github-issue-agent[openai] @ git+https://github.com/amey1234444/work-issue-agent.git"
Then from github_issue_agent import work_issue works immediately. It is not
on PyPI, so install from the git+https://... URL above rather than
pip install github-issue-agent. The [openai] extra pulls the SDK used for the
OpenAI and OpenRouter providers; the mock provider needs no extra and no key.
Configure
Copy .env.example to .env and fill in:
cp .env.example .env
| Variable | Purpose |
|---|---|
LLM_PROVIDER |
anthropic | openai | openrouter | mock |
ANTHROPIC_API_KEY |
required when provider is anthropic |
OPENAI_API_KEY |
required when provider is openai |
OPENROUTER_API_KEY |
required when provider is openrouter |
OPENROUTER_MODEL |
OpenRouter model slug (default openai/gpt-oss-120b:free) |
GITHUB_TOKEN |
classic PAT with repo scope (read issues, open PR) |
AGENT_MAX_ITERATIONS |
self-correction loops on test failure (default 3) |
mock needs no API key and returns a deterministic response — handy for trying
the pipeline end-to-end offline.
OpenRouter is an OpenAI-compatible gateway to hundreds of models; a free-tier
key can run :free model slugs (e.g. openai/gpt-oss-120b:free). Set
LLM_PROVIDER=openrouter and OPENROUTER_API_KEY=....
Usage
List the commands available in a repo (discovered from .ai/workflows/):
github-issue-agent list --path /path/to/target/repo
Resolve an issue and open a PR:
github-issue-agent work-issue https://github.com/org/repo/issues/123 --path /path/to/repo
Run any workflow with a free-form prompt instead of an issue:
github-issue-agent run add-feature --prompt "Add a --json flag to the export command" --path .
Useful flags:
--dry-run— plan only, make no edits (great for inspecting what the agent intends).--no-pr— apply edits and run tests locally, but don't commit/push/open a PR.--provider mock|anthropic|openai— override the provider for one run.--base <branch>— base branch for the PR (defaults to the repo's default branch).
Use as a library
The agent is also importable, so you can drive a run from your own Python code, a backend service or a notebook — pass the API key and issue URL as arguments and it does the rest:
from github_issue_agent import work_issue
result = work_issue(
"https://github.com/org/repo/issues/123",
provider="openrouter", # "anthropic" | "openai" | "openrouter" | "mock"
api_key="sk-or-...", # optional; falls back to the provider's env var
model="z-ai/glm-4.5-air:free", # optional; overrides the default for the provider
repo_path="/path/to/local/checkout",
github_token="ghp_...", # optional; falls back to GITHUB_TOKEN / GITHUB_PAT
open_pr=True, # False = apply + test only, no commit/push/PR
)
print(result.tests_passed) # bool
print(result.pr_url) # str | None
print(result.summary) # the model's summary of what changed
print(result.changed_files) # list[str]
work_issue(...) is sugar for run_workflow("work-issue", issue_url=...). Use
run_workflow(<name>, prompt=...) to drive any other workflow (e.g. fix-bug,
add-feature) with a free-form prompt instead of an issue. Pass an on_event
callback (kind, message) to stream progress, or set dry_run=True to get just
the plan. Unrecoverable problems raise github_issue_agent.AgentError; everything
else comes back on the WorkflowResult.
How to use — step by step
The most common goal is "resolve issue X and open a PR". Here is the full recipe.
1. Have a local checkout of the target repo (the repo the issue lives in). The agent edits and runs tests on a real working copy:
git clone https://github.com/org/target-repo.git
2. Make sure the target repo has a workflow file. The agent only runs commands
that exist as .ai/workflows/<name>.md in the target repo. At minimum it needs
.ai/workflows/work-issue.md. It also reads AGENTS.md and .ai/rules/* as the
"rules of the house" (e.g. "use Java 21", "only additive pom.xml changes"). The
better these instructions, the better the result — see What to expect below.
3. Provide credentials:
- An LLM key for your chosen provider (
OPENROUTER_API_KEY,OPENAI_API_KEY, orANTHROPIC_API_KEY).mockneeds none. - A GitHub token (
GITHUB_TOKEN, classic PAT withreposcope) — only needed when you actually want it to open a PR.
4a. Run it from the CLI:
LLM_PROVIDER=openrouter OPENROUTER_MODEL=z-ai/glm-4.5-air:free \
GITHUB_TOKEN=ghp_... OPENROUTER_API_KEY=sk-or-... \
github-issue-agent work-issue https://github.com/org/target-repo/issues/2 \
--path ./target-repo
4b. …or from Python (e.g. a Colab notebook):
from github_issue_agent import work_issue
result = work_issue(
"https://github.com/org/target-repo/issues/2",
provider="openrouter",
api_key="sk-or-...",
model="z-ai/glm-4.5-air:free",
repo_path="./target-repo",
github_token="ghp_...",
open_pr=True,
)
print(result.pr_url, result.tests_passed, result.summary)
5. Inspect the result. On success you get a PR URL and tests_passed=True. Tips:
- Start with
dry_run=True(library) or--dry-run(CLI) to see only the plan. - Use
open_pr=False/--no-prto apply edits and run tests without pushing — good for reviewing the diff locally first. - Begin with
provider="mock"to smoke-test the wiring with no key and no network.
What to expect
What it actually does. For each run the agent:
- Reads the issue and the target repo's instruction files + a file tree.
- Plans (LLM) — which files to read and the steps to take.
- Implements (LLM) — writes the real production code that resolves the issue
and adds/updates tests. It is not a test-only generator: in the live
NEWS-PLATFORM #2 run it
created
RefinerService.java(the actual feature), wired it into the consumer and controller, and added 10 JUnit tests. - Verifies — runs the repo's test command. If it fails, the test output is fed
back to the model to fix, repeating up to
AGENT_MAX_ITERATIONStimes. - Opens a PR — branch, commit, push, and open a PR that references the issue.
So the loop is Plan → Implement (code + tests) → Test → self-correct → PR. The tests are how the agent checks its own implementation; they are not the deliverable by themselves.
A run is considered successful when the code compiles/runs, the test command
exits 0, and (if open_pr=True) a PR is opened. WorkflowResult reports
tests_passed, pr_url, summary, changed_files, plan, and branch.
What it is not. It is not magic and not deterministic — output quality depends
on (a) the model you pick and (b) the quality of the repo's .ai/ rules.
Honest limitations observed in practice:
- Weak/free models can produce compiling-but-wrong code on early attempts (e.g. a
corrupted
pom.xml, or a Java regex escaping typo) and only converge after the repo's.ai/rulesare tightened. Stronger models need fewer guardrails. - It needs the real toolchain present to verify (e.g. JDK + Maven for a Java repo,
or
pytestfor Python). Withopen_pr=Falseit still plans/edits/tests locally. - Free-tier API keys have low rate limits; a full run makes several model calls and
can hit
429s. Reasoning models are handled automatically (reasoning disabled so they return an answer instead of burning the token budget "thinking"). - It works on one issue at a time and expects a local checkout it can modify.
Rule of thumb: treat it like a junior engineer who follows instructions literally.
Clear AGENTS.md + .ai/rules/* + a capable model → clean, passing PRs. Vague
instructions + a weak model → it may need a few iterations or a human nudge.
How a run works
- Context (
github_issue_agent/context.py) — reads instruction files,.ai/rules/*, and agit ls-filesfile tree of the target repo. - Plan (
github_issue_agent/workflow.py::Agent.plan) — the LLM returns a JSON plan listing the files it needs to read and the steps it will take. - Implement (
Agent.implement) — the agent loads the requested files and the LLM returns JSON file edits + a test command + PR metadata. - Apply & test (
github_issue_agent/editor.py,github_issue_agent/runner.py) — edits are applied (with a guard against escaping the repo root) and the test command runs. On failure the output is fed back to the LLM, up toAGENT_MAX_ITERATIONS. - PR (
github_issue_agent/git_ops.py,github_issue_agent/github_client.py) — branch, commit, push and open a pull request via the GitHub REST API.
Project layout
github_issue_agent/
api.py # importable library API: work_issue() / run_workflow()
cli.py # argparse entrypoint; turns .ai/workflows/*.md into commands
config.py # .env + env + .ai/config.yaml loading
context.py # gather instruction files + file tree + selected files
github_client.py # fetch issues, create repos/PRs (GitHub REST)
llm.py # provider abstraction: anthropic | openai | openrouter | mock
workflow.py # planning/coding loop + robust JSON extraction
editor.py # apply file edits safely
runner.py # run tests/lint and capture output
git_ops.py # branch / commit / push helpers
models.py # typed dataclasses (Issue, Plan, FileEdit, Implementation)
.ai/
config.yaml # which instruction files/rules/test command to use
workflows/*.md # the commands (work-issue, fix-bug, add-feature, ...)
prompts/*.md # optional overrides for planner/coder system prompts
rules/*.md # extra rules injected into context
tests/ # pytest suite
Extending
- New command: drop a markdown file in
.ai/workflows/. It is instantly available viagithub-issue-agent run <name>. - Different behaviour per repo: edit that repo's
AGENTS.md/.ai/rules/*. - Custom prompts: add
.ai/prompts/planner.mdor.ai/prompts/coder.md.
This is the Option 1 (local CLI MVP) from the design discussion. The clean module boundaries make it straightforward to later wrap in a backend service, a queue and multiple specialised agents (planner/coder/tester/reviewer) once the MVP reliably solves issues.
License
MIT
Metadata
Release files for github-issue-agent 0.1.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | |
|---|---|---|---|---|
| github_issue_agent-0.1.0.tar.gz | 30.3 kB | Without provenance | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| github_issue_agent-0.1.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 56.7 kB
Release files / github_issue_agent-0.1.0.tar.gz
| Download URL | github_issue_agent-0.1.0.tar.gz |
|---|---|
| Size | 30.3 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
963e8912fcff87612a1d9bb8fec6791861d1fa3b60db7854fade5e22973c7b7f
|
|
BLAKE2b-256 checksum How to use checksums |
25af8b813ac31bf1962db051ae10701b251188f3992d13865ffbfbdba3cea5f1
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.12.8
|
Release files / github_issue_agent-0.1.0-py3-none-any.whl
| Download URL | github_issue_agent-0.1.0-py3-none-any.whl |
|---|---|
| Size | 26.4 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
0bcb377f5fb20a31dbeec86e199f3e82007ca505f9a9fb32d6ebe3762aed3a43
|
|
BLAKE2b-256 checksum How to use checksums |
a7b97a0df0df032df11170fb843c7c70a01cb510b82223e24edcf608ceeeafbf
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.12.8
|
Security for github-issue-agent 0.1.0
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.