Book a callGitHub1

Reference02/06

Engine commands

Updated · Edit on GitHub

The OpenDesigner engine is one Python file, skills/opendesigner/scripts/engine.py, 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 build

The 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.

Eight groups · what each one writes
Start a system
initsketchfeel
init writes state.json and applies the house standards. sketch also builds a complete first version. feel only prints.
Record decisions
setpicklockunlock
decisions.md and state.json, then a fresh DESIGN.md and PRODUCT.md (skip with --no-doc).
Build and check
buildgeneratevalidateexportdesign-mdresolve
tokens/, every export in build/, DESIGN.md, overview.html and the preview pages. validate and resolve only print.
See the system
overviewpreviewshow
overview.html, preview.html, and one filled template per show. Add --open to open it.
Learn from a reference
intake
Reads a measurement file such as opendesigner/references/site.json and proposes dial positions with a confidence. --accept records them.
Keep it consistent
review
Reports hard-coded colors, sizes, radii, shadows and durations in your code, and stale DESIGN.md sections. --strict fails on any find.
Standards
standardsstandard overriderestoreaddremove
Every change goes into the decision log. standards.md and standards-state.json list what the system follows.
Feedback
feedback
opendesigner/feedback.md and a ready-to-file GitHub issue link. Nothing is posted unless you submit it.
The engine's commands in eight groups, each with the files it writes. Build is the one to run after every change: it refreshes the tokens, every export and the pages together.

Start a system

  • engine.py init --name "Acme" creates state.json with defaults and applies the house standards that fit.
  • engine.py sketch --name "Acme" --audience regular --platforms web --feel friendly,minimal records the five sketch answers and builds a complete first version.
  • engine.py feel fun calm shows how the engine reads your feel words. With no words, it lists every word it knows.
  • engine.py style search frosty finds the named styles that match your words. style list shows all 133, and style mix, mutate, explore and define make new ones.

The sketch takes these options:

  • --audience: dense (all day), regular or large (now and then, on the go).
  • --platforms: a comma list of web, ios, android and desktop.
  • --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, --stack and --theme are also accepted: your own picture of the look, your code stack, and light, dark or both. engine.py sketch --help explains 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.brandColor locks a value, and engine.py unlock raw.brandColor unlocks it.

set and pick share these options:

  • --why "...": the reason, saved in the decision log.
  • --set-by: how it was set. One of chosen (the default), confirmed_default, auto_default, assumed, delegated, reference, asset or standard.
  • --lock: lock the value as you set it.
  • --force: change a locked value. Use it only with the owner's OK.
  • --no-doc: skip refreshing DESIGN.md and PRODUCT.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)
Both commands record the decision and change the same six radius tokens. Only the first tells the next session why, which is what stops a later change from quietly undoing it.

Build and check

  • engine.py build generates the tokens and checks them. If there are no errors, it writes every export, DESIGN.md, the overview page and the preview pages. --force exports even with errors.
  • engine.py generate writes the DTCG 2025.10 tokens to opendesigner/tokens/.
  • engine.py validate checks contrast, target sizes and lint rules, and exits with an error code if anything fails. --json prints the results as JSON.
  • engine.py export --format tailwind writes one export. The formats are css, tailwind, figma, paper, swift, compose, dtcg, artifact and all.
  • engine.py design-md writes DESIGN.md and PRODUCT.md at the project root.
  • engine.py resolve prints 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.

engine.py build0 errors1 warning5 notes
$ 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.
validate prints the same report and exits with an error code if anything fails. An error here would have blocked every export.
A real build report: what was written, then the verdict. Warnings and notes are advice with their sources in brackets; only an error stops the exports. Photo: Marcel Strauß on Unsplash.

See the system

  • engine.py overview --open writes opendesigner/overview.html, your whole system on one page, and opens it.
  • engine.py preview --open writes opendesigner/preview.html, with every token on one page.
  • engine.py show palette --open fills one visual template with your real values. The templates are palette, type-scale, spacing-ruler, radius, elevation, motion, component-sheet and option-gallery.

Learn from a reference

  • engine.py intake opendesigner/references/site.json fits measurements from a reference to proposed dial positions, each with a confidence. --accept accepts them all. The extract skill makes the measurement file. See Start from an existing app or brand.

Keep it consistent

  • engine.py review scans your code for hard-coded colors, sizes, radii, shadows and durations, and for out-of-date DESIGN.md sections. It only reports: it never edits your code. --project src scans one folder. --strict exits with an error when it finds anything.

Standards

  • engine.py standards lists the standards this system follows, the ones overridden and why, and any newer house standards. --update applies 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. --path releases one value instead of all of them.
  • engine.py standard restore STD-ID follows the house standard again.
  • engine.py standard add --rule "..." --why "..." --source "..." adds a project standard. Optional: --path and --value, --review-pattern and --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 confusing saves a note to opendesigner/feedback.md and prints a ready-to-file GitHub issue link. The kinds are gap, bug, confusing and idea. Nothing is posted unless you open the link and submit it.

Good to know

  • The same state.json gives the same tokens.
  • When set or pick changes no tokens, the answer is still saved, as a DESIGN.md rule or a PRODUCT.md fact. The engine says so.
  • The engine has 157 tests, which pass on the maintainer's computer. No CI runs are recorded yet.