Project Info
Inspiration
My biggest inspiration are the games that have unique gameplay elements and lots of variety. I'm especially inspired by solo indie developers. I love making games with innovating elements and that's why I decided to make a game for this hackathon.
What it does
This is a dungeon crawler game featuring procedurally generated rooms, puzzles, and boss fights. However, the main feature of this game is that you're not playing alone - an AI listens to everything what you say and actively controls the environment of the world. The AI considers itself the god of this world and it can generate various events such as lightning strikes, enemy spawns, traps, and many others. It can also create positive events by granting blessings to players it is pleased with. The AI is designed to generate funny and unexpected events. This game's full potential will be realized in multiplayer mode, but due to time constraints on the hackathon and the overall complexity it was not possible to implement it yet.
How we built it
This is a web based application built in TypeScript. I created a basic 2d game engine by separating responsibilities into systems, such as render, world, level, game components and etc. I use Deepgram AI as a voice model. It transcribes player's speech into text which is sent to a text-based LLM that generates events. That LLM then sends a prompt to another LLM which translates the text into specific tool calls. Deepgram is also used to voice god's phrases. During the development I relied on my personal skills and on AI agents. Devin AI in particular proved to be very useful in my workflow.
Challenges we ran into
Creating the map generation and tiling system proved to be very difficult for me. Because the walls are isometric, they can connect in many different ways. For example, it was not very trivial to write an algorithm for generating two rooms connected by a corridor.
Accomplishments we're proud of
I'm particularly proud of how well the interaction between the AI "god" and the player turned out to be. The AI is constantly trying to be very mischievous and sabotage player unexpectedly. Watching people play my game is really fun! I will definitely implement multiplayer in the future.
What we learned
First of all, I learned how to write a game in very limited time constraints. That's definitely was a big challenge for me. Additionally, I learned how to orchestrate different AIs simultaneously in a projects and that turned out to be quite a nice experience.
What's next
I'm planning on adding multiplayer, proximity voice chat, new level, new monsters, more gameplay mechanics, puzzles, and definitely more events for the AI to use.
Dungeon Crawler
A top-down, pixel-art dungeon crawler built with PixiJS
and TypeScript. Procedurally generated rooms, real-time melee/ranged combat,
fog of war, a locked boss room, a hidden button-puzzle treasure room — and a
GameMaster: a mischievous, omnipotent god (random today, AI-driven
tomorrow) that demands worship, blesses the faithful, and smites the defiant
with live events. Pray to it by holding E.
Art: the 0x72 DungeonTileset II.
Running it
npm install
npm run dev # start the Vite dev server (hot reload)
npm run build # type-check (tsc -b) + production build to dist/
npm run lint # eslint
npm run preview # serve the production build
npm run test:deepgram # verify the Deepgram (voice) token works
npm run test:ai # run the Watcher→Hand LLM pipeline on sample lines
Open the printed localhost URL.
Voice + AI god (optional). To let the god hear and react to you, add keys to a
git-ignored .env at the repo root:
DEEPGRAM_TOKEN=your_deepgram_key # speech-to-text
OPENAI_TOKEN=sk-... # the god's brain
OPENAI_MODEL=gpt-4o-mini # optional, this is the default (cheap)
Verify them with npm run test:deepgram and npm run test:ai, restart the dev
server, then in the browser console: gm.awaken() and press V to talk.
(Tokens are inlined into the client build — fine for a local sandbox; use
short-lived keys / a server for production. See ai/.)
To skip the console each run, flip the startup toggles in
src/game/config.ts → GM:
export const GM = {
autoAwaken: false, // wake the OpenAI brain on boot (reacts to speech)
autoListen: false, // arm the mic — starts on your first click/keypress
debugAI: false, // log every Watcher/Hand decision to the console
};
Controls
| Input | Action |
|---|---|
WASD / Arrow keys | Move (also sets aim/facing) |
Space | Attack (hold to charge a bow) |
E (hold) | Pray to the god — raises your favor |
V | Toggle voice transcription (Deepgram mic; records the session) |
1–9, 0 | Select weapon slot |
+ / - | Cycle to next / previous weapon |
M | Reveal all fog of war |
P | Toggle passive mode (enemies ignore you) |
L | Toggle invincibility |
Space (on death) | Restart |
Clear every normal enemy to seal and reveal the boss corridor; find and press the red and blue floor buttons to open the secret treasure room.
Architecture
The codebase is a small, decoupled engine. Game rules live in systems, not in the loop, so new behaviour is added by writing a new system rather than editing the core.
src/
core/ Engine, GameLoop, shared types (System, UpdateContext)
events/ Type-safe EventBus (pub/sub)
world/ World (entities + collision), Entity, Collision, WorldMap
level/ Procedural LevelGenerator, room templates, SeededRandom
render/ Renderer (Pixi app), Camera, SpriteAnimator
input/ Keyboard + InputManager (abstract InputState)
assets/ Tileset loader, grouping, animation slicing
game/
Game.ts Composition root — wires everything together
config.ts Tuning constants (player, enemies, weapons, world)
Player.ts The player entity
enemies/ Enemy base class + one class per enemy type
weapons/ Weapon definitions, projectiles, weapon view
items/ Coins, hearts, chest, buttons, doors, secret wall
systems/ Movement, combat, enemy AI, fog, doors, drops, death…
gm/ GameMaster — the "god" of live random / AI-driven events
GameMaster.ts System orchestrator: favor, prayer, commands, hazards, API
actions.ts Action vocabulary (ACTIONS) + self-describing schema
effects.ts Ticking hazards (bullets, bombs, lightning, spikes, auras…)
library.ts The built-in event deck (LIBRARY)
command.ts Command-string shorthand parser
types.ts Event / action / tone types
index.ts window.gm API + exposeGameMaster()
Core ideas:
- Engine loop — each frame the
Enginebuilds anUpdateContext(dt,input,world,events) and runs every registeredSystem, then lets each entity update + sync its view. - EventBus — systems communicate through typed events
(
enemy:killed,boss:defeated,coin:collected,gm:cast, …) instead of referencing each other directly. - Entities vs views — an entity owns its logical
position; the engine syncs that onto its Pixiviewevery frame.
GameMaster (the god of live events)
The GameMaster ("GM") is a mischievous, omnipotent god that injects events into the running game — blessings, punishments, and chaos. It's designed so an external director (a developer at the console today, an LLM "god" tomorrow) can shape the experience at runtime, making each run unpredictable.
The god wants to be worshipped. The player prays by holding E, which
raises their favor. The god issues commands ("slay three", "be still",
"come to my altar"); obey before the deadline to be rewarded, defy it to be
smitten by lightning. Its judge action rewards the faithful and punishes
the defiant based on current favor.
It is just another System (so it ticks with everything else) and owns all
transient hazards: enemy bullets, bombs, lightning, spikes, timed buffs,
companions, divine commands, screen shake and the announcement banner.
Favor & prayer
- Favor ranges
-100..100(starts at 0). HoldingEraises it; failing a command lowers it (and calls down a bolt). gm.favor()reads it;gm.onPray(cb)is the hook a future AI god reacts to — it fires repeatedly while the player prays and once when they release.- Typed events are also emitted on the bus:
gm:pray,gm:favor,gm:command,gm:betray,gm:cast.
Divine commands
{ type: 'command', kind, need, time } makes the god demand something with an
on-screen prompt + countdown. Kinds:
| kind | the player must… | need |
|---|---|---|
pray | hold E for N seconds | seconds (default 3) |
kill | slay N enemies | kills (default 3) |
still | not move for N seconds | seconds (default 3) |
reach | walk to the glowing altar marker | (n/a) |
Success → favor up + heal + coins. Failure (deadline) → favor down + a tracked lightning bolt.
Betrayal
Companions are hittable by the player. Strike your own ally and it counts as
betrayal: gm:betray fires, the wounded ally abandons you, and your favor
drops. Striking down a guardian Angel is the gravest sin — favor crashes to
the floor (-100) and the god answers with a tracked bolt. Subscribe with
gm.onBetray(cb).
Event format
An event is plain, serialisable data: an ordered list of actions. Each action is a small, parameterised command. This declarative shape is easy to validate, log, compose and — crucially — generate with an AI.
{
"id": "demonic_onslaught",
"name": "Demonic Onslaught", // shown on the banner
"tone": "bad", // good | bad | neutral | chaos (banner colour)
"actions": [
{ "type": "shake", "intensity": 8, "duration": 0.6 },
{ "type": "bullet_hell", "delay": 0.4, "at": "far_player",
"count": 12, "pattern": "spiral", "damage": 1 }
]
}
delay(seconds) staggers an action after the event is cast.atis the common position param (see below).- All other fields are action-specific and read defensively with defaults, so partial/loose input never throws.
Positions (at): "player", "near_player", "far_player", "random",
or explicit { "x": 120, "y": 200 }.
Driving it: the window.gm API
Everything funnels through a single live instance exposed on window.gm. Open
the browser console while playing:
gm.help() // print a cheat-sheet
gm.random() // cast a weighted-random event from the deck
gm.list() // [{ id, name, tone }] of library events
gm.cast('bullet_hell') // cast a library event by id
gm.cast({ name: 'Doom', tone: 'bad', actions: [ // inline JSON event
{ type: 'bomb', at: 'player', fuse: 2 },
{ type: 'bullet_hell', delay: 0.5, count: 14, pattern: 'spiral' }
]})
gm.run('horde 12; shake 9 0.7') // command-string shorthand
gm.actions() // full action schema + params (for AIs)
gm.enemies() // spawnable enemy names
gm.favor() // devotion (-100..100)
gm.snapshot() // read game state (favor, mood, hp, transcript…)
gm.onPray(p => console.log(p)) // react to prayer (AI hook)
gm.onBetray(p => console.log(p)) // react to attacking an ally
gm.speak('Kneel.') // the god speaks (executor AI's voice)
gm.hear('praise be to you') // feed player speech in (Deepgram STT hook)
gm.mood(); gm.rollMood() // read / randomise temperament
gm.connectWebSocket('ws://…') // wire an external AI orchestrator
gm.cast(input) accepts any of: a library id, a full event object, a single
action object, an array of actions, a JSON string of any of those, or a
command-string. It returns false if the input can't be parsed.
Command-string shorthand
A terser format for manual use; it compiles to the same action directives.
Statements are separated by ; or newlines: verb [positional…] [key=value…].
bomb fuse=1.5 radius=48
horde 12; shake 9 0.7
spawn BigDemon 3 hp=4 at=far_player
elite Ogre hp=10
spawn Imp x=120 y=200
companion Skelet duration=0 # a permanent pet
meteors count=8 spread=90
say "A giant blocks your path" tone=bad
x= / y= fold into an at: {x, y} position. Aliases exist:
bullethell→bullet_hell, coins→loot, swarm→horde,
champion/boss→elite, quake→shake, bolt/zap→lightning,
pet/ally→companion, blink/tp→teleport,
trial/demand/decree→command, meteor→meteors.
Action reference
Call gm.actions() for the authoritative, self-describing schema (types +
defaults). Summary:
| Action | Tone | What it does |
|---|---|---|
spawn | bad | Spawn N of an enemy (enemy, count, hp/dmg/speed multipliers, scale, weapon) |
horde | bad | Swarm of small fast enemies around you (count, speed) |
elite | bad | One buffed, oversized champion (enemy, hp, dmg, scale) |
bomb | bad | Fused bomb; AoE on detonation. Drops where it lands and stays (run!); pass stick: true for an unavoidable one (fuse, radius, damage, hurtsEnemies) |
bullet_hell | bad | A stationary caster sprays bullet waves — kill it to stop the storm (caster, duration, rate, count, speed, damage, pattern, spin) |
lightning | bad | Telegraphed divine bolt; track: true follows the player (at, damage, radius) |
command | chaos | The god demands something — obey or be punished (kind, need, time, prompt) |
judge | chaos | Reward the faithful / smite the defiant by favor (threshold, reward, punish) |
spikes | bad | A patch of damaging floor spikes — step off them (size, duration, damage) |
meteors | bad | Rain explosions across an area over time (count, spread, interval, radius, damage) |
enrage | bad | Permanently buff every living enemy (speed, dmg) |
slow | bad | Temporary movement-speed penalty (factor, duration) |
damage | bad | Directly hurt the player (amount) |
companion | good | Summon ally(ies) that follow you and fight hostiles (enemy, count, duration, dmg) |
heal | good | Restore health (amount, or full: true) |
shield | good | Grant temporary (yellow) shield hearts (amount) |
loot | good | Scatter collectable coins (count) |
hearts | good | Drop heart pickups (count) |
haste | good | Temporary speed boost (factor, duration) |
smite | good | Damage every enemy at once (damage) |
explode | neutral | Instant AoE blast + flash + shake (radius, damage, hurtsEnemies, hurtsPlayer) |
teleport | neutral | Blink the player to another location (at) |
shake | neutral | Camera shake (intensity, duration) |
say | neutral | Show a banner title-card (text, tone) |
speak | chaos | The god's voice — bottom-screen dialogue (text, hold) |
mood | neutral | Set/nudge/roll the god's temperament (kindness, chaos, sensitivity, nudge, roll) |
Bullet patterns: ring, spiral, aimed, random.
Companions are friendly summons: they are not added to the hostile list, so your
weapons and enemy AI ignore them; they chase and hit nearby enemies, otherwise
they heel to you. duration: 0 makes a pet permanent.
Built-in event deck
gm.random() draws (weighted) from gm/library.ts. Current events:
- Hostile:
bullet_hell,crossfire,bomb_drop,hot_potato,minefield,elite,horde,ambush,mimic,molasses,enrage,spike_trap - Helpful:
blessing,windfall,fairy_gift,haste,smite,companion,reinforcements - Wild:
earthquake,pandemonium,meteor_shower,blink,gauntlet - The god speaks:
commandment,trial_of_blood,be_still,pilgrimage(commands);judgement,wrath,judgement_day,the_faithless,divine_gift,guardian_angel
Letting an AI run it (the god): how & when to send updates
The whole integration is one function: window.gm.cast(json). The AI's job
is to decide what JSON to send and when; the game does the rest. There are
two halves — observe (read state) and act (cast events).
Observe — give the model context to decide:
gm.snapshot()— a one-shot read of{ favor, praying, player{hp,…}, enemies, companions, kills, command }.- Event subscriptions (push):
gm.onPray(cb),gm.onBetray(cb), and the bus eventsgm:favor,gm:command,gm:cast. You can also tap gameplay events (enemy:killed,player:died,boss:defeated,coin:collected).
Act — send an event whenever the god should intervene. Two common cadences:
-
Event-driven (reactive). Best fit for the god persona — react to what the player does:
gm.onPray(({ favor }) => { if (favor > 60) gm.cast('divine_gift'); }); gm.onBetray(() => gm.cast('wrath')); -
Timed (a "director" loop). Every N seconds, snapshot → ask the model → cast its reply:
setInterval(async () => { const reply = await askLLM(gm.snapshot(), gm.actions()); gm.cast(reply); // a library id, a JSON event, or a command-string }, 12_000);
Where the AI runs. gm lives on window, so:
- In-page model / script: call
gm.cast(...)directly. - Server-side model: stream its output to the browser (WebSocket/SSE/fetch)
and call
gm.cast(payload)on each message.castaccepts a JSON string, so you can forward the model's raw text verbatim.
To stay in character, ground the model once with gm.actions() (vocabulary +
params) and gm.enemies() (valid names), then let it compose primitives:
demand prayer with a command, reward the faithful with divine_gift, and bury
the defiant under a bullet-hell + tracked lightning — all without new code.
Voice → god: the multi-AI pipeline
The intended end state is a three-AI pipeline that lets the god react to what
players actually say. Full design, wire protocol, and system prompts live in
ai/; in brief:
player voice ─▶ (1) Deepgram STT ─▶ gm.hear(text)
│ emits gm:transcript (in snapshot too)
▼
(2) "The Watcher" — judge/filter/mood: react? speak? intent? (may ignore)
│ natural-language intent
▼
(3) "The Hand" — executor: intent → gm.cast(JSON) / gm.speak / gm.run
The whole pipeline is implemented and runnable:
- The brain (OpenAI):
gm.awaken()starts the in-browser Watcher→Hand loop — it readsgm:transcript+gm.snapshot(), decides whether/how to react on a cheap model (OPENAI_MODEL, defaultgpt-4o-mini), and applies the result (speak/mood/cast).gm.sleep()stops it. Passgm.awaken({ debug: true })(or callgm.debug(true)) to log every AI decision — the Watcher's react/reason/intent/mood and the Hand's cast — to the console. Try the models headless withnpm run test:ai. Implementation:src/game/gm/godbrain.ts. - STT (Deepgram): press
V(orgm.listen()) to stream the mic to Deepgram; finals are fed intogm.hearautomatically.gm.saveSession()downloads the recorded transcript + audio. Token comes fromDEEPGRAM_TOKENin.env; verify it withnpm run test:deepgram. Implementation:src/game/gm/deepgram.ts. - STT in (manual):
gm.hear(text, speaker?)buffers speech intogm.snapshot()and emitsgm:transcript. - Mood:
gm.mood() / setMood / nudgeMood / rollMood()—kindness,chaos,sensitivity(0..1) bias the Watcher's choices;rollMood()is the JS "dice". - Voice out:
gm.speak(text)is the god's on-screen voice (distinct from thesaybanner). - Bridge:
gm.connect(transport)/gm.connectWebSocket(url)stream a curated event feed + snapshots out and acceptcast/speak/mood/exec/snapshotback. See the protocol + reference orchestrator inai/README.md. - Escape hatch:
gm.exec(code)runs raw JS withgmin scope (sandbox-only; prefer structured commands).
Why split the brain in two? The Watcher decides whether to react (most
chatter is ignored — gated by sensitivity) and sets the vibe; the Hand is a
strict, schema-bound translator that only emits valid commands. Each stays
focused and easy to validate.
Extending it
- New action: add a handler to
ACTIONSingm/actions.tsand a matching entry inACTION_SPECS(so it shows up ingm.actions()). - New event: add a
GameEventDeftoLIBRARYingm/library.ts. - New hazard: add an
ActiveEffectingm/effects.ts(it ticks each frame and is removed whenupdate()returnsfalse).
The GM shares the live enemies[] array (so AI/combat/collision pick up spawns
automatically) and routes kills through CombatSystem.killEnemy (so drops and
events fire exactly once). Player invincibility (L) still blocks bomb/bullet
damage.
Analysis
View
Metric
- 68
Figures cover GitHub contributors during the hackathon window. A co-authored commit counts in full for each author, so per-member totals add up to more than the whole-team figures.
Technology
- CSSIn code
- HTMLIn code
- JavaScriptIn code
- TypeScriptIn code
- OpenAIClaimed
4 of 5 appear in the indexed code. 1 claimed on Devpost could not be matched to code, which may simply mean the tool leaves no trace in the repository.
AI coding agents
No AI coding agent signals were found in this repository.
Detected from committed agent config files and commit authorship. Absence of a signal is not proof an agent was unused.
Codebase size
Source size
392 KB
Source files
76
Counts recognized source files only; vendored directories, binaries and lockfiles are excluded, so this is smaller than the repository on disk.
Repository
rPersival/hackathon-event-game
472 files · 966 KB · @ 9db06ab
Structure
Interface
3 files · 1%Screens, components and styles rendered to the user.
Application logic
65 files · 14%Domain rules, services and shared utilities.
+4 more
Supporting
Layers are inferred from where files sit in the tree, not from reading the code. A project that names its directories unconventionally will read oddly here — open the file browser to check anything the diagram implies.
Languages
- TypeScript89%
- Markdown10%
- JavaScript0%
- HTML0%
- CSS0%
Share of indexed source by file size. Binary and vendored files are excluded.
Dependencies
package.json
npm · 8- pixi.js
- +7 more
Declared in the repository’s manifests at the indexed commit. A declared package is not proof it is used, and runtime dependencies are listed first.
Feature verification
Betrayal mechanic (attacking companions/guardian angel)Verified
AI is mischievous and sabotages player; punishes defiance
Claimed on Devpostmedium confidencesrc/game/gm/GameMaster.ts:467— Betrayal handler: fires gm:betray, ally abandons player, favor drops (angel case crashes favor to floor)
Boss room, lock, and victory conditionVerified
Boss fights; clear enemies to seal/reveal boss corridor
Claimed on readmemedium confidencesrc/game/systems/VictorySystem.ts:13— Comment: displays a celebratory YOU WIN screen when the boss is defeatedsrc/game/systems/DoorSystem.ts— File referenced for boss corridor door handling alongside VictorySystem
Deepgram speech-to-text integrationVerified
Deepgram AI transcribes player speech into text
Claimed on Devposthigh confidencesrc/game/gm/deepgram.ts:66— DeepgramSession streams mic audio to Deepgram's live websocket API and feeds finals into gm.hear
External AI orchestrator bridge (WebSocket)Verified
gm.connect/connectWebSocket wires an external AI orchestrator
Claimed on readmemedium confidencesrc/game/gm/GameMaster.ts— connect/connectWebSocket methods present, exposed via gm API in index.tsai/README.md— Documents the wire protocol/orchestrator design
Favor/prayer system (hold E to pray, punishments/rewards)Verified
Pray to it by holding E; obey commands to be rewarded, defy to be smitten
Claimed on readmehigh confidencesrc/game/gm/GameMaster.ts:258— KeyE sets prayHeld; favor is nudged and gm:pray/gm:favor events emittedsrc/game/gm/GameMaster.ts:592— nudgeFavor clamps to FAVOR_MIN/MAX and emits gm:favor
Fog of warVerified
fog of war
Claimed on readmehigh confidencesrc/game/systems/FogOfWar.ts— Dedicated FogOfWar system present and wired as engine System
GameMaster (AI/random god) that injects live eventsVerified
An AI listens to speech and actively controls the environment, generating events like lightning, enemy spawns, traps, blessings
Claimed on Devposthigh confidencesrc/game/gm/GameMaster.ts:224— GameMaster tracks favor, prayer, mood; casts actionssrc/game/gm/actions.ts— Action vocabulary including lightning, spawn, bomb, blessing-style effectssrc/game/gm/library.ts— Built-in event deck (LIBRARY) with hostile/helpful/wild eventssrc/game/gm/effects.ts— Ticking hazard effects (bullets, bombs, lightning, spikes)
Hidden button-puzzle treasure roomVerified
A hidden button-puzzle treasure room
Claimed on readmehigh confidencesrc/game/systems/ButtonPuzzleSystem.ts:22— Monitors red/blue floor buttons; opens SecretWall and chest once both are pressedsrc/game/items/FloorButton.ts— FloorButton item backing the puzzlesrc/game/items/SecretWall.ts— SecretWall item that opens on puzzle solve
Procedurally generated dungeon rooms and corridorsVerified
Procedurally generated rooms, puzzles, and boss fights
Claimed on readmehigh confidencesrc/level/LevelGenerator.ts:58— LevelGenerator class implements grid-based room allocation, sizing, corridor carving, and enemy population, seeded via SeededRandom
Test scripts for Deepgram token and AI pipelineVerified
npm run test:deepgram / npm run test:ai verify voice and AI pipeline
Claimed on readmehigh confidencescripts/test-orchestrator.mjs:1— End-to-end script running sample utterances through Watcher then Hand against real OpenAI callsscripts/test-deepgram.mjs— Script referenced by npm run test:deepgram to verify the Deepgram token
Two-stage LLM pipeline (Watcher -> Hand) turning speech into game eventsVerified
Transcribed text sent to LLM that generates events, then a second LLM translates text into tool calls
Claimed on Devposthigh confidencesrc/game/gm/godbrain.ts:101— consider() runs the Watcher (judge prompt) to decide react/intent, then the Hand (executor prompt) to produce a gm.cast event, calling OpenAI's chat completions API directlyai/prompts/judge.md— Watcher system promptai/prompts/executor.md— Hand/executor system prompt
window.gm console API for driving/testing the godVerified
Implied by 'How we built it' tool-call architecture; documented extensively in README
Claimed on readmehigh confidencesrc/game/gm/index.ts:153— exposeGameMaster() installs a GmApi (cast, run, random, favor, awaken, listen, connect, etc.) on window.gm
Isometric wall tiling / autotiling for map generationCode-supported
Because the walls are isometric, they can connect in many different ways
Claimed on Devpostlow confidencesrc/world/autotile.ts— Autotiling logic exists for wall/tile connections, but README describes the game as top-down pixel-art, not isometric, so the devpost's 'isometric' framing is not clearly corroborated
Deepgram also used to voice the god's phrases (TTS)Claimed only
Deepgram is also used to voice god's phrases
Claimed on Devpostmedium confidenceMultiplayer modeClaimed only
This game's full potential will be realized in multiplayer mode... not yet implemented
Claimed on Devposthigh confidence
An AI agent derived these features from the project’s Devpost page and readme, then searched the code for each one. Verified features are backed by cited code; claimed-only features had no supporting code, which is not by itself proof a feature is missing.
Export this project's context (description, README, evidence, key source files) to chat with an AI agent elsewhere.