This is a submission for the Hacktoberfest Weekend Challenge: Build for a Friend
๐ชง The Line Nobody Warns You About
You know the one. It shows up on basically every job post:
"Experience with real-world projects required."
I'm a student too, so I've seen it from the inside. You can ace every exam and still have nothing to point at. Tutorials end the second the video does. Real work is messier: there are tasks, blockers, deadlines, and (if you're lucky) someone you can ask when you're stuck.
That gap is what SkillPilot is about. And I built it for my friend Kritika.
The Short Version
| Built for | My friend Kritika |
| The problem | Students have grades but nothing that looks like real project experience |
| What it is | An AI-powered learning platform that simulates real industry workflows |
| The open-source bit | Gemma, an open-weight model, running locally in my backend |
| Built with | Next.js 14 ยท Express + TypeScript ยท Flutter ยท MongoDB Atlas |
So What Does It Actually Do?
If you're a student, it works like a tiny, friendly workplace:
- Tell it what you're into and how comfortable you are. It hands you a real-feeling project: a title, a description, a difficulty level, a suggested tech stack, 5โ8 concrete tasks and a deadline.
- Work the board. Tasks live on a Kanban board (To Do, In Progress, Done), and your progress updates automatically as you move cards across.
- Ask the mentor when you're stuck. It's an AI chat that knows which project you're in, so you get help with your thing, not a generic answer.
-
Finish and show it off. When the project's done, SkillPilot writes a portfolio entry, a skills summary and resume-ready bullet points. You get a public page at
/portfolio/[username].
If you're a recruiter, you can browse and filter student profiles, open the projects and technologies behind them, and run an AI skill analysis that breaks a student's abilities down by category.
And every bit of AI in there runs on Gemma, locally in my backend. No closed API anywhere.
Poke Around the Repo
It's a monorepo. This is what the web/ side looks like when I ls around:
SkillPilot/web$ ls
backend frontend
SkillPilot/web/frontend$ ls
next.config.js next-env.d.ts package.json package-lock.json postcss.config.js src tailwind.config.ts tsconfig.json
SkillPilot/web/backend$ ls
node_modules package.json package-lock.json src tsconfig.json
There's also a mobile/ Flutter app and a docs/ folder (architecture, setup, API notes) at the repo root.
A quick honesty note: SkillPilot has a bit of a backstory. It began as a prototype for the TechmentorX Hackathon. What I'm writing about here is the current version, where the AI layer runs on open-weight Gemma locally in the backend.
How It's Put Together
Next.js 16 (web) โโโโ
โโโโบ Express + TypeScript API โโโบ AI service layer โโโบ Local Gemma
Flutter (mobile) โโโโ โ
โโโโบ MongoDB Atlas
The AI calls live in their own service layer in the backend, separate from the route controllers. Controllers just ask it for a project, a mentor reply, a portfolio or a skill score.
The stack
| Layer | What I used | Why |
|---|---|---|
| Web | Next.js 14, TypeScript, Tailwind, Zustand, Framer Motion | Quick dashboards, a public portfolio route, simple state |
| API | Node.js, Express, TypeScript, Mongoose | Typed all the way through, fast to iterate on |
| Mobile | Flutter, Provider, Material 3 | One codebase, and students live on their phones |
| Database | MongoDB Atlas | Projects, tasks, chats and portfolios are all document-shaped |
| Auth | JWT access + refresh tokens, bcrypt, httpOnly cookies | Boring, safe defaults |
| AI | Gemma (open-weight), run locally | Open weights I control, and no per-request cost |
The API
| Method | Endpoint | What it does |
|---|---|---|
| POST |
/api/auth/signup ยท /api/auth/login
|
Accounts |
| POST | /api/projects/generate |
AI-generated project |
| PATCH | /api/tasks/:projectId/:taskId/status |
Move a Kanban card |
| POST | /api/chat/:projectId |
Project-scoped mentor chat |
| POST | /api/projects/:id/portfolio |
Portfolio and resume bullets |
| GET | /api/recruiter/students |
Browse talent |
| POST | /api/recruiter/students/:id/skill-score |
AI skill analysis |
How Gemma pulls its weight
Project generation is a contract, not a chat. The Kanban board has to render whatever the AI sends back, so the output has to follow a shape (simplified):
{
"title": "string",
"description": "string",
"difficulty": "beginner | intermediate | advanced",
"techStack": ["string"],
"tasks": [{ "title": "string", "description": "string" }],
"deadline": "ISO date"
}
The mentor knows where you are. POST /api/chat/:projectId ties every conversation to one project, so answers are about what you're actually building.
Portfolios write themselves. When a project is finished, the AI looks at the project and its tasks and turns them into a summary, the skills you picked up, and resume bullets.
Skill scores are a starting point, not a verdict. Scoring a person is the most sensitive thing SkillPilot does. The score is there to help a recruiter dig into a student's real projects, not to replace looking at them.
Want to Run It?
# Backend
cd web/backend
cp .env.example .env # add your credentials
npm install
npm run dev # http://localhost:5000
# Frontend
cd web/frontend
cp .env.example .env.local
npm install
npm run dev # http://localhost:3000
# Mobile
cd mobile
flutter pub get
flutter run
More detailed setup lives in docs/setup.md.
Why Open (and Local) Matters Here
The challenge asks where going open actually beats going closed. For SkillPilot, it's these five things.
1. The AI lives on my backend, not someone else's.
People think twice about sharing unfinished project ideas, "why doesn't my code work?" questions, and skill scores with their name on them. With Gemma, inference happens on hardware I control, so those prompts and answers don't get sent to a third-party model API. To be precise, the app's data is stored in MongoDB Atlas. Local Gemma keeps the model side in my hands, and I'd rather claim exactly that.
2. Students can't pay per token.
A mentor is only useful if you can ask the hundredth question without watching a meter. Local inference means no per-request cost and no model API key to rotate or leak. It doesn't get pricier when more students show up.
3. The model isn't wired into everything.
Since the AI calls sit in one service layer, upgrading or swapping the model stays contained to one place. I'm not at the mercy of one vendor's pricing, deprecations or surprise model updates.
4. Scoring people deserves something you can inspect.
A system that evaluates students for recruiters should sit on something you can examine and test. Open weights let me poke at how the scorer behaves instead of treating it as a black box.
5. The honest trade-off.
A local open model is smaller than the biggest hosted ones. I'm fine with that for a product aimed at students: cheaper, more private on the model side, and fully in my control.
| Closed API | Local Gemma (SkillPilot) | |
|---|---|---|
| Prompts leave my backend | Yes | No |
| Cost per request | Per token | None |
| Swap or upgrade the model | Vendor decides | I decide |
| Needs a model API key | Yes | No |
| Raw capability ceiling | Higher | Lower |
Where I'd Take It Next
- Get SkillPilot in front of more students and fix whatever confuses them.
- Show recruiters why a skill score came out the way it did.
- Try other open models behind the same AI service layer.
Prize Categories
- Best Use of Gemma: Gemma, an open-weight model run locally in my backend, powers every AI feature: project generation, the project-scoped mentor, portfolio writing and skill scoring.
- Best Use of MongoDB Atlas: Atlas is the data layer behind an app built on an open-weight model. It stores users, projects, task state, chat history and portfolios.
Before You Go
Building for one person is a good constraint. Every feature has to earn its place by being useful to someone real, and for SkillPilot that someone is Kritika.
If you've mentored a junior, or you were the student with the empty portfolio, I'd love to hear what finally got you from "I've learned things" to "I've built something." Tell me in the comments. ๐
Did this project with my friend as a teammate - @aniket937


Top comments (0)