Reference02/06
Engine commands
The OpenDesigner engine is one Python file, skills/, that does the math and writes the tokens, exports and pages. Your AI usually runs these commands for you, but you can run them yourself from your project folder. It needs Python 3.10 or newer, uses only the standard library, and makes no network calls.
How to run it
Run commands from the root of your project. The engine keeps its state in ./opendesigner/.
python3 path/to/OpenDesigner/skills/opendesigner/scripts/engine.py buildThe commands below write engine.py for short. If you installed the plugin, the file sits in the plugin's own folder. Every command takes --dir to use a different state folder, and --help to list its options.
Start a system
engine.py init --name "Acme"createsstate.jsonwith defaults and applies the house standards that fit.engine.py sketch --name "Acme" --audience regular --platforms web --feel friendly,minimalrecords the five sketch answers and builds a complete first version.engine.py feel fun calmshows how the engine reads your feel words. With no words, it lists every word it knows.engine.py style search frostyfinds the named styles that match your words.style listshows all 133, andstyle mix,mutate,exploreanddefinemake new ones.
The sketch takes these options:
--audience:dense(all day),regularorlarge(now and then, on the go).--platforms: a comma list ofweb,ios,androidanddesktop.--feel: your feel words, in your own words.--brand: your brand color as a hex value, such as"#167874".--surfaces: the main surface first, such as"app:operate,landing page:persuade".--delegated: marks answers as handed over when you said "just pick", for example--delegated feel,brand.--brief,--stackand--themeare also accepted: your own picture of the look, your code stack, and light, dark or both.engine.py sketch --helpexplains each.
Record decisions
engine.py set dials.roundness 45 --why "softer corners"records one decision with its reason.engine.py pick Q-shape-01 subtle --why "dense work tool"answers one interview question by its id.engine.py lock raw.brandColorlocks a value, andengine.py unlock raw.brandColorunlocks it.
set and pick share these options:
--why "...": the reason, saved in the decision log.--set-by: how it was set. One ofchosen(the default),confirmed_default,auto_default,assumed,delegated,reference,assetorstandard.--lock: lock the value as you set it.--force: change a locked value. Use it only with the owner's OK.--no-doc: skip refreshingDESIGN.mdandPRODUCT.md.
Say why
The reason goes into the decision log, so a later session knows what the value protects before it changes it.
$ engine.py set dials.roundness 60 --why "softer corners for a friendlier feel" D-0009 set dials.roundness = 60 (chosen) tokens: 6 values change (radius.container, radius.control, radius.control-sm, ...)opendesigner/decisions.md
## D-0009 · dials.roundness = 60 - set_by: chosen · locked: no · date: 2026-10-14 · supersedes: D-0008 · source_ref: none - reason: softer corners for a friendlier feel - previous value: 45
Leave the reason out
The value is saved, but the log can't say what it was for. The next change has nothing to weigh it against.
$ engine.py set dials.roundness 45 D-0008 set dials.roundness = 45 (chosen) tokens: 6 values change (radius.container, radius.control, radius.control-sm, ...)opendesigner/decisions.md
## D-0008 · dials.roundness = 45 - set_by: chosen · locked: no · date: 2026-10-14 · supersedes: none · source_ref: none - reason: (no reason given)
Build and check
engine.py buildgenerates the tokens and checks them. If there are no errors, it writes every export,DESIGN.md, the overview page and the preview pages.--forceexports even with errors.engine.py generatewrites the DTCG 2025.10 tokens toopendesigner/.tokens/ engine.py validatechecks contrast, target sizes and lint rules, and exits with an error code if anything fails.--jsonprints the results as JSON.engine.py export --format tailwindwrites one export. The formats arecss,tailwind,figma,paper,swift,compose,dtcg,artifactandall.engine.py design-mdwritesDESIGN.mdandPRODUCT.mdat the project root.engine.py resolveprints the current dial positions and the values made from them, as JSON.
Run build after every change, so the exports and pages are never out of date.
$ python3 path/to/ OpenDesigner/ skills/ opendesigner/ scripts/ engine.py build built opendesigner: tokens, build/ (css, tailwind, figma, paper, swift, compose, dtcg, artifact), DESIGN.md, PRODUCT.md, preview.html, overview.html Passes: no errors. 1 warning, 5 notes. (opendesigner) WARN No brand color yet, so the accent uses a stand-in blue [Brand color is a raw input; DC-L06-06] NOTE The smallest control is 24px tall. Give it a 44px tap area with padding (size.target.min), never a smaller one [L14 I-1; DC-L03-12, S-L03-029] NOTE 10 scale steps are not used by any named value yet (for example motion.duration.extra, motion.duration.long-exit, radius.0). That is fine if you plan to use them [Orphan tokens; spec 6.3] NOTE … 3 more notes Checked 164 color pairs in every mode, 12 mode combinations and 419 tokens.
See the system
engine.py overview --openwritesopendesigner/, your whole system on one page, and opens it.overview.html engine.py preview --openwritesopendesigner/, with every token on one page.preview.html engine.py show palette --openfills one visual template with your real values. The templates arepalette,type-scale,spacing-ruler,radius,elevation,motion,component-sheetandoption-gallery.
Learn from a reference
engine.py intake opendesigner/fits measurements from a reference to proposed dial positions, each with a confidence.references/ site.json --acceptaccepts them all. The extract skill makes the measurement file. See Start from an existing app or brand.
Keep it consistent
engine.py reviewscans your code for hard-coded colors, sizes, radii, shadows and durations, and for out-of-dateDESIGN.mdsections. It only reports: it never edits your code.--project srcscans one folder.--strictexits with an error when it finds anything.
Standards
engine.py standardslists the standards this system follows, the ones overridden and why, and any newer house standards.--updateapplies the newer ones and keeps your overrides.engine.py standard override STD-ID --why "their words"changes a house standard, only when the owner asks.--pathreleases one value instead of all of them.engine.py standard restore STD-IDfollows the house standard again.engine.py standard add --rule "..." --why "..." --source "..."adds a project standard. Optional:--pathand--value,--review-patternand--review-message,--authority, and--beats.engine.py standard remove PRJ-01 --why "..."removes a project standard.
Every standard change is recorded in the decision log. See For enterprise design-system teams.
Feedback
engine.py feedback "the spacing question was confusing" --kind confusingsaves a note toopendesigner/and prints a ready-to-file GitHub issue link. The kinds arefeedback.md gap,bug,confusingandidea. Nothing is posted unless you open the link and submit it.
Good to know
- The same
state.jsongives the same tokens. - When
setorpickchanges no tokens, the answer is still saved, as aDESIGN.mdrule or aPRODUCT.mdfact. The engine says so. - The engine has 157 tests, which pass on the maintainer's computer. No CI runs are recorded yet.