BUILT — see Headless-CLI.md for the real, current reference.
rebuild-model+cleanverified;build-modwired to the game’s ownModuleEditor.BuildModification(full build+deploy, headless). This file is the original design/reasoning; the “build-mod needs discovery / build stays in the editor” speculation below was superseded once theMercury ▸ Mod Editorbuild method was found and called via reflection.
Status: designed, not built. The documentation work made HAF readable by AI (llms.txt, the Pages site). This is
the complement: a command-line surface that makes the editor’s functions operable without the GUI — so an agent, a
script, or CI can author, validate, and bake content the same way a human does in Tools ▸ HAF, minus the clicking.
Unity + the ENCReload project are already a hard requirement to use the authoring tools. A batch-mode CLI therefore
adds no new requirement — it just drives the existing, GUI-free pipeline from the command line. The bake tests already
prove this works: BakeSmokeTest / BakeFeatureTest run the real ConfigFor → UniversalBaker path as static methods
with no window open. The CLI is the same invocation, parameterised.
| Capability | Needs Unity? | How |
|---|---|---|
| Edit data-only registries (resize, formations, era, sound overrides, retexture config) | No | plain JSON (pack.json, haf_*.json) — editable directly or by a standalone verb |
| Validate a pack (bones/paths/GUIDs/schema) | No (mostly) | the pack validator core — pure logic + file checks |
| Convert a model file (GLB/glTF/OBJ/FBX) | No | glbconv.exe (already standalone) |
| Blender prep (rig/decimate/clip extract) | No | already headless (blender -b) |
Bake Skeleton / Atlas / ClipCollection / district FxMesh |
Yes | the Amplitude SDK is Unity-bound → Unity batch mode |
So: everything except baking the Amplitude assets can run with no Unity at all; baking runs headless in Unity batch mode. There is no way to bake Amplitude assets on a machine without Unity — that’s the one hard limit, stated plainly.
Two different targets hide behind this:
ConfigFor →
UniversalBaker and already called headless by the bake tests. The CLI just invokes it via batch mode. This is the
pragmatic path, full-fidelity, no RE..asset files with correct meta-GUIDs and packaging them
into the loadable Resources/bundle the game reads. That envelope is what the SDK importers do; replicating it
standalone is large, version-fragile, and duplicates working tooling for the sole benefit of removing a dependency the
editor already requires. Not recommended — the payoff (no Unity) is exactly the requirement we’ve accepted as fine.A single entry class HAF.Cli in the ENCReload editor assembly (where ConfigFor/UniversalBaker/the registries
live), invoked via:
Unity.exe -batchmode -quit -projectPath <ENCReload> -logFile - -executeMethod HAF.Cli.Run -- <request.json>
wrapped in a small haf shim (.bat / .sh) so callers don’t hand-write the Unity path. -batchmode -quit means no
GUI and a clean exit; -logFile - streams to stdout.
Fast path (optional): pure-data verbs (validate, list, simple registry edits) touch only JSON and need no Unity —
they can be a standalone .exe (like glbconv) to avoid Unity’s ~1-minute batch startup per call. Recommended split:
standalone for data/validate (instant), Unity batch mode for bake (necessarily slow). One requirement, two speeds.
Request in, structured result out — no interactive prompts (batch mode has no console input).
| Verb | Unity? | Does | Reuses |
|---|---|---|---|
list [--kind models\|districts\|formations\|sounds] |
no | dump current registry entries as JSON | the registries |
validate <pack> |
no | pre-flight content check → {warnings, errors} |
ValidateEntry (validator) |
bake <request.json> |
yes | bake one model/district/prop from a JSON bake request → produce assets + upsert registry | ConfigFor → UniversalBaker |
set-resize / set-formation / silence-sound … |
no | scripted data-only edits (thin wrappers over the registries) | ModelRegistry etc. |
bake request = the JSON form of a BakeConfig (model file, pawn, size, shading, animated/clip, strip, etc.) — the
same fields the Factory/Animation Lab collect. Mapping through ConfigFor (the single shared config path the GUI and the
tests already use) means the CLI can’t drift from the GUI’s behaviour.
{ ok, produced: [...asset paths], registry: "...", warnings: [], errors: [] }.0 ok, 2 validation failed, 3 bake failed, 4 bad request — so a caller can branch without parsing prose.A rebuilt model isn’t in the game until the mod is built (“referencing ≠ rendering — it needs a Build for the MeshCollection”). So the CLI needs both, and they differ sharply in difficulty:
rebuild-model — ready. Reuses the exact GUI/BakeSmokeTest path: ModelRegistry.Load() →
ModelFactoryWindow.ConfigFor(def) → UniversalBaker.Build / BuildAnimated → copy the BakeResult GUIDs back →
ModelRegistry.Upsert. A batch-mode -executeMethod HAF.Cli.RebuildModel is a direct implementation. (Honor the
def’s reuseExtracted/keepTexture so a rebuild doesn’t clobber hand-edited extracted textures; offer -fresh to
force a re-slim.)build-mod — needs discovery. This is not HAF’s code nor this project’s editor DLLs (those are inspectors).
Assets/Configurations/ModdingSettings.txt points at the Humankind Mod Tools (Steam app 1718880) — the mod
build is the modding SDK’s pipeline. Its programmatic/headless entry point is unknown: candidates are a build
method in Amplitude.Framework.Editor.dll (decompile + test), a Unity -executeMethod target the SDK exposes, or a
CLI the Mod Tools app itself provides. Open question for the author: how is the mod built today (which menu /
button / tool)? That answer targets this verb directly instead of guessing.HAF.Cli.Bake — one -executeMethod command that bakes a single existing registry entry headless
and prints the result JSON. Proves batch mode works in this project/Unity before building more. ~half a day..exe (list, validate, set-*) — no Unity, instant, pairs with the
validator.haf shim + a bake <request.json> that accepts arbitrary bake requests, plus docs + an example
request. This is the point an agent (or CI) can author → validate → bake end-to-end.bake request and the data verbs are another consumer of the registry schema (the
ModelDef↔ModelEntry duplication that was deliberately left un-refactored). Route everything through ConfigFor and
the registry classes — don’t hand-roll a third schema — and extend check_schema_parity.sh to cover the request shape.ConfigFor and the registries rather
than reimplementing.