DEV Community

pyfile-toolkit
pyfile-toolkit

Posted on

I Built a Self-Running GitHub Triage Agent (250 Lines, Free LLM)

I Built a Self-Running GitHub Triage Agent (250 Lines, Free LLM)

Maintainers drown in issue queues. Every repo has a backlog of open issues
that nobody got around to classifying — bugs sitting next to feature
requests next to duplicates. So over a weekend I built an agent that does
the boring part: reads a repo's open issues + PRs, classifies each one,
assigns priority, and renders a dashboard
. ~250 lines, no frameworks,
no build step, and the LLM part costs nothing.

The agent loop (3 steps)

  1. Fetch. GitHub's public REST API lists open items for owner/repo.
  2. Reason. Each item goes to an LLM with a strict JSON output contract: {"category": "bug|feature|question|duplicate|docs", "short": "...", "priority": 1-5}. Because free-tier endpoints get overloaded (hello, HTTP 503), I rotate through ~5 :free models until one answers.
  3. Render. A single-file page (inline CSS/JS, no deps) shows color-coded cards sorted by priority, each linking back to the original issue.

What a real run looks like

On facebook/react (22 open items at scan time): 22/22 classified, zero
errors. Highlights:

  • p1 bug — "Freezing props prevents later assignments causing …"
  • p1 bug — "Unbounded debug info causes RangeError on large …"
  • p1 question — "Performance optimization inquiry…"

On typeorm/typeorm: a clean mix of bug, feature, docs verdicts with
one-word takes like "Remove dayjs dependency to reduce TypeORM
dependencies"
.

Why this matters for agents

The interesting part isn't the code — it's the pattern. A triage agent is
a tiny vertical slice of what autonomous engineering looks like: read real
state, make structured decisions with an LLM, present actionable output.
Same skeleton powers PR review, dependency drift reports, or release-note
generation. Swap the data source, keep the loop.

The honest bits

  • Free LLM tiers are flaky: 503s are the norm on popular models. Model rotation with fallback is non-negotiable.
  • Small models sometimes return sloppy JSON. The parser takes the first {...} block and tolerates markdown noise.
  • GitHub rate limits (60 req/hr unauthenticated) matter for big scans; a PAT removes that.

Try it

export LLM_KEY="<openrouter key>"
node server.mjs   # serves localhost:7811
Enter fullscreen mode Exit fullscreen mode

Open the page, type owner/repo, watch the agent work. Full source:
a single server.mjs (agent loop + rotation) and index.html.

If you maintain an open-source repo — point this at your issues for
10 seconds and tell me if the verdicts feel right. Feedback welcome below.

Top comments (0)