esp-modkit 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (85) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +35 -0
  3. data/LICENSE +21 -0
  4. data/README.md +117 -0
  5. data/docs/architecture.md +125 -0
  6. data/docs/authoring-guide.md +206 -0
  7. data/docs/getting-started.md +183 -0
  8. data/docs/reference/api/active-project.md +22 -0
  9. data/docs/reference/api/agent.md +24 -0
  10. data/docs/reference/api/docs-generator.md +20 -0
  11. data/docs/reference/api/http-server.md +46 -0
  12. data/docs/reference/api/index.md +38 -0
  13. data/docs/reference/api/introspection.md +17 -0
  14. data/docs/reference/api/mcp-installer.md +26 -0
  15. data/docs/reference/api/mcp-server.md +27 -0
  16. data/docs/reference/api/mw-builder.md +14 -0
  17. data/docs/reference/api/mw-data-files.md +20 -0
  18. data/docs/reference/api/mw-dialogue-dsl.md +58 -0
  19. data/docs/reference/api/mw-i18n.md +20 -0
  20. data/docs/reference/api/mw-linter.md +18 -0
  21. data/docs/reference/api/mw-loader.md +26 -0
  22. data/docs/reference/api/mw-openmw-config.md +15 -0
  23. data/docs/reference/api/mw-operations.md +24 -0
  24. data/docs/reference/api/mw-preflight.md +17 -0
  25. data/docs/reference/api/mw-reference-index.md +21 -0
  26. data/docs/reference/api/mw-scaffolder.md +13 -0
  27. data/docs/reference/api/mw-script-blob.md +31 -0
  28. data/docs/reference/api/mw-script-extractor.md +17 -0
  29. data/docs/reference/api/operations.md +25 -0
  30. data/docs/reference/api/plugins.md +24 -0
  31. data/docs/reference/api/preferences.md +13 -0
  32. data/docs/reference/api/project-marker.md +23 -0
  33. data/docs/reference/api/providers.md +22 -0
  34. data/docs/reference/api/recents.md +17 -0
  35. data/docs/reference/api/ui.md +21 -0
  36. data/docs/reference/api/vcs.md +17 -0
  37. data/docs/reference/api/watcher.md +11 -0
  38. data/docs/reference/commands.md +271 -0
  39. data/docs/walkthrough.md +193 -0
  40. data/exe/esp +10 -0
  41. data/lib/esp/active_project.rb +71 -0
  42. data/lib/esp/agent.rb +104 -0
  43. data/lib/esp/cli/docs.rb +44 -0
  44. data/lib/esp/cli/i18n.rb +67 -0
  45. data/lib/esp/cli/mcp.rb +52 -0
  46. data/lib/esp/cli/plugins.rb +42 -0
  47. data/lib/esp/cli/refs.rb +137 -0
  48. data/lib/esp/cli/support.rb +87 -0
  49. data/lib/esp/cli.rb +317 -0
  50. data/lib/esp/docs_generator.rb +148 -0
  51. data/lib/esp/http_server.rb +232 -0
  52. data/lib/esp/introspection.rb +151 -0
  53. data/lib/esp/mcp_installer.rb +122 -0
  54. data/lib/esp/mcp_server.rb +465 -0
  55. data/lib/esp/mw/builder.rb +71 -0
  56. data/lib/esp/mw/data_files.rb +67 -0
  57. data/lib/esp/mw/dialogue_dsl.rb +209 -0
  58. data/lib/esp/mw/i18n.rb +113 -0
  59. data/lib/esp/mw/linter.rb +103 -0
  60. data/lib/esp/mw/loader.rb +130 -0
  61. data/lib/esp/mw/openmw_config.rb +138 -0
  62. data/lib/esp/mw/operations.rb +374 -0
  63. data/lib/esp/mw/preflight.rb +161 -0
  64. data/lib/esp/mw/reference_index.rb +182 -0
  65. data/lib/esp/mw/scaffolder.rb +197 -0
  66. data/lib/esp/mw/script_blob.rb +87 -0
  67. data/lib/esp/mw/script_extractor.rb +85 -0
  68. data/lib/esp/mw/tes3conv.rb +38 -0
  69. data/lib/esp/operations.rb +285 -0
  70. data/lib/esp/plugins.rb +75 -0
  71. data/lib/esp/preferences.rb +63 -0
  72. data/lib/esp/project_marker.rb +99 -0
  73. data/lib/esp/providers/anthropic.rb +74 -0
  74. data/lib/esp/providers/ollama.rb +102 -0
  75. data/lib/esp/providers/openai.rb +91 -0
  76. data/lib/esp/providers.rb +76 -0
  77. data/lib/esp/recents.rb +74 -0
  78. data/lib/esp/ui.rb +144 -0
  79. data/lib/esp/vcs.rb +112 -0
  80. data/lib/esp/version.rb +11 -0
  81. data/lib/esp/watcher.rb +55 -0
  82. data/lib/esp.rb +85 -0
  83. data/locales/en.yml +164 -0
  84. data/locales/fr.yml +10 -0
  85. metadata +241 -0
@@ -0,0 +1,13 @@
1
+ <!-- esp:auto — regenerated by `esp docs build`; edits here are overwritten -->
2
+
3
+ # Esp::Mw::Scaffolder
4
+
5
+ **Source:** `lib/esp/mw/scaffolder.rb`
6
+
7
+ Scaffolds a fresh mod folder under `mods/<Mod>/`. One TES3 Header
8
+ record (pre-filled with sensible defaults), one README, and that's
9
+ it — no script / i18n / design subdirectories until the author
10
+ actually wants them. Picks the source format from `--format`;
11
+ subprocess formats get a `#!` line and exec bit.
12
+
13
+ <!-- /esp:auto — write durable docs below this line; they survive rebuilds -->
@@ -0,0 +1,31 @@
1
+ <!-- esp:auto — regenerated by `esp docs build`; edits here are overwritten -->
2
+
3
+ # Esp::Mw::ScriptBlob
4
+
5
+ **Source:** `lib/esp/mw/script_blob.rb`
6
+
7
+ Synthesizes the ZSTD-framed blobs that tes3conv expects for a Script
8
+ record's SCVR (variable list) and SCDT (bytecode) subrecords.
9
+
10
+ Vanilla blobs use ZSTD frames whose blocks may be raw (small/empty
11
+ scripts) or compressed-with-LZ77 matches (large scripts). tes3conv
12
+ accepts either, so we always emit raw blocks — simpler, smaller code,
13
+ and only marginally larger output. Decoding always goes through
14
+ libzstd via zstd-ruby so we can extract names from any vanilla blob.
15
+
16
+ Raw frame layout:
17
+ 4 bytes ZSTD magic (28 b5 2f fd)
18
+ 1 byte Frame Header Descriptor (0x00)
19
+ 1 byte Window Descriptor (0x58)
20
+ 3 bytes Block header ((size << 3) | type<<1 | last_block)
21
+ N bytes Raw payload
22
+
23
+ Payload layout (both SCVR and SCDT placeholder):
24
+ 4 bytes LE uint32 — byte length of names section (0 if none)
25
+ N bytes Null-terminated variable names, concatenated
26
+
27
+ Empty vars and the bytecode placeholder both use a 4-byte zero payload
28
+ (length prefix = 0). A truly zero-byte payload is rejected by tes3conv
29
+ for SCDT and isn't what vanilla emits anywhere.
30
+
31
+ <!-- /esp:auto — write durable docs below this line; they survive rebuilds -->
@@ -0,0 +1,17 @@
1
+ <!-- esp:auto — regenerated by `esp docs build`; edits here are overwritten -->
2
+
3
+ # Esp::Mw::ScriptExtractor
4
+
5
+ **Source:** `lib/esp/mw/script_extractor.rb`
6
+
7
+ Inverse of preflight's text_source resolution. Given a JSON-format mod
8
+ source whose Script records carry inline `text`, hoist each script
9
+ body out to `scripts/<id>.mwscript` and replace the inline fields
10
+ (`text`, `bytecode`, `variables`, `header`) with a single
11
+ `text_source` pointer. Preflight regenerates everything on build, so
12
+ the resulting source round-trips losslessly through `esp build`.
13
+
14
+ Skips records that already have `text_source` set — re-running is a
15
+ no-op. .rb sources are out of scope; their author owns the layout.
16
+
17
+ <!-- /esp:auto — write durable docs below this line; they survive rebuilds -->
@@ -0,0 +1,25 @@
1
+ <!-- esp:auto — regenerated by `esp docs build`; edits here are overwritten -->
2
+
3
+ # Esp::Operations
4
+
5
+ **Source:** `lib/esp/operations.rb`
6
+
7
+ Transport-agnostic service layer — the engine-agnostic shell half. One
8
+ method per operation, each taking a string-keyed params hash and
9
+ returning a plain Ruby hash. The HTTP API (`esp serve`) and the MCP
10
+ server (`esp mcp serve`) are thin shells over these methods plus the
11
+ active plugin's ops, so both frontends emit byte-identical payloads.
12
+
13
+ What's here vs. in the plugin (Esp::Mw::Operations): the ops that have
14
+ no Morrowind-specific concept — version/health/commands, the LLM
15
+ provider seam, project/workspace/preferences state, recents, the
16
+ git-diff review surface (the document under review is opaque to the
17
+ shell). The plugin owns build/lint/unpack/scaffold/i18n/refs/dialogue
18
+ and anything that decodes records.
19
+
20
+ Errors that are the caller's fault (missing field, no index, validation)
21
+ raise InputError; frontends map that to a 400 / tool-error. Lower
22
+ layers raise their own typed errors which both frontends also treat as
23
+ caller errors via the ERROR_CODES table below.
24
+
25
+ <!-- /esp:auto — write durable docs below this line; they survive rebuilds -->
@@ -0,0 +1,24 @@
1
+ <!-- esp:auto — regenerated by `esp docs build`; edits here are overwritten -->
2
+
3
+ # Esp::Plugins
4
+
5
+ **Source:** `lib/esp/plugins.rb`
6
+
7
+ The game-plugin registry. Each plugin (Esp::Mw today; future Esp::Ob,
8
+ Esp::Sr) registers itself here at load time with a short `id` (the value
9
+ that lands in a project's `.espresso/project.json` `game:` field) and the
10
+ modules the frontends need to reach into — its Operations façade today.
11
+
12
+ The shell composes its routes/tools against this registry, so the only
13
+ thing tying the shell to a specific game is the project the user opens.
14
+ Add a plugin: drop `lib/esp/<id>/`, call `Esp::Plugins.register` from its
15
+ entry file, and add it to the load manifest.
16
+
17
+ Lookups are by id string; `default_id` names the plugin used when no
18
+ project is open (today: the first registered, which is `mw` because the
19
+ load manifest loads it first). Slice 2 wires Esp::Operations.dispatch
20
+ through `Esp::Plugins.active_for(input)` so an op like `build` reaches
21
+ the plugin matching the active project's `game:` field rather than
22
+ always Esp::Mw::Operations.
23
+
24
+ <!-- /esp:auto — write durable docs below this line; they survive rebuilds -->
@@ -0,0 +1,13 @@
1
+ <!-- esp:auto — regenerated by `esp docs build`; edits here are overwritten -->
2
+
3
+ # Esp::Preferences
4
+
5
+ **Source:** `lib/esp/preferences.rb`
6
+
7
+ Per-user preferences persisted as JSON alongside the recents file at
8
+ ESP_DATA_DIR/preferences.json. Today: just `mods_home` (the directory the
9
+ Open Project picker starts in / New Project scaffolds into). More keys to
10
+ come; the merge-with-defaults read keeps backward compatibility as the
11
+ schema grows. Same single-writer / mutex story as Recents.
12
+
13
+ <!-- /esp:auto — write durable docs below this line; they survive rebuilds -->
@@ -0,0 +1,23 @@
1
+ <!-- esp:auto — regenerated by `esp docs build`; edits here are overwritten -->
2
+
3
+ # Esp::ProjectMarker
4
+
5
+ **Source:** `lib/esp/project_marker.rb`
6
+
7
+ Reads, writes, and discovers the `.esp/project.json` marker that
8
+ identifies a directory as an esp project. Used by:
9
+
10
+ - `esp init` and `Esp::Operations.projects_new` to write the marker.
11
+ - `Esp::Operations.open_project` to read the marker's `game:` field so
12
+ the plugin registry knows which Operations module owns this project.
13
+ - The CLI cwd walk-up so `esp build` works from anywhere inside a
14
+ project tree without `--root`.
15
+
16
+ Marker filename rename (step 23.5 slice 3): the canonical location is
17
+ now `.esp/project.json`. Projects scaffolded by an earlier ESPresso
18
+ release wrote `.espresso/project.json`; readers accept both, writers
19
+ only emit the new name. The back-compat lookup goes away when the
20
+ release that drops it ships (no auto-migration — we don't silently
21
+ rewrite a user's project files).
22
+
23
+ <!-- /esp:auto — write durable docs below this line; they survive rebuilds -->
@@ -0,0 +1,22 @@
1
+ <!-- esp:auto — regenerated by `esp docs build`; edits here are overwritten -->
2
+
3
+ # Esp::Providers
4
+
5
+ **Source:** `lib/esp/providers.rb`
6
+
7
+ The LLM-provider seam behind Esp::Agent. Each provider translates the
8
+ agent's neutral transcript to its API's native wire shape and normalizes
9
+ the response back to a Completion. Providers self-register here, so adding
10
+ one is a new file under providers/ plus a require in the load manifest.
11
+
12
+ Provider contract:
13
+ #complete(system:, tools:, messages:) -> Completion(text:, tool_calls:, raw:)
14
+ system — String system prompt
15
+ tools — Array<{name:, description:, input_schema:}> (JSON-Schema)
16
+ messages — the neutral transcript (see Esp::Agent)
17
+ raw — the provider-native assistant *message* for this turn, stored
18
+ on the assistant entry and replayed verbatim next call so
19
+ provider-side state (Anthropic thinking signatures, OpenAI
20
+ tool_calls) survives multi-turn tool use.
21
+
22
+ <!-- /esp:auto — write durable docs below this line; they survive rebuilds -->
@@ -0,0 +1,17 @@
1
+ <!-- esp:auto — regenerated by `esp docs build`; edits here are overwritten -->
2
+
3
+ # Esp::Recents
4
+
5
+ **Source:** `lib/esp/recents.rb`
6
+
7
+ Per-user "recently opened projects" list, persisted as JSON in the data
8
+ directory the Tauri shell injects via ESP_DATA_DIR (macOS:
9
+ ~/Library/Application Support/com.coreyellis.espresso/). Falls back to
10
+ ~/.config/esp/ for CLI / unit-test use. The MW_DATA_DIR env var still
11
+ works as a one-release deprecation alias so existing dev shells keep
12
+ finding the same on-disk state; remove after step 24 ships.
13
+ Single-writer per process — the mutex serialises threaded WEBrick
14
+ handlers; cross-process collisions are accepted in v1 (one ESPresso ↔
15
+ one backend).
16
+
17
+ <!-- /esp:auto — write durable docs below this line; they survive rebuilds -->
@@ -0,0 +1,21 @@
1
+ <!-- esp:auto — regenerated by `esp docs build`; edits here are overwritten -->
2
+
3
+ # Esp::Ui
4
+
5
+ **Source:** `lib/esp/ui.rb`
6
+
7
+ Internal i18n for the *tool's own* user-facing strings — CLI output and
8
+ error messages. Distinct from Esp::Mw::I18n, which localizes mod *content*;
9
+ this localizes mw itself so the distributed tool can ship in other
10
+ languages.
11
+
12
+ Catalogues live at locales/<locale>.yml, resolved relative to this file
13
+ (they ship with the tool, independent of Esp::ROOT — which points at the
14
+ user's mod project). Lookup is dot-pathed with %{named} interpolation,
15
+ falling back to en, then to the key itself.
16
+
17
+ Active locale: ESP_UI_LOCALE (with MW_UI_LOCALE honoured as a one-release
18
+ deprecation alias), then LANG/LC_ALL (stripped to the language subtag,
19
+ e.g. "fr_FR.UTF-8" -> "fr"), then en. Tests pin it to en.
20
+
21
+ <!-- /esp:auto — write durable docs below this line; they survive rebuilds -->
@@ -0,0 +1,17 @@
1
+ <!-- esp:auto — regenerated by `esp docs build`; edits here are overwritten -->
2
+
3
+ # Esp::Vcs
4
+
5
+ **Source:** `lib/esp/vcs.rb`
6
+
7
+ A thin wrapper over the `git` CLI, scoped to a working-tree `root`. Backs
8
+ the diff-review loop (step 20): list working-tree changes, show one file's
9
+ diff, stage approved files, discard rejected ones. We shell out rather than
10
+ bind libgit2 (nothing extra to ship) — the project is already a git repo.
11
+
12
+ Every method takes an explicit `root` so it operates on the *user's* mod
13
+ project, never the toolchain repo, and so it's testable against a scratch
14
+ repo. Git failures raise GitError; the Operations layer maps that to a
15
+ caller-facing error.
16
+
17
+ <!-- /esp:auto — write durable docs below this line; they survive rebuilds -->
@@ -0,0 +1,11 @@
1
+ <!-- esp:auto — regenerated by `esp docs build`; edits here are overwritten -->
2
+
3
+ # Esp::Watcher
4
+
5
+ **Source:** `lib/esp/watcher.rb`
6
+
7
+ File-watch-driven rebuild. Watches a mod's source directory (and
8
+ its scripts/, i18n/, etc. subtrees) and re-invokes Esp::Mw::Builder on
9
+ any matching change. Blocks until SIGINT.
10
+
11
+ <!-- /esp:auto — write durable docs below this line; they survive rebuilds -->
@@ -0,0 +1,271 @@
1
+ <!-- esp:auto — regenerated by `esp docs build`; edits here are overwritten -->
2
+
3
+ # Command reference
4
+
5
+ Every command accepts `--json` for structured output to stdout.
6
+ Errors print as `{"error": "..."}` to stderr with a non-zero exit.
7
+
8
+ ## Top-level commands
9
+
10
+ ### `esp version`
11
+
12
+ Print esp version
13
+
14
+ Usage: `esp version`
15
+
16
+ Options:
17
+ - `--json` — Output structured JSON instead of human text
18
+ - `--root STRING` — Project root (defaults to the active project or the toolchain repo)
19
+
20
+ ### `esp doctor`
21
+
22
+ Check install prerequisites (Ruby, tes3conv, references index)
23
+
24
+ Usage: `esp doctor`
25
+
26
+ Options:
27
+ - `--json` — Output structured JSON instead of human text
28
+ - `--root STRING` — Project root (defaults to the active project or the toolchain repo)
29
+
30
+ ### `esp init`
31
+
32
+ Bootstrap an esp project here (or in NAME subdir) — writes .esp/project.json + git init
33
+
34
+ Usage: `esp init [NAME]`
35
+
36
+ Options:
37
+ - `--game STRING` — Game plugin (default: mw; pass --game ob etc. once those plugins exist)
38
+ - `--force` — Re-initialise even if .esp/project.json already exists
39
+ - `--json` — Output structured JSON instead of human text
40
+ - `--root STRING` — Project root (defaults to the active project or the toolchain repo)
41
+
42
+ ### `esp setup`
43
+
44
+ Configure git diff driver + tracked pre-commit hooks
45
+
46
+ Usage: `esp setup`
47
+
48
+ Options:
49
+ - `--json` — Output structured JSON instead of human text
50
+ - `--root STRING` — Project root (defaults to the active project or the toolchain repo)
51
+
52
+ ### `esp new`
53
+
54
+ Scaffold a new mod folder under mods/<MOD>/
55
+
56
+ Usage: `esp new MOD`
57
+
58
+ Options:
59
+ - `--format STRING` — Source format: json | rb | py | js | mjs | ts
60
+ - `--author STRING` — Author name (default: git config user.name)
61
+ - `--description STRING` — Plugin description
62
+ - `--force` — Overwrite an existing mod folder
63
+ - `--json` — Output structured JSON instead of human text
64
+ - `--root STRING` — Project root (defaults to the active project or the toolchain repo)
65
+
66
+ ### `esp unpack`
67
+
68
+ Import a plugin (a path, or an installed plugin NAME) to mods/<name>/
69
+
70
+ Usage: `esp unpack PLUGIN [NAME]`
71
+
72
+ Options:
73
+ - `--config STRING` — openmw.cfg to resolve a bare plugin NAME against
74
+ - `--json` — Output structured JSON instead of human text
75
+ - `--root STRING` — Project root (defaults to the active project or the toolchain repo)
76
+
77
+ ### `esp build`
78
+
79
+ Build mods/<MOD>/<MOD>.{json,rb,py,js,mjs,ts} -> dist/<MOD>[.locale].esp
80
+
81
+ Usage: `esp build [MOD]`
82
+
83
+ Options:
84
+ - `--all` — Build every mod in mods/
85
+ - `--install` — After building, register the mod(s) with openmw.cfg
86
+ - `--config STRING` — openmw.cfg to register with (implies --install target)
87
+ - `--locale STRING` — Build with this locale (suffixes the output: dist/<MOD>.<locale>.esp)
88
+ - `--json` — Output structured JSON instead of human text
89
+ - `--root STRING` — Project root (defaults to the active project or the toolchain repo)
90
+
91
+ ### `esp install`
92
+
93
+ Register dist/<MOD>.esp with OpenMW's openmw.cfg (and/or copy to Data Files)
94
+
95
+ Usage: `esp install MOD`
96
+
97
+ Options:
98
+ - `--copy_to STRING` — Also copy the built .esp into PATH (original-engine users: Morrowind/Data Files/)
99
+ - `--to_data_files` — Also copy to the auto-detected Morrowind Data Files dir (MORROWIND_DATA env override)
100
+ - `--register_openmw` — Register with openmw.cfg (default: true; --no-register-openmw skips)
101
+ - `--json` — Output structured JSON instead of human text
102
+ - `--root STRING` — Project root (defaults to the active project or the toolchain repo)
103
+
104
+ ### `esp lint`
105
+
106
+ Find dangling refs and missing-master issues using the reference index
107
+
108
+ Usage: `esp lint MOD`
109
+
110
+ Options:
111
+ - `--json` — Output structured JSON instead of human text
112
+ - `--root STRING` — Project root (defaults to the active project or the toolchain repo)
113
+
114
+ ### `esp watch`
115
+
116
+ Rebuild MOD on any change under mods/<MOD>/. Blocks until Ctrl-C.
117
+
118
+ Usage: `esp watch MOD`
119
+
120
+ Options:
121
+ - `--locale STRING` — Build with this locale on each change
122
+ - `--json` — Output structured JSON instead of human text
123
+ - `--root STRING` — Project root (defaults to the active project or the toolchain repo)
124
+
125
+ ### `esp serve`
126
+
127
+ Start the HTTP API on localhost (mirrors the CLI; same payload shapes)
128
+
129
+ Usage: `esp serve`
130
+
131
+ Options:
132
+ - `--port NUMERIC` — TCP port to bind on localhost
133
+ - `--json` — Output structured JSON instead of human text
134
+ - `--root STRING` — Project root (defaults to the active project or the toolchain repo)
135
+
136
+ ### `esp extract-scripts`
137
+
138
+ Move every Script record's inline text into mods/<MOD>/scripts/<id>.mwscript
139
+
140
+ Usage: `esp extract-scripts MOD`
141
+
142
+ Options:
143
+ - `--json` — Output structured JSON instead of human text
144
+ - `--root STRING` — Project root (defaults to the active project or the toolchain repo)
145
+
146
+ ## `esp plugins` subcommand group
147
+
148
+ Inspect installed OpenMW plugins
149
+
150
+ ### `esp plugins list`
151
+
152
+ List plugins installed in OpenMW (active flag + load order)
153
+
154
+ Usage: `esp list`
155
+
156
+ Options:
157
+ - `--config STRING` — Path to openmw.cfg (default: per-OS user config)
158
+ - `--json` — Output structured JSON instead of human text
159
+
160
+ ## `esp refs` subcommand group
161
+
162
+ Manage vanilla ESM references
163
+
164
+ ### `esp refs unpack`
165
+
166
+ Convert vanilla ESMs to JSON under the per-user references directory
167
+
168
+ Usage: `esp unpack`
169
+
170
+ Options:
171
+ - `--data STRING` — Data Files directory (default: $MORROWIND_DATA or the detected install)
172
+ - `--json` — Output structured JSON instead of human text
173
+ - `--references_dir STRING` — Override the references directory (default: $ESP_REFERENCES_DIR or $ESP_DATA_DIR/references)
174
+
175
+ ### `esp refs index`
176
+
177
+ Build/refresh the SQLite index over the per-user references directory
178
+
179
+ Usage: `esp index`
180
+
181
+ Options:
182
+ - `--json` — Output structured JSON instead of human text
183
+ - `--references_dir STRING` — Override the references directory (default: $ESP_REFERENCES_DIR or $ESP_DATA_DIR/references)
184
+
185
+ ### `esp refs find`
186
+
187
+ Find vanilla records — substring match on id + name by default
188
+
189
+ Usage: `esp find [QUERY]`
190
+
191
+ Options:
192
+ - `--type STRING` — Filter by record type (e.g. Npc, Cell, Script)
193
+ - `--like STRING` — SQL LIKE pattern on id (e.g. 'Fargoth%')
194
+ - `--exact` — Match QUERY as an exact id instead of a substring
195
+ - `--show` — Print full JSON of each match
196
+ - `--limit NUMERIC` — Max rows to print
197
+ - `--json` — Output structured JSON instead of human text
198
+ - `--references_dir STRING` — Override the references directory (default: $ESP_REFERENCES_DIR or $ESP_DATA_DIR/references)
199
+
200
+ ## `esp i18n` subcommand group
201
+
202
+ Translation catalogue tools
203
+
204
+ ### `esp i18n check`
205
+
206
+ Report missing/orphan i18n keys per locale (vs. en)
207
+
208
+ Usage: `esp check MOD`
209
+
210
+ Options:
211
+ - `--json` — Output structured JSON instead of human text
212
+ - `--root STRING` — Project root (defaults to the active project or the toolchain repo)
213
+
214
+ ### `esp i18n audit`
215
+
216
+ Audit tool-UI locales: undefined/unused/orphan keys + missing translations
217
+
218
+ Usage: `esp audit`
219
+
220
+ Options:
221
+ - `--json` — Output structured JSON instead of human text
222
+ - `--root STRING` — Project root (defaults to the active project or the toolchain repo)
223
+
224
+ ## `esp docs` subcommand group
225
+
226
+ Generate / introspect reference documentation
227
+
228
+ ### `esp docs build`
229
+
230
+ Regenerate docs/reference/ from the CLI tree and module headers
231
+
232
+ Usage: `esp build`
233
+
234
+ Options:
235
+ - `--out STRING` — Output directory (relative to repo root)
236
+ - `--json` — Output structured JSON instead of human text
237
+
238
+ ### `esp docs introspect`
239
+
240
+ Dump the CLI command tree + module surface as JSON (always JSON)
241
+
242
+ Usage: `esp introspect`
243
+
244
+ Options:
245
+ - `--json` — Output structured JSON instead of human text
246
+
247
+ ## `esp mcp` subcommand group
248
+
249
+ Model Context Protocol server for AI tools
250
+
251
+ ### `esp mcp serve`
252
+
253
+ Run the MCP server over stdio (JSON-RPC 2.0) for AI clients
254
+
255
+ Usage: `esp serve`
256
+
257
+ Options:
258
+ - `--json` — Output structured JSON instead of human text
259
+
260
+ ### `esp mcp install`
261
+
262
+ Register `esp mcp serve` with an AI client (Claude Code / Desktop)
263
+
264
+ Usage: `esp install`
265
+
266
+ Options:
267
+ - `--client STRING` — Which MCP client to configure
268
+ - `--name STRING` — Name to register the server under in mcpServers
269
+ - `--json` — Output structured JSON instead of human text
270
+
271
+ <!-- /esp:auto — write durable docs below this line; they survive rebuilds -->