Skip to content

islamumarov/QCard

Repository files navigation

🎴 QCard β€” Behavioral Interview Card Game

A Next.js app that turns behavioral-interview prep into a card game. Draw a random question card, answer out loud, and Claude plays the interviewer: it analyzes each answer and asks realistic follow-ups. After 5 main questions it delivers a scored feedback report β€” what was strong, what to improve, and what the interviewer expected. Everything (sessions, questions, full conversation logs, feedback) is persisted to SQLite.

Screenshots

Landing Interview
Landing page Interview page
  • 🎀 Speech-to-text β€” answer by talking (Web Speech API)
  • πŸ”Š Text-to-speech β€” the interviewer reads questions aloud
  • 🧠 Adaptive follow-ups β€” generated from your actual answer (claude-opus-4-8)
  • 🧩 Pick your answer framework β€” STAR, PAR, or CARL; the interviewer and feedback adapt to it
  • 🎯 Pick your target SWE level β€” Google L3–L7; questions and the grading bar calibrate to it
  • πŸ“‹ Persistent record β€” every turn + feedback saved to SQLite

Answer frameworks

At the start of a session you choose how you want to structure your answers. The choice is stored on the session and rewrites the interviewer's system prompt and the final feedback rubric (src/lib/methodologies.ts).

Framework Components Best for
STAR Situation Β· Task Β· Action Β· Result The default β€” most behavioral questions.
PAR Problem Β· Action Β· Result Concise, punchy answers.
CARL Context Β· Action Β· Result Β· Learning Growth / failure / learning questions (probes reflection).

The interviewer probes for whatever component is missing or vague β€” e.g. in CARL it will specifically ask what you learned if you describe an outcome but never reflect on it. The feedback report then scores your answers against that same framework.


Target SWE level

You also choose a target Google SWE level at the start. Where the framework sets the structure of an answer, the level sets the bar it's judged against (src/lib/levels.ts). The level affects two things:

  1. Question selection β€” cards are tagged with a level band; the deck is drawn to suit your target (junior cards for L3, scope/strategy cards for L6–L7), widening gracefully if a band is sparse.
  2. Grading bar β€” the interviewer's probing and the final feedback are calibrated to that level's behavioral expectations (scope, ambiguity, ownership, influence). The 1–10 rating is anchored to the chosen level, so a 7/10 at L6 β‰  a 7/10 at L3.
Level Title Bar (behavioral)
L3 SWE II Reliable execution & ownership of well-scoped tasks.
L4 (default) SWE III Independent feature/component ownership.
L5 Senior Team-level ownership; influence without authority.
L6 Staff Sustained multi-team / org-radius direction-setting.
L7 Senior Staff Multi-org technical direction, as a sustained pattern.

The level bars are grounded in researched, cross-checked Google leveling rubrics. Example: answer an L6 session with a single-team story and the interviewer will push you on the cross-team / org-level radius β€” the L6 distinctive signal.


Requirements

  • Node.js β‰₯ 18.18 (Next.js 15 requirement). .nvmrc pins 22.21.1 β€” run nvm use. better-sqlite3 is a native addon compiled for the Node it was installed under, so use one consistent Node version (see Troubleshooting).
  • An Anthropic API key (console.anthropic.com). Without a key the app still runs end-to-end using built-in fallback follow-ups/feedback.
  • Voice features work best in Chrome / Edge (Web Speech API). You can always type instead.

Setup

nvm use                             # Node 22.21.1 (from .nvmrc) β€” important
npm install
cp .env.local.example .env.local    # then paste your ANTHROPIC_API_KEY
npm run db:seed                     # optional β€” DB also auto-seeds on first run
npm run dev                         # http://localhost:3000

dev / start / db:seed run a Node-version preflight (scripts/check-node.mjs) that stops with a clear message if Node is too old or the native binary doesn't match.

How it works

Landing  ──POST /api/session──▢  pick 5 random cards, create session, log intro + first card
   β”‚
Interview page (client)
   β”‚  speak/type answer
   β”œβ”€ POST /api/answer  { sessionId, content }
   β”‚     └─ log answer ─▢ Claude analyzes ─▢ { followup | next }
   β”‚           followup β†’ log follow-up, stay on card (max 2)
   β”‚           next     β†’ advance; log next card, or finish
   β”‚
   └─ when 5 cards done β†’ POST /api/feedback
         └─ Claude reads whole transcript ─▢ scored feedback ─▢ saved + shown

The interviewer prompt, follow-up cap, and feedback prompt live in src/lib/anthropic.ts. The model returns structured JSON (output_config.format) with adaptive thinking, so decisions are reliable to parse.

Database (SQLite)

File: data/qcard.db (auto-created; WAL mode). Schema in src/lib/db.ts:

Table Purpose
questions The card deck (category, text, difficulty, level_min/level_max band). Seeded from questions.ts.
sessions One interview: status, target main-question count, current index, chosen framework (methodology), target level (level), timestamps.
session_questions The 5 cards chosen for a session, their order, status, and follow-ups asked.
messages Full conversation log β€” every interviewer line, candidate answer, and follow-up, tagged by role + kind and linked to its card.
feedbacks Final report per session: strengths, improvements, expectations, overall, rating.
sessions 1β”€β”€β”€βˆž session_questions 1β”€β”€β”€βˆž messages
sessions 1β”€β”€β”€βˆž messages            (intro / feedback rows have null question)
sessions 1β”€β”€β”€βˆž feedbacks
questions 1β”€β”€β”€βˆž session_questions

LLM providers (switchable)

The interviewer is backed by a pluggable provider layer (src/lib/llm/). Pick one with QCARD_PROVIDER:

Provider QCARD_PROVIDER Key Default model Key var
Anthropic Claude anthropic (default) console.anthropic.com claude-opus-4-8 QCARD_ANTHROPIC_MODEL
Google Gemini gemini aistudio.google.com/apikey gemini-2.5-flash QCARD_GEMINI_MODEL

Both use schema-constrained JSON output so decisions/feedback parse reliably (Anthropic output_config.format + adaptive thinking; Gemini responseSchema, thinking off on flash tiers). The active provider is shown as a chip in the UI. If the selected provider has no key, the app falls back to built-in responses.

Switch example β€” in .env.local:

QCARD_PROVIDER=gemini
GEMINI_API_KEY=...
# QCARD_GEMINI_MODEL=gemini-2.5-pro

Add another provider by implementing the JsonLLM interface in src/lib/llm/types.ts and registering it in src/lib/llm/index.ts.

Configuration (env)

Variable Default Meaning
QCARD_PROVIDER anthropic LLM backend: anthropic or gemini.
ANTHROPIC_API_KEY β€” Key for the Anthropic provider.
QCARD_ANTHROPIC_MODEL claude-opus-4-8 Claude model (also accepts legacy QCARD_MODEL).
GEMINI_API_KEY β€” Key for the Gemini provider (GOOGLE_API_KEY also read).
QCARD_GEMINI_MODEL gemini-2.5-flash Gemini model.
QCARD_MAIN_QUESTIONS 5 Main questions per interview.
QCARD_MAX_FOLLOWUPS 2 Max follow-ups the interviewer may ask per card.
QCARD_DB_PATH <projectRoot>/data/qcard.db SQLite file location. If the configured path doesn't exist, the app searches the project structure (anchored on package.json, from both cwd and the module dir) for an existing qcard.db before creating a new one β€” so it resolves correctly even when launched from another directory.

Project layout

src/
  app/
    page.tsx                  landing / start
    interview/[id]/page.tsx   interview screen
    api/
      session/route.ts        POST  create session
      session/[id]/route.ts   GET   load state
      answer/route.ts         POST  answer β†’ analyze β†’ follow-up / next
      feedback/route.ts       POST  generate + save feedback
  components/
    InterviewClient.tsx       chat UI, mic, TTS, progress
    FeedbackReport.tsx        final report
  hooks/useSpeech.ts          Web Speech API (STT + TTS)
  lib/
    db.ts                     SQLite schema + queries
    llm/
      index.ts                provider selection + analyze/feedback ops
      prompts.ts              shared prompts, schemas, fallbacks
      anthropic.ts            Anthropic (Claude) provider
      gemini.ts               Google Gemini provider
      types.ts                JsonLLM provider interface
    methodologies.ts          STAR / PAR / CARL frameworks + prompt guidance
    levels.ts                 Google L3–L7 bars + interviewer/feedback calibration
    questions.ts              card bank (level-banded)
    state.ts                  build client state from DB
    types.ts                  shared types

Voice quality (TTS)

The interviewer voice uses the browser's built-in voices, which vary a lot by browser/OS. QCard auto-selects the most natural English voice available and offers a picker in the UI.

  • Best, no setup: use Chrome or Edge β€” they ship neural "Google US English" / "Natural" voices.
  • Safari / macOS: defaults to Samantha. For human-quality voices, download them once: System Settings β†’ Accessibility β†’ Spoken Content β†’ System Voice β†’ Manage Voices β†’ English β†’ pick a Siri, (Enhanced), or (Premium) voice. They then appear in the dropdown and are auto-preferred. (macOS also ships novelty voices like Zarvox / Bad News β€” QCard filters those out so they're never auto-selected.)

Troubleshooting

ERR_DLOPEN_FAILED / "compiled against a different Node.js version" on Start Interview β€” better-sqlite3's native binary is ABI-locked to the Node it was built under. You installed under one Node and are running under another (common: nvm default is older than the project Node). Fix:

nvm use                      # switch to .nvmrc (22.21.1), then re-run
# or, to use a different Node, rebuild the addon for it:
npm rebuild better-sqlite3

The predev/prestart guard now catches this before the server starts.

Notes

  • API routes run on the Node.js runtime (better-sqlite3 is native); next.config.mjs marks it as a server-external package.
  • The DB is created on first request β€” no manual migration step needed.

License

MIT β€” see LICENSE.

About

Behavioral Interview Card Game

Topics

Resources

License

Stars

1 star

Watchers

0 watching

Forks

Releases

No releases published

Packages

 
 
 

Contributors