A Modern TypeScript Interactive Fiction Platform
Sharpee is a modern, TypeScript-based Interactive Fiction platform designed for extensibility, clean separation of concerns, and developer experience. This report focuses on recent platform improvements since December 2025.
All 11 core packages published, current version 0.9.2-beta.1
npm install @sharpee/sharpee@beta
| Feature | Status | Details |
|---|---|---|
| Monorepo Dependencies | Complete | Converted all file:../ to workspace:* |
| TypeScript Project References | Complete | All packages have proper references arrays |
| Build Automation | Complete | scripts/build-release.sh with version bumping |
| GitHub Actions CI | Complete | Automatic npm publish on tag push |
Use pnpm publish instead of npm publish in monorepos! Only pnpm properly converts workspace:* protocol to real version numbers during publish. npm OIDC Trusted Publishing was unreliable; classic token auth just works.
Complete type safety for the event system with 25+ typed event data types.
EventDataRegistry with declaration merging
// Type-safe event creation
const event = createTypedEvent('if.event.taken', {
actor: playerId,
target: lampId,
from: roomId
});
// Type guards narrow the type
if (isEventType(event, 'quit.confirmed')) {
console.log(event.data.finalScore); // TypeScript knows this exists
}
// Safe data extraction with defaults
const { message, hint } = getEventDataWithDefaults(event, 'query.invalid', {
message: 'Invalid response.',
hint: undefined
});
| Feature | Location | Purpose |
|---|---|---|
EventDataRegistry |
@sharpee/core | Central registry for event data types |
TypedSemanticEvent<T> |
@sharpee/core | Generic typed event wrapper |
createTypedEvent() |
@sharpee/core | Type-safe event factory |
isEventType() |
@sharpee/core | Type guard for event narrowing |
getEventDataWithDefaults() |
@sharpee/core | Safe extraction with fallbacks |
| 25+ IF Event Types | @sharpee/stdlib | TakenEventData, DroppedEventData, etc. |
New slot types and simplified story grammar access.
VOCABULARY, MANNER, and enhanced DIRECTION handling
// Context-aware vocabulary slots
vocab.define('panel-colors', {
words: ['red', 'yellow', 'mahogany', 'pine'],
when: (ctx) => ctx.currentLocation === insideMirrorId
});
grammar.define('push :color panel')
.fromVocabulary('color', 'panel-colors')
.mapsTo('push_panel')
.build();
// Manner adverbs (16 built-in)
grammar.define(':manner open :target')
.manner('manner')
.mapsTo('open')
.build();
// "carefully open door" -> command.manner === 'carefully'
// Direction slots with automatic constant conversion
grammar.define('push :direction wall')
.direction('direction')
.mapsTo('push_wall')
.build();
| Feature | Purpose |
|---|---|
IGrammarVocabularyProvider |
Context-aware vocabulary for grammar patterns |
| Named Categories | Organize vocabulary by purpose (e.g., 'panel-colors') |
| Context Predicates | when function scopes vocabulary to locations/states |
| Dynamic Extension | Stories can extend() categories at runtime |
carefully, quietly, quickly, slowly, forcefully, gently, loudly, softly, cautiously, boldly, stealthily, silently, hastily, deliberately, violently, tenderly
Removed 789 lines of wrapper code. Stories now get direct GrammarBuilder access via parser.getStoryGrammar(), enabling all slot types including .direction(), .vocabulary(), and .manner().
Single source of truth for darkness and perception checking.
Sense-based perception with event filtering
// Check if actor can perceive via a specific sense
const canSee = perception.canPerceive(actor, location, world, 'sight');
const canHear = perception.canPerceive(actor, location, world, 'hearing');
// Filter events based on perception
const filtered = perception.filterEvents(events, actor, location, world);
// Visual events in darkness -> perception.blocked events
// Event flow with perception:
Action executes
-> Events generated (room.description, contents.listed, etc.)
-> PerceptionService.filterEvents()
- Visual events in dark -> perception.blocked
- Non-visual events pass through
-> TextService renders
| Component | Change |
|---|---|
VisibilityBehavior.isDark() |
Single source of truth for room darkness |
LightSourceTrait.isLit |
Now optional - enables fallback logic |
| Light Priority | 1. Explicit isLit, 2. Switchable isOn, 3. Default to lit |
| Transparent Containers | Light inside glass box illuminates room |
| Worn Items | Light sources worn by actor provide illumination |
| Sense | Status | Blocking Condition |
|---|---|---|
| Sight | Implemented | Darkness (no light source present) |
| Hearing | Interface Ready | Currently always passes |
| Smell | Interface Ready | Currently always passes |
| Touch | Interface Ready | Currently always passes |
IFID generation and validation for story identification.
Generate, validate, normalize IFIDs
# CLI commands
sharpee ifid generate
# -> 621168D1-6D5C-449F-83D5-841D03A1BF78
sharpee ifid validate 621168D1-6D5C-449F-83D5-841D03A1BF78
# -> Valid IFID
# Programmatic usage
import { generateIfid, validateIfid, normalizeIfid } from '@sharpee/core';
const ifid = generateIfid(); // UUID v4, uppercase
validateIfid(ifid); // Treaty of Babel: 8-63 chars, A-Z/0-9/-
normalizeIfid(ifid); // Uppercase conversion
| Interface | Purpose |
|---|---|
StoryMetadata |
Title, author, IFID, version, description |
SharpeeConfig |
package.json extension for story configuration |
Single source of truth via VisibilityBehavior.isDark()
Sense-based perception with event filtering
Treaty of Babel compliant story identification
npm package configuration and publishing
Type-safe events with EventDataRegistry
VOCABULARY, MANNER slot types with context predicates
Non-physical player character design for emotional IF
Direct GrammarBuilder access, -789 lines
Action Refactoring Complete
All 43 stdlib actions verified to follow four-phase pattern. Platform assessment completed.
Perception & Darkness Systems
PerceptionService implemented. Unified darkness checking (ADR-068). Cloak of Darkness fully working.
Beta Release Preparation
IFID utilities (ADR-074). Package preparation (ADR-081). Build automation scripts.
npm Beta Release
All 11 packages published to npm at 0.9.1-beta.13. Monorepo refactored to workspace:* dependencies. GitHub Actions CI/CD working.
Typed Event System (ADR-082)
EventDataRegistry with 25+ event types. Type guards and helpers. Declaration merging for stdlib events.
Grammar Enhancements (ADR-082)
GrammarVocabularyProvider with context predicates. VOCABULARY and MANNER slot types. 25 new unit tests.
Grammar Simplification (ADR-084)
StoryGrammarImpl wrapper removed (-789 lines). Stories get direct GrammarBuilder access.
New Stdlib Actions
WAVE, DIG, WIND actions added. Action pattern fully validated.
Version 0.9.2-beta.1
New SEND action added (47 total). All 12 packages version bumped. 636 transcript tests passing.
The platform has matured significantly with the beta release. Type safety, perception systems, and grammar enhancements address key developer experience concerns.
| Aspect | Rating | Recent Improvements |
|---|---|---|
| Four-Phase Compliance | Excellent | 100% of 47 actions follow pattern |
| Type Safety | Excellent | Typed events (ADR-082) |
| Grammar System | Excellent | Vocabulary/manner slots, simplified API |
| Perception System | Very Good | PerceptionService, unified darkness |
| npm Publishing | Complete | 11 packages, CI/CD automated |
| Documentation | Excellent | 89+ ADRs, inline docs |
| Category | Remaining Work |
|---|---|
| Documentation | Author guides, API reference, example games |
| GenAI Layer | Story spec templates, LLM code generation pipeline |
| Advanced Features | Disambiguation, pronoun resolution, undo/redo |
| Web Client | React template, accessibility, standard UI |
| NPC System | Conversation trees, behavior scheduling |