ContribGuide — Project Story

Inspiration

I'm a second-year CS student with a long-term goal of contributing to open source. And like most people who've tried, I hit the same wall every time.

You find a repo you care about. You open the issues tab. There are 400 of them. Half are too complex, a quarter are already being worked on, and the rest have no context. You spend two hours reading threads, still don't know where to start, and close the tab.

That experience — repeated across thousands of developers every day — is what ContribGuide is built to solve. The barrier to a first OSS contribution isn't skill. It's orientation. Nobody tells you which issue fits your skill level, what the codebase context is, or what a specific part of the code does when you're stuck.

I built ContribGuide because I wanted it to exist for myself. And because I believe the first contribution is the hardest one — and the most important one to get right.


What It Does

ContribGuide is a Slack agent with a unified onboarding flow built around three slash commands:

/contrib-start — Skill profiling + ML issue matching

The agent runs a 4-question onboarding conversation — repo URL, preferred languages, experience level, and time available. It builds a contributor profile, checks for language mismatch between the contributor and the repo, fetches open GitHub issues, and ranks them using semantic ML.

Each recommended issue shows:

  • A match score based on semantic similarity
  • A predicted success probability using a weighted feature model:

$$P(\text{success}) = 0.35 \cdot s_{\text{semantic}} + 0.30 \cdot s_{\text{difficulty}} + 0.20 \cdot s_{\text{comments}} + 0.15 \cdot s_{\text{labels}}$$

Where:

  • $s_{\text{semantic}}$ — cosine similarity between contributor profile vector and issue vector
  • $s_{\text{difficulty}}$ — fit between user level and estimated issue difficulty from labels
  • $s_{\text{comments}}$ — inverse comment count (more comments = more complex)
  • $s_{\text{labels}}$ — presence of beginner-friendly labels (good first issue, help wanted)

  • A plain-English match reason generated by Groq explaining why this specific issue fits this specific contributor

Once the contributor picks an issue, ContribGuide fetches the issue body and top comments, then generates a context brief — what the issue is about, what's been discussed, and a concrete first step tailored to their skill level.

/contrib-explain — Codebase Q&A

After picking an issue, contributors can ask any question:

/contrib-explain what is cProfile and how does it relate to this issue?
/contrib-explain which files should I look at first?
/contrib-explain what does this label mean?

ContribGuide fetches the repo's file tree and uses Groq to answer in full context — always useful, no dependency on chat history.

/contrib-status — Health check

Verifies all four services are live: Slack bot, GitHub API, Groq API, and ML embedder. Judges can run this first to confirm everything is operational.


How I Built It

Architecture:

User (Slack)
    │
    ▼
Slack Bolt (Socket Mode) + Block Kit UI
    │
    ├── /contrib-start ──→ Skill profiling conversation
    │                          │
    │                          ├──→ GitHub API (issues + language detection)
    │                          ├──→ ML Service
    │                          │     ├── sentence-transformers (embeddings)
    │                          │     ├── Cosine similarity ranker
    │                          │     └── Success predictor
    │                          └──→ Groq API (match reason + context brief)
    │
    ├── /contrib-explain ──→ GitHub API (file tree)
    │                            └──→ Groq API (contextual answer)
    │
    └── /contrib-status ──→ Health checks across all services

Tech stack:

  • Slack Bolt (Socket Mode) — agent framework
  • Slack Block Kit — structured rich UI throughout
  • GitHub REST API — issue fetching, language detection, file tree
  • sentence-transformers (all-MiniLM-L6-v2) — semantic embeddings, CPU-only, ~80MB
  • Groq API (llama-3.1-8b-instant) — free tier LLM
  • FastAPI — ML microservice
  • Python 3.10+

The ML layer:

The semantic ranker embeds both the contributor profile and each issue as dense vectors, then computes cosine similarity. Since vectors are L2-normalized at embedding time, the dot product equals cosine similarity — making batch ranking a single matrix multiply:

$$\text{scores} = V_{\text{issues}} \cdot v_{\text{profile}}$$

Where $V_{\text{issues}} \in \mathbb{R}^{n \times d}$ is the issue embedding matrix and $v_{\text{profile}} \in \mathbb{R}^{d}$ is the contributor profile vector.

A skill-difficulty penalty term downranks issues that are too far above or below the contributor's level:

$$\text{score}{\text{final}} = \text{score}{\text{semantic}} - 0.1 \times |\text{level}{\text{user}} - \text{level}{\text{issue}}|$$

The full test suite covers 29 unit tests + a full integration test simulating the complete judge experience — all passing before submission.


Challenges

Module-level token loading

The single most stubborn bug: a module-level HEADERS dict in the GitHub client captured the token at import time — before load_dotenv() ran. Every token rotation failed silently. Fix: converted HEADERS to a get_headers() function so the token is read at call time, not import time.

Slack URL format mangling

When a user pastes a GitHub URL in Slack, it becomes <https://github.com/owner/repo|display_text> internally. The GitHub API client received this and 404'd. Fixed with a regex pre-processor in parse_repo_url.

Language mismatch infinite loop

When a user acknowledged a mismatch warning and said "yes" to continue, the agent called send_ranked_issues again — re-triggering the same warning, creating an infinite loop. Fixed with a lang_override flag in the session.

Windows DLL policy blocking PyTorch

sentence-transformers uses PyTorch which was blocked by Windows Application Control policy. Solved by switching to WSL for development.

Conversation state management

Managing a multi-turn flow where each message means something different depending on where the user is — handling re-entry, mid-flow restarts, typo tolerance, word/number/letter input normalization — required careful session state design from the start.


What I Learned

  • Agentic conversation state is harder than it looks. Multi-turn flows with edge cases, re-entry, and input normalization need careful design upfront — not as an afterthought.

  • The LLM is the explanation layer, not the reasoning layer. Ranking and prediction are done deterministically by ML models. Groq generates natural language. Keeping these roles separate makes the system more reliable and debuggable.

  • Module import order matters more than you think. Several hours were lost to variables being set at import time before environment variables loaded. Always initialize lazily inside functions when dealing with secrets.

  • Build for the demo from day one. Every feature was designed with a clear demo moment — the ranked issues card, success prediction badge, match reason, context brief, codebase Q&A. If you can't show it in 30 seconds, reconsider it.


Impact — Track 2: Agent for Good

Open source contribution is one of the most effective paths into the tech industry — but the entry barrier disproportionately affects developers without mentors, networks, or institutional support.

ContribGuide directly addresses economic opportunity and accessibility in tech:

  • Lowers the barrier for first-time contributors regardless of background
  • Reduces maintainer burden by answering onboarding questions automatically
  • Makes OSS contribution accessible to intermediate developers who aren't sure they're ready yet
  • Works for any public GitHub repo — not locked to a specific project or ecosystem

The first contribution is the hardest one. ContribGuide makes it happen.

Built With

Share this project:

Updates