Inspiration

Debugging is slow, and it's slow in a specific way: a stack trace tells you where something broke, but not why. When something fails, the usual move is to copy the error into ChatGPT — but generic AI chatbots can't see your actual codebase, so their answers are educated guesses, not grounded diagnoses.

I wanted to build something that closes that gap: an AI agent that doesn't just read an error message, but actually goes and looks at your real code before saying anything.

What it does

RepoDoctor takes two inputs — a GitHub repository URL and a log/error file — and:

  1. Parses the log for both full Python tracebacks and single-line ERROR/CRITICAL/FATAL messages, including fully-qualified error types like sqlalchemy.exc.OperationalError
  2. Clones the actual repository and pulls the exact lines of code around each failing line — not a snippet the AI imagines, the real code
  3. Checks recent git history on the affected file, so the diagnosis can factor in "this broke right after a recent commit changed X"
  4. Diagnoses each error with an LLM, grounded in that real code and history, returning a root cause, a suggested fix, a confidence level, and a severity rating (critical/high/medium/low)
  5. Correlates errors across the whole batch in a second pass, to catch cases where multiple failures share one underlying root cause
  6. Presents everything in a clean, dark, dashboard-style UI — summary metrics, severity-ranked error cards, before/after diff suggestions, and honest "I couldn't find this file" warnings when confidence is low, instead of guessing silently

How I built it

The whole project runs as a single Streamlit process — no separate backend server. The pipeline logic (log parsing, repo cloning via GitPython, code context extraction, LLM prompting via the OpenAI API) lives in a backend/ module, and the Streamlit frontend calls those functions directly, in-process. This turned out to be the key architectural decision: it made the app trivially deployable on Streamlit Community Cloud with zero extra infrastructure, since there was no second server to stand up or coordinate.

I built it incrementally: get a minimal end-to-end pipeline working first (parse → clone → match → diagnose → display), confirm it with a real API call, then layer in features one at a time — git-history awareness, severity scoring, multi-error correlation, generic log format support, and diff suggestions — testing each addition before moving to the next, since every LLM call costs real API budget.

Challenges I ran into

  • Regex fragility on real-world data. My initial error-type regex only matched simple names like ZeroDivisionError. When I tested RepoDoctor against a completely different, unfamiliar repo of mine (not the one it was built to analyze), it silently failed to detect a real sqlalchemy.exc.OperationalError — because that error type has dots in it, and \w+ doesn't match dots. Testing on a stranger's real repo, rather than just my own project, is what caught this.
  • Streamlit's HTML rendering quirks. Splitting an HTML <div> open tag and its closing tag across two separate st.markdown() calls doesn't nest the way you'd expect — the browser auto-closes the div immediately, leaving empty boxes in the layout. The fix was switching to Streamlit's native st.container(border=True) instead of manually building HTML card wrappers.
  • Architecting for deployment from the start mattered. I originally built this with a separate FastAPI backend that the Streamlit frontend called over HTTP. That worked locally, but Streamlit Community Cloud only runs one process — so before deploying, I refactored the frontend to import and call the backend functions directly, removing the HTTP layer and the FastAPI/uvicorn dependency entirely.

What I learned

  • Testing your own tool on your own code only proves it works on code you already wrote in a way it expects. Testing it on a genuinely unfamiliar repo is what actually proves it generalizes — and it's where the real bugs showed up.
  • Grounding an LLM's output in real, retrieved context (actual code, actual git history) produces meaningfully more trustworthy answers than just feeding it an error message and hoping.
  • Small architectural decisions made early (like whether to keep the backend as a separate service) have outsized effects on how easy deployment ends up being later.

What's next for RepoDoctor

  • Support for stack trace formats beyond Python (JavaScript/Node, Java, etc.)
  • Support for private repositories via authenticated cloning
  • Turning the "before/after" diff suggestion into an actual applied patch or draft pull request, rather than just a suggestion to copy manually

Built With

Share this project:

Updates

Submission history