A propose-only GitHub Actions bot that reviews Java pull requests with fine-tuned GPT models, classifies each changed hunk as a bug, vulnerability, or code smell, and posts one aggregated PR comment with concrete refactor suggestions — without ever touching your code or failing CI.
Generic static analyzers flag technical debt with broad, rule-based heuristics and no explanation of why something matters or what to do about it. Reviewers end up doing that translation manually, PR after PR. This project wires two fine-tuned OpenAI models — one for classification, one for refactor suggestions — into a small autonomous agent that inspects a PR's Java diff, decides for itself which files and tools to invoke next, and reports findings the way a human reviewer would: inline, per-hunk, with a short "what to change and why."
flowchart LR
A["PR labeled td-agent"] --> B["GitHub Actions<br/>td_agent.yml"]
B --> C["scripts/td_agent_pr.py"]
C --> D["Agent loop<br/>agent/loop.py + planner.py"]
D --> E["InspectDiff<br/>GitHub API diff"]
E --> F["TDAnalyzeFile<br/>td_engine/analyzer.py"]
F --> G["TDRefactorFile<br/>td_engine/refactorer.py"]
G --> H["PostPRComment"]
G --> I["CreateOrUpdateCheck"]
H --> J["Single aggregated<br/>PR comment"]
I --> K["Technical Debt Agent check run<br/>status: success"]
The agent loop is plan → act → observe: on each step a planner LLM (or a deterministic fallback if no planner model is configured) decides which registered tool to call next — InspectDiff, TDAnalyzeFile, TDRefactorFile, RunTests, PostPRComment, or CreateOrUpdateCheck — based on a compact JSON summary of what's been analyzed so far. It stops once every changed Java file has been analyzed and suggestions exist for every detected issue.
| Layer | Tools / Technologies |
|---|---|
| Trigger | GitHub Actions (pull_request, push), td-agent label gate |
| Classification LLM | OpenAI fine-tuned GPT-3.5 (TD_CLASSIFICATION_MODEL_ID) |
| Refactor LLM | OpenAI fine-tuned GPT-3.5 (TD_REFACTOR_MODEL_ID) |
| Planner LLM | OpenAI GPT-4.1 (TD_PLANNER_MODEL_ID), with a deterministic fallback planner |
| Agent orchestration | Custom Python plan-act-observe loop (agent/) |
| GitHub integration | GitHub REST API via requests (diffs, PR comments, check runs) |
| Target language | Java (.java hunks; sample_java/ fixture for manual testing) |
| Testing / CI | pytest, ruff, GitHub Actions (ci.yml) |
# 1. Clone
git clone https://github.com/GENAI-CODEDEBT/Code_debt_agentic.git
cd Code_debt_agentic
# 2. Set up environment
python -m venv .venv
source .venv/bin/activate # or .venv\Scripts\activate on Windows
pip install -e ".[dev]" # or: pip install -r requirements.txt
# 3. Configure environment variables
cp .env.example .env
# fill in .env with your own OpenAI key and model IDs — see .env.example- Add these as repository secrets (Settings → Secrets and variables → Actions):
OPENAI_API_KEY,TD_CLASSIFICATION_MODEL_ID,TD_REFACTOR_MODEL_ID,TD_PLANNER_MODEL_ID.GITHUB_TOKENis provided automatically.TD_AGENT_TEST_COMMANDis optional and can be left unset. - Open a PR that touches
.javafiles and add thetd-agentlabel — the workflow exits immediately without it. - Push changes or re-label the PR to re-trigger. The bot analyzes up to
MAX_FILES_TO_ANALYZEchanged Java files, comments with its findings, and creates a passing Technical Debt Agent check run.
export GITHUB_EVENT_PATH=path/to/a/pull_request/event.json # GitHub webhook payload shape
export GITHUB_EVENT_NAME=pull_request
export GITHUB_SHA=<commit-sha>
python -m scripts.td_agent_prNote: there is currently no dry-run flag — a local run still calls the real GitHub API for posting the comment and check run using whatever GITHUB_TOKEN you export, and the classification/refactor stages call the real OpenAI models. Point GITHUB_EVENT_PATH at a real or synthetic PR event payload for the repo/PR you have access to.
sample_java/src/main/java/com/example/TechDebtDemo.java contains intentional issues (hard-coded secret, null dereference, swallowed exception, reference equality) for exercising the workflow end-to-end: branch it, tweak the file, open a PR, add the td-agent label.
- Analyzes up to
MAX_FILES_TO_ANALYZE(10) changed Java files per PR, capped atMAX_STEPS(6) planner iterations — seeagent/config.py. - Classifies each changed hunk as exactly one of
BUG,VULNERABILITY, orCODE_SMELL, with a heuristic severity (MAJOR,CRITICAL,MINORrespectively). - Generates a 1–3 sentence refactor suggestion per detected issue — explanation only, never a patch.
- Is propose-only and CI-safe by construction: every tool call and the entrypoint's
main()catch their own exceptions, and the process always exits0.
- Hunk-accurate diff parsing without a diff library.
agent/tools.py's_extract_hunks_from_patchwalks GitHub's unified-diff patch text manually to recover per-hunk line ranges, so comments land on the exact lines GitHub shows in the PR view. - Making an LLM-driven loop safe to run unattended in CI. The planner returns free-form JSON, so a malformed or missing response has to degrade to a deterministic fallback (
_fallback_plan) rather than crash the workflow — and the entrypoint wraps everything so a model or API failure reports a clean summary instead of a red build. - Balancing autonomy with a bounded budget. The agent decides its own next tool call, but
max_stepsandmax_files_to_analyzekeep a single PR run from running away in cost or latency.
- Add an
evals/harness that scores classification accuracy against a labeled Java debt dataset - Implement an actual local dry-run mode that skips the GitHub API calls
- Extend beyond Java to other languages via a pluggable hunk-language detector