esp — author Morrowind plugins as source code
esp is a Ruby toolchain for authoring Morrowind plugins without the
Construction Set. You write records in JSON, Ruby, Python,
JavaScript, or TypeScript; esp builds them to an .esp via
tes3conv, manages scripts, translations, and dialogue, and lints
against the real vanilla game data. Every operation renders as human
text or structured JSON, so a CLI, an HTTP API, an MCP server, and an AI
agent all drive the same pipeline.
Why — source is truth, the .esp is output
The Construction Set is a direct-manipulation binary editor: the .esp
is the document. esp inverts that. You author intent as
human-readable source and the .esp is a disposable build artifact —
the relationship a compiler has with its object files. You never
hand-edit the binary; you edit source and rebuild.
That one inversion buys the whole software-engineering toolchain:
git diff / blame / branch / review, procedural generation, lint as a
build step, CI, HTTP/MCP automation, and AI authoring. The cost is the
loss of direct manipulation — spatial placement and visual browsing.
ESPresso is the planned AI-first GUI built on
this backend, where you describe intent, an agent writes diffable
source, and you review the git diff.
Quickstart
Install once:
gem install esp-modkit # puts `esp` on your PATH
# tes3conv is a required prerequisite, not on Homebrew: download a release
# https://github.com/Greatness7/tes3conv/releases
# or: cargo install --git https://github.com/Greatness7/tes3conv
esp doctor # confirm Ruby, tes3conv, and the references index
esp refs unpack # unpack vanilla ESMs to ~/.config/esp/references/ (one-time)
esp refs index # build the SQLite index there (one-time)
Native extensions (sqlite3, zstd-ruby) compile on install, so a C
toolchain is needed. To work from a checkout instead of the gem, clone the
repo, bundle install, and put bin/esp on your PATH (the launcher pins
this repo's Ruby + Gemfile).
Then per-project — anywhere on disk:
mkdir -p ~/morrowind/MyFirstMod && cd ~/morrowind/MyFirstMod
esp init # writes .esp/project.json + git init + mods/ + dist/
esp new MyMod # scaffold mods/MyMod/
esp lint MyMod # check references against vanilla data
esp build MyMod # -> dist/MyMod.esp (in *this* project)
esp install MyMod # OpenMW: register via data= + content=
esp install MyMod --to-data-files # Original engine: copy into Morrowind/Data Files/
esp walks up from the current directory to find the .esp/project.json
marker, so commands work from any subdir of your project tree —
no flags needed inside a project, --root PATH if you're scripting
across several, or export ESP_PROJECT_ROOT=… to pin one per shell.
New here? Read the walkthrough — one plugin from empty to built, with the thesis shown in practice.
Three frontends, one pipeline
Every command has a --json mode whose payload is identical across all
three frontends:
| Frontend | Command | For |
|---|---|---|
| CLI | esp <cmd> |
humans + scripts |
| HTTP | esp serve |
ESPresso, local automation |
| MCP | esp mcp serve (esp mcp install to wire up) |
AI clients (Claude Code / Desktop) |
Command surface
doctor check prerequisites (Ruby, tes3conv, refs index)
init [NAME] new project (marker + git init + mods/ + dist/)
new MOD [--format rb|py|js|ts] scaffold a mod
build [MOD] [--locale fr] [--all] [--install] build -> dist/MOD[.locale].esp
watch MOD rebuild on change
lint MOD dangling-ref check (needs refs index)
extract-scripts MOD hoist inline script text to files
unpack PLUGIN [NAME] .esp -> mods/NAME/NAME.json
install MOD [--copy-to PATH | --to-data-files] OpenMW + optional file copy
refs unpack | index | find Q vanilla data: build + query
i18n check MOD missing/orphan locale keys
docs build | introspect regenerate / dump docs
serve | mcp serve HTTP API / MCP server
Documentation
- Walkthrough — empty → built, end to end.
- Getting started — install, paths on
macOS, wiring
espinto an AI client. - Authoring guide — source formats,
text_source, dialogue DSL, translation, linting. - Architecture — layered design, the roadmap, why each piece exists.
- Command reference (auto-generated)
- API reference (auto-generated)
Regenerate the auto pages with bin/esp docs build; a pre-commit hook
fails if they drift.
Development
bundle exec rake # RuboCop + Minitest (must be green)
bundle exec rake admin # local quality reports (coverage, lint, stats)