G
YouTube TikTok Games
Book a callGitHub142

Reference02/08

Engine commands

Updated · Suggest a change

The engine is the part of OpenDesigner that does the math and writes your files: the tokens, the exports, DESIGN.md and the pages. Your AI usually runs it for you, so use this page when you want to run a command yourself or check what one does. It runs on your computer and makes no network calls.

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.

How to run it

Run commands from the root of your project, with Python 3.10 or newer. The engine keeps its state in ./opendesigner/.

python3 path/to/OpenDesigner/skills/opendesigner/scripts/engine.py build

The tables below write engine.py for short. Add --help to any command to list its options.

For developers

The engine is one Python file, skills/opendesigner/scripts/engine.py, and uses only the standard library. If you installed the plugin, the file sits in the plugin's own folder. Every command takes --dir to use a different state folder.

Start a system

CommandWhat it does
engine.py detectReads what your project folder already answers, such as the name, platforms, brand color, font and dark mode, before any question. It changes nothing.
engine.py init --name "Acme"Creates state.json with defaults and applies the house standards that fit.
engine.py sketchRecords 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 list shows all 133, and style mix, mutate, explore and define make new ones.
For developers

A full sketch:

engine.py sketch --name "Acme" --audience regular --platforms web --feel friendly,minimal

Its 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.
  • --theme: system-light-dark (light and dark, matching the device setting), light-dark-toggle (light and dark, plus a switch in the app), light-only or dark-only.
  • --brief (your own picture of the look), --stack (your code stack) and --logo (your logo, PNG or SVG) are also accepted. engine.py sketch --help explains each.

Record decisions

CommandWhat it does
engine.py set dials.roundness 45 --set-by delegated --why "softer corners"Records one decision with its reason and how it was set: chosen with --quote "<their words>", confirmed_default, delegated, assumed or reference. Fonts, the brand color, dials and answers must say which.
engine.py pick Q-shape-01 subtle --set-by chosen --quote "keep corners small"Answers one interview question by its id.
engine.py lock raw.brandColorLocks a value. engine.py unlock raw.brandColor undoes it.

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.
For developers

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, standard or research.
  • --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.
  • --dry-run: show what the value would change, old and new tokens and the checks, and record nothing.
  • --source-ref: where the value came from, such as a research file, saved with the decision.

Build and check

CommandWhat it does
engine.py buildGenerates the tokens and checks them. If there are no errors, it writes the exports for the platforms you chose, DESIGN.md, the overview page and the preview pages, and names any code file it left out. --force exports even with errors.
engine.py generateWrites the DTCG 2025.10 tokens to opendesigner/tokens/.
engine.py validateChecks contrast, target sizes and lint rules, and exits with an error code if anything fails. --json prints the results as JSON.
engine.py contrast "#767676" "#ffffff"Prints the contrast ratio of two colors, rounded down, and which WCAG minimums it meets. --min 4.5 exits with an error when the pair is under 4.5:1.
engine.py export --format tailwindWrites one export. --format all writes every one.
engine.py design-mdWrites DESIGN.md and PRODUCT.md at 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.

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, brand), 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.
For developers

The export formats are css, tailwind, figma, paper, swift, compose, dtcg, artifact, brand and all.

See the system

CommandWhat it does
engine.py overview --openWrites opendesigner/overview.html, your whole system on one page, and opens it.
engine.py preview --openWrites opendesigner/preview.html, with every token on one page.
engine.py show palette --openFills one visual template with your real values.
engine.py audit-page --openWrites opendesigner/audit.html: your system as your code builds it. Each token with how often the code uses it, then each place the code and the system disagree, with the file and line, a count, and up to three ways to settle it that you can preview.
engine.py adapt site.json --openWrites opendesigner/adapt.html: what a reference would change in your system and what stays yours, side by side. It records nothing.
For developers

The templates show fills are palette, type-scale, spacing-ruler, radius, elevation, motion, component-sheet and option-gallery. audit-page and adapt fill audit and adapt. Picks on the audit page come back as OD:fix A-space-01=down (change the code) or OD:waive A-color-02 --why "..." (keep it, with the reason); each becomes a ledger item. audit-page --json prints the page's data, every finding included.

Learn from a reference

engine.py adapt opendesigner/references/site.json --open shows the same fit as a page: your system now beside your system with the reference's changes, what changes, and what stays (anything you locked, your brand color and fonts). Tick what to take and copy it back. Nothing is recorded until then.

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.

Research your users

CommandWhat it does
engine.py research plan --brief "..."Writes the plan: who the users are, what to find out, searches to start from and the tools to look for.
engine.py research add findings.jsonChecks and saves the findings, writes research/users.md and lists what they recommend. research check checks a file without saving it.
engine.py research market, positioning, directionsShow the competitor table, draft and confirm the positioning, and write the board with three directions.
engine.py research skip --why "..."Records that you don't want research, and the reminders stop. research status says where it stands.
research.py coverageChecks the research against the full list: users and their tasks, the competitors' design systems, accessibility, platform conventions, content and voice, your analytics, and stakeholders. Says what is missing and which tool would fill it.
research.py stakeholders scan, interview, answer, show, decideSkips itself on a solo project (it records "solo: not needed"). Otherwise finds who has a say from your repo, drafts at most five questions for each role, records each answer with who said it, and shows where people agree and where they differ. Only an agreed point becomes a decision on its own; you settle a conflict in your own words.
research.py summaryWrites research/summary.html: the users, the market, what each stakeholder said and what is still open, on one page to share with your team.

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 also says when your code uses no tokens or more raw values than tokens, when the brand color or fonts in DESIGN.md appear nowhere in your code, and when the system is 10 or more commits behind the code. It also notices when a merge undid a locked decision or a token file was edited by hand. Its last line is Review: ok, or how many things are left to fix and why. It only reports: it never edits your code.

$ engine.py review --project srctest run
Review of src: 1 file scanned.
3 hard-coded values skip the tokens: 1 color, 1 radius, 1 size.
card.css:1 #f3f4f6 -> use var(--ds-color-surface-sunken)
card.css:1 border-radius: 7px -> use var(--ds-radius-container) (12px, the container radius)
card.css:1 padding: 13px -> use var(--ds-space-12) (13 is off the 4px ladder)
To keep a raw value on purpose, add a comment with the reason on that line: od-ignore: <why, at least two words>.
Tokens are not in charge: the code uses none of them (no var(--ds-...)).
1 line breaks a standard:
card.css:1 padding: 13px -> Write spacing in rem or em so the layout scales with the user's text size. (STD-accessibility-motion-13, must)
Fix the code. A house standard changes only when the person asks: engine.py standard override <id> --why "<their words>".
DESIGN.md matches state.json.
Review: 5 to fix: 4 findings; tokens are not in charge.
Most of the output of engine.py review on a one-line style sheet: three raw values with the token to use for each, code that uses no tokens yet, one line that breaks a house standard, a DESIGN.md still in step with state.json, and the last line, which counts what is left to fix. Once nothing is, it reads Review: ok. Photo: Eduardo Drapier on Unsplash.

With no system in the project, it still counts the hard-coded values and names any system it finds in a subfolder. It fails when AGENTS.md, CLAUDE.md or DESIGN.md point at an opendesigner/ folder that isn't there.

Two more commands keep later sessions on the same system:

CommandWhat it does
engine.py statusPrints where the work stands: the step you're on, what waits for you and the next command. It changes nothing. Run it first in a new session.
engine.py checkinA check-in: what changed since the last one, ledger items still open, whether DESIGN.md is out of date, how many things review finds, and the one next step. It also runs on its own at the start of each session in Claude Code, after every 5 decisions and when a ledger round closes. --every 10 changes the count, --off turns it off for the project.
engine.py handoff yesWrites a short note into your AGENTS.md, and a CLAUDE.md that imports it, so later AI sessions read the system first. handoff no writes nothing. Either answer is recorded, so you're asked once.
For developers

--project src scans one folder, or one file. When that folder has fewer than three style files, review also reads the page the site ships (the newest index.html, in public/ first) and the files it loads, and lists what it skipped. Findings come newest file first. docs/ and OpenDesigner's own exports are skipped. --strict exits with an error on any finding, tokens not in charge, a DESIGN.md that disagrees with the code or validate errors, and names each reason. engine.py guard install runs it before every commit, after you say yes; git commit --no-verify skips it once and guard remove takes it out. guard status says whether it is on.

status --json prints the same facts as JSON. status --hook is what the Claude Code plugin runs when a session starts: five short lines, and nothing in a project with no system.

What each round did. Before an AI builds something, it opens a round in the ledger and lists what the round covers. Each item ends as done, broken (with what broke) or skipped on purpose (with the reason and who decided). review lists every open item and review --strict fails on one, so nothing is quietly dropped, and DESIGN.md shows the ledger.

CommandWhat it does
engine.py ledger start "Settings page" --items comp.action.button,A-space-01Opens a round. An item can be a building block (it counts toward the system's coverage), something from the audit page, or your own name.
engine.py ledger done A-space-01Marks it done. ledger broke <item> --note "..." records what broke; ledger waive <item> --why "..." --by owner --quote "your words" skips it on purpose. An AI can waive with --by agent, but the round won't close until you agree.
engine.py ledger closeCloses the round, and refuses while anything is open. ledger show lists every round.
engine.py exempt site/figures --why "demo figures show mistakes on purpose" --by ownerKeeps files that break the system on purpose out of review. Review lists them, with the reason, so they are never hidden.

Things removed on purpose. Code keeps no record of what was taken out, so a later session tends to add it back. Record a removal the day it happens.

CommandWhat it does
engine.py removed add "divider lines between rows" --why "their words" --pattern "border-bottom"Records something the owner removed. DESIGN.md lists it under "Removed on Purpose", and review flags code that brings it back. A pattern that would catch a focus ring, label, alt text, reduced-motion code or a target size is refused.
engine.py removed listLists what was removed, and what was brought back.
engine.py removed restore R-01 --why "their words"Allows it again. Only the owner brings a removed thing back.

Standards

CommandWhat it does
engine.py standardsLists 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-IDFollows the house standard again.
engine.py standard add --rule "..." --why "..." --source "..."Adds a project standard.
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.

For developers

standard add also takes --path and --value, --review-pattern and --review-message, --authority, and --beats.

Feedback

CommandWhat it does
engine.py feedback "the spacing question was confusing" --kind confusingSaves 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.
engine.py feedback "buttons feel too loud" --kind noteRecords your own note on the work in opendesigner/RULES.md. A note you give twice becomes a rule later sessions follow. --same N-01 marks a repeat of an earlier note.

Good to know

  • The same state.json gives the same tokens.
  • engine.py version shows this copy's release and whether the system here was made with an older or newer one. It never checks online.
  • 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 more than 200 tests, which pass on the maintainer's computer. No CI runs are recorded yet.