A local-first, single-user study app for building job-ready AI/ML engineering skills. It combines a prerequisite graph, generated crash-course articles, short-answer grading with confidence calibration, spaced repetition, coding exercises, a learning journal, a study-buddy chat that reads your measured progress, and interview prep against a real job description.
- Node.js 24+
- Python 3.12 (the setup script creates a local virtual environment)
- Claude Code CLI for the default subscription-backed LLM provider
npm install
npm run db:migrate
npm run db:seed
npm run setup:python
npm run devThen open http://localhost:3000.
The defaults require no .env.local. To use the ChatGPT-subscription-backed
Codex CLI instead of Claude, create .env.local containing:
LLM_PROVIDER=codex-cliRun codex login status first and confirm it says Logged in using ChatGPT.
This path does not need an API key. Optional CODEX_MODEL_FAST,
CODEX_MODEL_BALANCED, and CODEX_MODEL_DEEP overrides are documented in
.env.local.example.
LLM_PROVIDER sets the default for everything. The study-buddy chat can
override it per conversation from the picker in its header — provider and model
tier, with the concrete model each tier resolves to shown inline. The choice is
stored on the thread, so the in-page panel and the popped-out window always
agree on which model is answering, and a thread left untouched keeps following
LLM_PROVIDER rather than pinning today's default. /dashboard reports which
backends are actually runnable.
npm test
npm run build
npm run smoke:python
npm run smoke:llmsmoke:llm uses the active provider. With the default claude-cli provider it
uses the logged-in Claude subscription and does not require
ANTHROPIC_API_KEY. If the subscription is temporarily rate-limited, the
smoke test reports the provider's reset message and can be rerun afterward.
- SQLite state lives in
data/app.db. - Generated articles and exercises are cached in SQLite.
- Exercise submissions run through the local Python interpreter with a 10-second timeout. This is intended for code you wrote yourself; it is not a security sandbox.
- Generated exercise files live under
runtime/exercises/. data/,runtime/,.llm-workdir/,.env.local, and build output are gitignored.
/interview takes a job description — pasted, or a link to one — and returns
the lessons that close the distance between it and what you have actually
demonstrated:
- One structured call extracts the posting's requirements and maps them onto curriculum slugs. Slugs are validated against the real catalogue, and anything the model failed to map falls back to a lexical matcher; a requirement that matches nothing is reported as uncovered rather than attached to the nearest node.
- The gap report is measured, not guessed. It leads with answers you gave at confidence 4-5 that scored badly, because those are the ones you would walk into an interview believing you knew.
- The study plan pulls in the unmet prerequisites of everything it recommends, ordered so the top item is always something you can start now. When the path is longer than the plan can show, the remainder is counted rather than dropped.
- The screen is a short-answer question set written for the posting and graded through the same endpoint as every other quiz, so it moves mastery and the review schedule.
Links are fetched server-side with loopback and private addresses blocked, a size cap and a timeout. Postings that render client-side (most large job boards) return an empty shell; the page says so and pasting the text always works.
Gap reports are saved to local interview history automatically. A saved entry can reopen its report, resume its generated question set, or show the original graded result after completion.
Turn it off with interviewPrep in lib/features/flags.ts.
/career adds a local career profile, an application pipeline whose historical
rows are never deleted, live job scans, and a bounded weekly study plan. The
planner defaults to Monday-Friday at 7:00 AM for one hour, with one focus topic
per morning. You can choose five to seven days, 60/90/120-minute blocks, and a
one- or two-topic daily limit. The full remaining curriculum stays visible in a
separate queue; it is not packed into each day.
Live job scans use the same subscription-backed provider boundary as Study
Buddy and deliberately require claude-cli or codex-cli. The CLI gets public
web search for that one operation only. Search results must have a visible date
within 21 days and a fit score of at least 5; nothing enters the pipeline until
you select it. Profile background is also supplied to generated lessons and
Study Buddy as a calibration hint, but it never overrides measured mastery.
The same progress service used by the UI and chat is available over MCP:
npm run mcpThe repository includes .mcp.json, which registers this server as
ml-study-guide.