A React + TypeScript terminal dungeon RPG for learning beginner Linux commands. Rooms behave like directories, files behave like items, and doors behave like folders. The player types commands in the left terminal and sees the current dungeon room on the right.
AI features use Gemini through Supabase Edge Functions when a Gemini key is available. Gemini never runs directly in the browser. If the key is missing, expired, quota-limited, or the Edge Function is slow/unavailable, the app automatically falls back to deterministic local content so the game remains playable.
Terminal Quest opens with a cinematic dungeon landing page that frames the app as a playable Linux RPG, not a plain command tutorial.
The landing page teaches the premise before the player ever signs in: Linux commands become spells, rooms become directories, and the keyboard becomes the controller.
The onboarding flow demonstrates the core interaction with a simple example: type a command, submit it, and watch the dungeon react.
The first screen is built for beginners too, with a clear start action and no Linux experience required.
Players can create an account or continue with Google through Supabase authentication. The screen keeps the terminal fantasy alive while still behaving like a real login flow.
If a previous quest exists, the app offers to resume it and shows the saved room, difficulty, explored rooms, and commands used.
The main game is a split-screen terminal and tile-based dungeon. The player types real Linux-style commands on the left and sees the current dungeon room on the right.
Commands such as ls, cd, pwd, cat, file, find, mkdir, rm, and mv drive real movement, discovery, items, locked doors, and room transitions.
Rooms are generated with connected wall structures, lighting, banners, skulls, vents, doors, archways, keys, scrolls, and decorative dungeon props while preserving playable paths.
The map uses room-by-room visibility instead of a full-map reveal, so each chamber feels like a new directory to inspect.
Moving through a door updates the shell path and the dungeon view together. Rooms behave like directories, doors behave like folders, and the player physically walks to exits before changing rooms.
The Dungeon Master reacts through the wizard popup instead of cluttering the terminal. It can explain mistakes, nudge stuck players, praise useful commands, and guide beginners toward the next action.
The hint book and inventory sit under the map, keeping guidance, collected items, and the current room visible without hiding the game board.
The profile page turns each run into a learning record. The account tab shows login identity and the current shell archetype generated from play history.
The stats tab tracks progression such as completed levels, favorite command, keys found, streaks, and total commands.
The mastery tab explains each command as a spell, showing what the command teaches and what the player should practice next.
Achievements reward milestones such as finishing levels, practicing find, and opening locked doors.
The progress tab visualizes commands used, time per run, efficiency, and command mastery shape.
The difficulty tab lets the player calibrate Linux familiarity, which feeds the adaptive level generation system.
Gemini can power Dungeon Master tutoring, command flavor text, adaptive level generation, generated mkdir rooms, Mau quizzes, profile summaries, and run reports. If the API key is missing, expired, quota-limited, or slow, deterministic local fallbacks keep the whole game playable.
Core gameplay also includes:
- Dungeon-style inventory at
~/inventory - Win condition by moving the target file into inventory
- Door placement on wall boundaries only
- Door labels above exits
- Player walking to doors/files before changing rooms or picking items up
- Sound effects and dungeon ambience for movement, doors, rooms, and actions
- Node.js 18 or newer
- npm
- Supabase project
- Gemini API key, optional
Install dependencies:
npm installCreate a local frontend env file:
cp .env.example .envFill in .env:
VITE_SUPABASE_PROJECT_ID="vtizdyjqkwcrygqblpcm"
VITE_SUPABASE_URL="https://vtizdyjqkwcrygqblpcm.supabase.co"
VITE_SUPABASE_PUBLISHABLE_KEY="your-supabase-publishable-key"This is a Vite app, so use VITE_* variables. Do not use NEXT_PUBLIC_*.
Start Vite:
npm run devOpen the URL Vite prints, usually:
http://localhost:8080
If you edit .env, restart Vite with Ctrl + C, then:
npm run devGemini keys belong in Supabase secrets, not in .env.
The app works without a Gemini key. Without GEMINI_API_KEY, these systems fall back automatically:
Dungeon Master chat -> local tutor replies
Command flavor text -> command-specific fallback lines
Run report feedback -> locally generated coaching note
Generated levels -> deterministic adaptive dungeon generator
Generated mkdir rooms -> deterministic room blueprint
Mau quizzes -> deterministic quiz pools
Profile summary -> local profile summary
If you want AI-enhanced text/generation, set hosted secrets:
npx supabase secrets set GEMINI_API_KEY=your-gemini-api-key --project-ref vtizdyjqkwcrygqblpcm
npx supabase secrets set GEMINI_MODEL=gemini-2.5-flash --project-ref vtizdyjqkwcrygqblpcmIf you stop paying for the key or remove it later, no code change is required. The Supabase functions and browser services both return fallback content.
Deploy Edge Functions:
npx supabase functions deploy dungeon-master --project-ref vtizdyjqkwcrygqblpcm --no-verify-jwt --use-api
npx supabase functions deploy generate-level --project-ref vtizdyjqkwcrygqblpcm --use-api
npx supabase functions deploy generate-room --project-ref vtizdyjqkwcrygqblpcm --use-api
npx supabase functions deploy generate-quiz --project-ref vtizdyjqkwcrygqblpcm --use-apidungeon-master uses --no-verify-jwt so public browser calls using Supabase publishable keys work cleanly.
For local Supabase function testing, copy:
cp supabase/.env.example supabase/.env.localThen fill in only if testing AI locally:
GEMINI_API_KEY="your-gemini-api-key"
GEMINI_MODEL="gemini-2.5-flash"AI fallback is implemented in both places:
Browser services: src/game/aiFallback.ts
Supabase functions: supabase/functions/*
Browser calls time out after a short wait and continue with local fallback content. This protects the player experience if:
GEMINI_API_KEY is missing
GEMINI_API_KEY expires
Gemini quota is exceeded
Supabase Edge Functions return an error
Network calls hang or fail
To intentionally test no-AI mode, remove or rename the GEMINI_API_KEY Supabase secret, redeploy the functions, and play normally. The app should still generate levels, rooms, quizzes, reports, and guidance.
If you want to remove the hosted secret:
npx supabase secrets unset GEMINI_API_KEY --project-ref vtizdyjqkwcrygqblpcmThese command-like inputs intentionally use local hardcoded replies to save Gemini credits:
sudo
grep file
find
Use natural-language prompts to test Gemini tutor mode:
i am new to linux how does this work
what does mv do
how do i win
If every answer looks identical or generic, that usually means fallback mode is active. That is safe. For AI-enhanced mode, check:
.envpoints to the same Supabase project that has the functionsVITE_SUPABASE_PUBLISHABLE_KEYbelongs to that projectGEMINI_API_KEYis set as a Supabase secret and still has credits/quota- Edge Functions were redeployed after code changes
- Vite was restarted after
.envchanges
ls list files and doors in the current room
cd <door> enter a directory/door
pwd print current room path
cat <file> read a file
file <name> inspect a file
find <name> search the dungeon
mkdir <name> create a generated directory/room
rm <name> remove a file
mv <file> ~/inventory move a file into inventory
help show command help
clear clear terminal history
Default win command:
mv victory.jpg ~/inventoryFor Gemini-generated levels, the terminal prints the exact Win: command.
npm run dev
npm test
npm run build
npm run previewFor Vercel/Netlify/Cloudflare Pages, use:
Build command: npm run build
Output directory: dist
Keep GEMINI_API_KEY in Supabase secrets only if AI-enhanced mode is desired. Do not put Gemini keys in frontend hosting environment variables.
Commit examples, not secrets:
.env.example
supabase/.env.example
Do not commit:
.env
.env.local
supabase/.env.local
supabase/.temp
node_modules
dist














