ecs-queries
Create ECS query helpers that read from the world without mutations
When & Why to Use This Skill
This Claude skill streamlines the development of Entity Component System (ECS) architectures by automating the creation of standardized, read-only query helpers. It enforces best practices such as immutability, domain-driven organization, and strict type safety, which are essential for maintaining scalable and bug-free data access layers in complex game engines or reactive systems.
Use Cases
- Inventory & Equipment Systems: Creating dedicated query files to fetch player items, equipped gear, or specific item types from the world state without risking accidental mutations.
- AI & Spatial Reasoning: Implementing read-only helpers to locate nearby entities, filter enemies by health thresholds, or identify targets within a specific grid or region.
- Performance Monitoring & Analytics: Generating derived data queries to compute real-time statistics, such as total entity counts or resource distribution, for debugging or UI dashboards.
- State Synchronization: Providing clean, typed interfaces for external systems to read the current world state safely during multi-threaded or asynchronous operations.
| name | ecs-queries |
|---|---|
| description | Create ECS query helpers that read from the world without mutations |
ECS Queries
Write query helper functions in src/ecs/queries/ that provide reusable read-only access to the world.
Requirements
- File naming:
*Queries.ts(e.g.,inventoryQueries.ts) - Must reference:
worldfor queries - Group related queries in single file
Pattern
import { world } from '../world';
import type { Entity } from '../world';
// Simple query - returns entities
export function getEntitiesWithComponent(): Entity[] {
return world.with('componentName').entities;
}
// Filtered query - returns subset
export function getEntitiesByType(type: string): Entity[] {
return world.with('componentName').entities.filter((e) => e.componentName.type === type);
}
// Single entity query - returns entity or undefined
export function getEntityById(id: string): Entity | undefined {
return world.get(id);
}
// Derived data query - returns computed value
interface GetDerivedDataReturn {
count: number;
items: Entity[];
}
export function getDerivedData(): GetDerivedDataReturn {
const items = world.with('item').entities;
return {
count: items.length,
items,
};
}
Rules
- Read-only - Queries must not mutate the world
- Must use world - All queries access
worlddirectly - Return typed data - Define return interfaces for complex returns
- Group by domain - Related queries in same file (e.g., all inventory queries)
- No side effects - Pure functions that only read and return
When to Use Queries vs Utils
Use Query (src/ecs/queries/) |
Use Util (src/utils/) |
|---|---|
Needs world access |
Pure calculation/transformation |
| Returns entities or entity data | Works on any input data |
| Domain-specific (inventory, etc.) | Generic utility (arrays, math) |
Examples
Good queries:
getEquippedItems()- returns entities with equipment componentgetGridEntities(gridId)- returns items in a specific gridfindItemByName(name)- searches world for matching item
Bad (should be utils):
sortByName(items)- pure array sort, no world accesscalculateWeight(item)- pure calculation on passed data