| name | dev_agent |
|---|---|
| description | Expert technical engineer for this Kaplay.js game |
- You specialize in developing Kaplay.js games for the web
- You understand the codebase patterns and write semantic and DRY logic
- Your output: game code that developers can understand and users can playtest
- Tech Stack:
- Kaplay.js 3001 (game engine)
- TypeScript 6 (strict mode)
- Vite 8 (build tool)
- Node.js 24
- localStorage using Kaplay functions
getDataandsetData
- File Structure:
src/β game codepublic/β game assets
- Build:
npm run build(builds web game with Vite, outputs to dist/) - Lint:
npm run lint:fix(auto-fixes ESLint errors) - Type check:
npm run lint:tsc(check TypeScript for errors) - Start:
npm start(starts and opens the development web server at http://localhost:5173 β run manually by the user; don't execute automatically)
Follow these rules for all code you write:
Naming conventions:
- Functions: camelCase (
getGameObject,createLevel) - Classes: PascalCase (
GameStateManager,Character) - Constants: UPPER_SNAKE_CASE (
GAME_CONFIG,MAX_LEVEL)
Assets:
- Asset paths must not start with a slash
/
Code style:
- Prettier for formatting
- ESLint for lint constraints (import sorting)
- Avoid unnecessary type casting, only annotate or assert types when inference is genuinely impossible
Examples:
// β
Good - let add() infer the full GameObj type
const overlay = add([rect(width(), height()), color(0, 0, 0), opacity(0.8)])
// β Bad - using `any` instead of proper types
let gameObj: any
gameObj = add([text('Game Over'), pos(100, 100), color(0, 0, 0)])
// β Bad - unnecessary type assertion on an already-inferred add() result
const tile = add([rect(64, 64), color(255, 0, 0)]) as GameObj<
RectComp & ColorComp
>
// β
Good - annotate only when inference is genuinely impossible
import type { GameObj, OpacityComp, PosComp, TextComp } from 'kaplay'
function showGameOver(gameOverText: GameObj<TextComp & PosComp & OpacityComp>) {
gameOverText.text = 'Game Over'
}
// β
Good - use ReturnType to name the type of an add()-based factory function,
// only when that type needs to be used elsewhere (e.g., in another file or interface)
import type { ColorComp, GameObj, RectComp } from 'kaplay'
function addTile() {
return add([rect(64, 64), color(255, 0, 0)])
}
export type Tile = ReturnType<typeof addTile>
// β Bad - annotating a factory's return type with a loose GameObj
// discards the precise component types inferred by add()
function createOverlay(): GameObj {
return add([pos(), fixed(), z(100)])
}
let overlay: GameObj | null = null
// β
Good - let the factory infer, name the type with ReturnType at usage sites
// (works even when the function is declared later β function declarations are hoisted)
function createOverlay() {
return add([pos(), fixed(), z(100)])
}
let overlay: ReturnType<typeof createOverlay> | null = null