Releases
Version history for both Specs packages. Each release follows Semantic Versioning.
[0.18.0] - 2026-04-24
Replaces Figma-native layout property names with semantic equivalents for auto-layout alignment and wrapping. Introduces a bi-axial itemSpacing model that consolidates counterAxisSpacing into a single property, and narrows layoutMode from a generic Style to a strict enum. Five renamed alignment/wrap fields use plain-language names (mainAxisAlignment, crossAxisAlignment, wrapAlignment, wrap) instead of Figma axis terminology. The documentation site was considerably rearchitected with updated content and navigation.
Added
Styles.itemSpacing— widened fromStyletoStyle | ItemSpacing(scalar when uniform,ItemSpacingobject withhorizontal/verticalwhen axes differ)Styles.wrap— boolean toggle for auto-layout wrapping (replaceslayoutWrap)Styles.wrapAlignment— typed asWrapAlignment('START' | 'SPACE_BETWEEN') ornull; structural property, not token-bindable (replacescounterAxisAlignContent)Styles.mainAxisAlignment— typed asMainAxisAlignment('START' | 'END' | 'CENTER' | 'SPACE_BETWEEN') ornull; structural property, not token-bindable (replacesprimaryAxisAlignItems)Styles.crossAxisAlignment— typed asCrossAxisAlignment('START' | 'END' | 'CENTER' | 'STRETCH' | 'BASELINE') ornull; structural property, not token-bindable (replacescounterAxisAlignItems)
Changed
Styles.layoutMode— narrowed fromStyletoLayoutMode('NONE' | 'HORIZONTAL' | 'VERTICAL') ornull; enum constraint, no TokenReference
Removed
Styles.counterAxisSpacing— consolidated intoStyles.itemSpacingbi-axial modelStyles.layoutWrap— replaced byStyles.wrapStyles.counterAxisAlignContent— replaced byStyles.wrapAlignmentStyles.primaryAxisAlignItems— replaced byStyles.mainAxisAlignmentStyles.counterAxisAlignItems— replaced byStyles.crossAxisAlignment
Migration
Styles.counterAxisSpacing→Styles.itemSpacing.vertical: read theverticalsub-field of theItemSpacingobject when axes differ; scalaritemSpacingapplies uniformlyStyles.layoutMode→LayoutMode | null: replace genericStylehandling with exhaustive match on'NONE' | 'HORIZONTAL' | 'VERTICAL'Styles.layoutWrap→Styles.wrap: rename only; same boolean semanticsStyles.counterAxisAlignContent→Styles.wrapAlignment: rename and remap values —'AUTO'→'START','SPACE_BETWEEN'→'SPACE_BETWEEN'Styles.primaryAxisAlignItems→Styles.mainAxisAlignment: rename and narrow type — replace genericStylehandling with exhaustive match on'START' | 'END' | 'CENTER' | 'SPACE_BETWEEN'; remap Figma values'MIN'→'START','MAX'→'END'Styles.counterAxisAlignItems→Styles.crossAxisAlignment: rename and narrow type — replace genericStylehandling with exhaustive match on'START' | 'END' | 'CENTER' | 'STRETCH' | 'BASELINE'; remap Figma values'MIN'→'START','MAX'→'END'
[0.17.0] - 2026-04-13
Introduces ResolvedConfig as the fully-resolved counterpart to Config, where every property with a default is guaranteed present. Config properties with defaults are now optional, making input configs more tolerant of omissions. Removes the unused Variant.name and Variant.baseline fields.
Added
ResolvedConfig— fully-resolved config type where every property with a default is required; only true feature toggles (subcomponents,glyphNamePattern,codeOnlyPropsPattern) remain optional
Changed
Config— all properties with defaults are now optional (input type tolerant of omissions):processing.variantDepth— optional; defaults to 9999processing.details— optional; defaults to LAYEREDformat.output— optional; defaults to JSONformat.keys— optional; defaults to SAFEformat.layout— optional; defaults to LAYOUT
ResolvedConfig— all properties with defaults are required (post-merge guarantee):processing.slotConstraints— required (defaultfalse)processing.inferNumberProps— required (defaultfalse)format.tokens— required (defaultTOKEN)include.invalidVariants— required (defaultfalse)include.invalidCombinations— required (defaulttrue)include.emptyVariants— required (defaultfalse)subcomponents.scope— required when subcomponents is present (defaultNESTED)
DEFAULT_CONFIG— typed asResolvedConfig; now explicitly includesslotConstraints: false,inferNumberProps: false; removedsubcomponents(feature toggle — off by default, matching the Figma plugin)
Removed
Variant.name— unused optional label; variant identity is fully described byconfigurationVariant.baseline— never populated in output; baseline determination is an internal processing concern
Migration
Variant.name→ removed: delete any references; useVariant.configurationto identify variantsVariant.baseline→ removed: delete any references; no replacement needed (field was never populated)
[0.16.0] - 2026-04-05
Adds Config.include.emptyVariants for controlling inclusion of layered variants with no elements. Makes invalidVariants and invalidCombinations optional with sensible defaults, and removes the unused variantNames field.
Added
Config.include.emptyVariants— optional boolean to control inclusion of layered variants that contain no elements; defaults to false
Changed
Config.include.invalidVariants— now optional; defaults to false when absentConfig.include.invalidCombinations— now optional; defaults to true when absent
Removed
Config.include.variantNames— unused field removed from the API
Migration
Config.include.variantNames→ removed: delete any references to this field from config construction code; regenerate cached specs to remove the field from serialized output
[0.15.0] - 2026-03-25
Adds structured subcomponent discovery via a new Config.processing.subcomponents object with scope, match, and exclude settings, replacing the flat subcomponentNamePattern string. Introduces SubcomponentRef for referencing subcomponent definitions and widens instanceOf on both AnatomyElement and Element to accept it. Corrects Typography field types for leadingTrim, fontFamily, and fontStyle to match actual Figma API values.
Added
SubcomponentRef— reference to a subcomponent definition via{ $ref: "#/subcomponents/{key}" }AnatomyElement.instanceOf— widened to acceptstring | SubcomponentRefElement.instanceOf— widened to acceptstring | PropBinding | SubcomponentRefConfig.processing.subcomponents— optional nested object grouping subcomponent discovery settings:scope,match, andexclude. Absence means no subcomponent detectionConfig.processing.subcomponents.scope— optional enum (NESTED|PAGE) controlling where the transformer searches for subcomponentsConfig.processing.subcomponents.match— required array of{C}/{S}template patterns defining which assets are subcomponentsConfig.processing.subcomponents.exclude— optional array of{C}/{S}template patterns defining which matched assets to exclude
Changed
Typography.leadingTrim— corrected fromnumber | 'mixed' | TokenReferenceto'NONE' | 'CAP_HEIGHT' | 'mixed'per Figma APITypography.fontFamily— corrected fromstring | number | 'mixed'tostring | 'mixed' | TokenReference; removed impossiblenumber, addedTokenReferencefor variable-bound fontsTypography.fontStyle— corrected fromstring | number | 'mixed'tostring | 'mixed' | TokenReference; removed impossiblenumber, addedTokenReferencefor variable-bound fonts
Removed
Config.processing.subcomponentNamePattern— replaced byConfig.processing.subcomponents.matchConfig.include.subcomponents— subcomponent inclusion is now implied by the presence ofmatchpatterns
Migration
Config.processing.subcomponentNamePattern→Config.processing.subcomponents.match: wrap the single pattern string in a one-element arrayConfig.include.subcomponents→ removed: remove the field; subcomponents are included whenprocessing.subcomponentsis defined withmatchpatterns. Omitprocessing.subcomponentsentirely to disable detection
[0.14.0] - 2026-03-18
Introduces code-only props support with FigmaCodeOnlySource provenance metadata and a configurable container-layer naming pattern, plus a new NumberProp type for numeric property values. Adds DTCG-aligned $extensions for platform-specific metadata across all prop types, replacing the prior x-platform convention on BooleanProp. Expands SlotProp with minItems, maxItems, and anyOf constraints consolidatable via the new slotConstraints processing flag.
Added
FigmaCodeOnlySource— provenance metadata for props extracted from a Figma code-only container layer (kind,layer,instanceOf?)FigmaPropExtension.source— optionalFigmaCodeOnlySourceon the Figma extension, present only for code-only propsConfig.processing.codeOnlyPropsPattern— optional naming pattern for detecting the code-only props container layerConfig.processing.inferNumberProps— opt-in flag to inferNumberPropfrom TEXT code-only propsNumberProp— numeric property type for number-valued component propsAnyProp—NumberPropadded as a fifth union member (type: 'number')FigmaPropExtension— Figma-specific metadata for prop definitions (native prop type)PropExtensions— DTCG §5.2.3 platform-specific extensions container, keyed by reverse-domain notationBooleanProp.$extensions— optional platform metadata on boolean propsStringProp.$extensions— optional platform metadata on string propsEnumProp.$extensions— optional platform metadata on enum propsSlotProp.$extensions— optional platform metadata on slot propsSlotProp.minItems— minimum number of items the slot acceptsSlotProp.maxItems— maximum number of items the slot acceptsSlotProp.anyOf— component type names permitted in the slotConfig.processing.slotConstraints— opt-in flag to consolidate slot constraint code-only props into slot properties
Changed
BooleanProp—x-platformreplaced by$extensionsto align with DTCG convention used onTokenReference
[0.13.0] - 2026-03-13
Added
Metadata.schema.latest— optional stable URL pointing to the latest schema on main for discoveryMetadata.generator.license— optional resolved license state:statusandlevelnested inside generatorStyles.fillColor— glyph fill color for GLYPH element typeStringProp.examples— sample values demonstrating typical content for string propsElement.content— unified content for text strings and glyph namesSlotProp.nullable— optional flag indicating the slot prop accepts nullConditional— conditional binding withif/condition/then/elsefor derived valuesConditionExpression— declarative condition pairing anoperation(string) withargsConditionArgs— condition arguments:value(PropBinding) and optionalcompareTo
Changed
SlotProp.default— now optional; omitted when no meaningful default existsColorStyleValue— accepts bare hex strings (e.g.#666E74) matching the existingColorStyletypeBooleanProp,StringProp,EnumProp,SlotProp— allow$-prefixed metadata fields viapatternPropertiesMetadata.schema.url— clarified as versioned schema URL pinned to a git tagElementType—'icon'renamed to'glyph'to distinguish raw visual assets from composed Icon componentsConfig.processing.iconNamePattern→Config.processing.glyphNamePattern— glyph detection patternStringProp.default— now optional; useexamplesfor demo contentStringProp.default— widened tostring | nullfor nullable propsSlotProp.default— widened tostring | nullfor nullable slot propsBindingKey—'text'replaced by'content'
Removed
TextProp— merged intoStringPropIconProp— merged intoStringPropElement.text— useElement.contentinstead
Migration
TextProp/IconProp→StringProp: replace all type imports and references withStringProp; the shape is identicalElement.text→Element.content: read element content fromcontentinstead oftext; applies to both text strings and glyph namesElementType'icon'→'glyph': update all references to the'icon'literal in element type checksConfig.processing.iconNamePattern→Config.processing.glyphNamePattern: update config objects and any code referencing this field
[0.12.0] - 2026-03-05
Added
ElementTypeRef— reference to an external element type definition via$refURIConfig.processing.iconNamePattern— optional naming pattern for detecting icon content assetsSides— per-side composite object with logical directions:top,end,bottom,startCorners— per-corner composite object with logical directions:topStart,topEnd,bottomEnd,bottomStartStyles.padding— scalar when uniform,Sidesobject when per-side values differ
Changed
AnatomyElement.type— widened fromstringtostring | ElementTypeRefStyles.strokeWeight— acceptsStyle | Sidesinstead ofStyle; scalar when uniform,Sidesobject when per-side values differStyles.cornerRadius— acceptsStyle | Cornersinstead ofStyle; scalar when uniform,Cornersobject when per-corner values differ
Removed
Styles.paddingLeft— useStyles.paddingwithSides.startinsteadStyles.paddingRight— useStyles.paddingwithSides.endinsteadStyles.paddingTop— useStyles.paddingwithSides.topinsteadStyles.paddingBottom— useStyles.paddingwithSides.bottominsteadStyles.strokeTopWeight— useStyles.strokeWeightwithSides.topinsteadStyles.strokeBottomWeight— useStyles.strokeWeightwithSides.bottominsteadStyles.strokeLeftWeight— useStyles.strokeWeightwithSides.startinsteadStyles.strokeRightWeight— useStyles.strokeWeightwithSides.endinsteadStyles.topLeftRadius— useStyles.cornerRadiuswithCorners.topStartinsteadStyles.topRightRadius— useStyles.cornerRadiuswithCorners.topEndinsteadStyles.bottomLeftRadius— useStyles.cornerRadiuswithCorners.bottomStartinsteadStyles.bottomRightRadius— useStyles.cornerRadiuswithCorners.bottomEndinstead
Migration
Styles.paddingLeft/paddingRight/paddingTop/paddingBottom→Styles.padding: when all sides are equal, readpaddingas a number; when sides differ, readpaddingas aSidesobject withtop,end,bottom,startusing logical directions (start= left in LTR)Styles.strokeTopWeight/strokeBottomWeight/strokeLeftWeight/strokeRightWeight→Styles.strokeWeight: same scalar-or-Sidespattern; individual per-side stroke weights now live underSides.top,Sides.end,Sides.bottom,Sides.startStyles.topLeftRadius/topRightRadius/bottomLeftRadius/bottomRightRadius→Styles.cornerRadius: same scalar-or-Cornerspattern; per-corner values useCorners.topStart,Corners.topEnd,Corners.bottomEnd,Corners.bottomStart
[0.11.0] - 2026-03-04
Added
PropBinding— component-prop binding type; discriminated by$bindingkey; replacesReferenceValueTokenReference— unified DTCG-aligned token reference; replacesVariableStyleandFigmaStyle;$tokenand$typeare the complete platform-facing APITokenReference.$token— DTCG dot-separated token path, usable directly as a DTCG aliasTokenReference.$type— DTCG token type discriminator:color,dimension,string,number,boolean,shadow,gradient,typography,effectsTokenReference.$extensions["com.figma"]— optional Figma extraction metadata:id,name,collectionName,rawValueStyles.typography— composite typography; value isTokenReference,Typography, or omittedTypography— 13 optional inline text properties:fontSize,fontFamily,fontStyle,lineHeight,letterSpacing,textCase,textDecoration,paragraphIndent,paragraphSpacing,leadingTrim,listSpacing,hangingPunctuation,hangingListStyles.aspectRatio— aspect ratio constraint;AspectRatioValuepair ornullwhen unconstrainedAspectRatioValue— requiredx(numerator) andy(denominator) number fieldsAspectRatioStyle— type aliasAspectRatioValue | nullConfig.format.tokens— token reference serialization profile; five options:TOKEN(default,$token+$type),TOKEN_NAME(dot-delimited path string only),TOKEN_FIGMA_EXTENSIONS($token+$type+$extensions["com.figma"]),FIGMA_NAME(slash-delimited path string only),CUSTOM(same asTOKEN, signals custom post-processing)Metadata.license?— optional{ status: string; description: string }fieldstyles.textColor— style key for text colourstyles.cornerSmoothing— style key for corner smoothing (Figma squircle factor)styles.effects— style key replacingeffectStyleId;TokenReferenceor inlineEffectsShadow— fields:visible,inset?,offsetX,offsetY,blur,spread,colorBlur— fields:visible,radiusEffects— fields:shadows?,layerBlur?,backgroundBlur?GradientStop— fields:position(0–1),colorGradientCenter— fields:x,y(0–1)LinearGradient— discriminated bytype: 'LINEAR'; fields:angle,stopsRadialGradient— discriminated bytype: 'RADIAL'; fields:center,stopsAngularGradient— discriminated bytype: 'ANGULAR'; fields:center,stopsGradientValue— discriminated unionLinearGradient | RadialGradient | AngularGradient
Changed
Element.instanceOf— acceptsstring | PropBinding; bound form is{ $binding: "#/props/..." }Element.text— acceptsstring | PropBinding; bound form is{ $binding: "#/props/..." }Style—PropBindingreplacesReferenceValue;TokenReferencereplacesVariableStyleandFigmaStyleConfig.format—variables,simplifyVariables, andsimplifyStylesreplaced by singletokensfield; token output shape is now controlled entirely by thetokensprofileStyles.backgroundColor— narrowed toColorStyle; inline gradients representableStyles.textColor— narrowed toColorStyle; inline gradients representableStyles.strokes— narrowed toColorStyle; inline gradients representablestyles.fillsrenamed tostyles.backgroundColor
Removed
Config.format.variables— removed; useConfig.format.tokensinsteadConfig.format.simplifyVariables— removed; useConfig.format.tokensprofile insteadConfig.format.simplifyStyles— removed; useConfig.format.tokensprofile insteadReferenceValue— removed; usePropBindinginsteadisReferenceValue— removed; no replacement; prop-binding guards are upstream consumer responsibilityVariableStyle— removed; useTokenReferenceinsteadFigmaStyle— removed; useTokenReferenceinsteadStyles.fontSize— removed; usetypography.fontSizeinsteadStyles.fontFamily— removed; usetypography.fontFamilyinsteadStyles.fontStyle— removed; usetypography.fontStyleinsteadStyles.lineHeight— removed; usetypography.lineHeightinsteadStyles.letterSpacing— removed; usetypography.letterSpacinginsteadStyles.textCase— removed; usetypography.textCaseinsteadStyles.textDecoration— removed; usetypography.textDecorationinsteadStyles.paragraphIndent— removed; usetypography.paragraphIndentinsteadStyles.paragraphSpacing— removed; usetypography.paragraphSpacinginsteadStyles.leadingTrim— removed; usetypography.leadingTriminsteadStyles.listSpacing— removed; usetypography.listSpacinginsteadStyles.hangingPunctuation— removed; usetypography.hangingPunctuationinsteadStyles.hangingList— removed; usetypography.hangingListinsteadStyles.textStyleId— removed; usetypographywith aTokenReferenceinsteadstyles.effectStyleId— removed with no deprecation period; consumers must migrate tostyles.effects
Migration
config.format.variables/simplifyVariables/simplifyStyles→config.format.tokens: settokensto a profile —TOKEN(default) for$token+$type;TOKEN_NAMEfor dot-delimited string;TOKEN_FIGMA_EXTENSIONSfor full object with Figma metadata;FIGMA_NAMEfor slash-delimited string;CUSTOMfor custom post-processingElement.instanceOf→Element.instanceOf: replace{ $ref: "#/props/..." }with{ $binding: "#/props/..." }; update anyisReferenceValueguards to narrow against$bindingdirectlyElement.text→Element.text: same key rename from$refto$bindingStyles.visible→Styles.visible: same key rename from$refto$bindingVariableStyle→TokenReference: replace{ id, variableName, collectionName }with{ $token: "<Collection>.<name>", $type: "<type>", $extensions: { "com.figma": { id, name, collectionName } } }; read token path from$tokenand token category from$typeFigmaStyle→TokenReference: replace{ id, name }with{ $token: "<dot.path>", $type: "typography" | "effects" | ... }and move the Figma UUID to$extensions["com.figma"].idStyles.<typographyProperty>→Styles.typography.<property>: all 14 flat typography properties replaced with single compositetypographyfield; whentypographyis aTokenReference, read$tokenfor the style path; whentypographyis aTypographyobject, read individual fieldsfills→backgroundColor: any consumer readingcomponent.styles.fillsmust update tocomponent.styles.backgroundColoreffectStyleId→effects: any consumer readingstyles.effectStyleIdmust update tostyles.effects; wheneffectsis aTokenReference, read$tokenand$type; wheneffectsis anEffects, read fromshadows,layerBlur, orbackgroundBlurby role
[0.9.0] - 2026-02-12
Added
- DEFAULT_CONFIG - Default configuration constant for transformer setup
- Provides sensible defaults for all Config fields
- Ensures consistent behavior across CLI, MCP, and Plugin environments
- Co-located with Config type definition for easy maintenance
[0.10.0] - 2026-02-16
Added
- Runtime JS build - Compile TypeScript types to
dist/index.jsfor ESM consumers- Enables ESM runtime imports without loading
.tsfiles - Keeps type definitions in
types/for TypeScript tooling
- Enables ESM runtime imports without loading
[0.8.0] - 2026-02-10
Added
- TypeScript type definitions (
types/package): Complete type definitions for all schema entities- Core types: Component, Anatomy, Props, Variant, Metadata, Config, Styles, Element, Layout
- Property types with proper discriminators: BooleanProp, TextProp, IconProp, EnumProp, SlotProp
- Style types supporting all 59 properties with specific value types
- Reference value types for prop bindings and style references
Changed
- Component.metadata - Now optional to support components without full metadata
- Variant.layout - Changed from single LayoutNode to array of LayoutNode for proper hierarchical representation
- Props type values - TextProp, IconProp, and EnumProp now use
type: 'string'with discriminators (matching schema) - Style properties - Changed from generic
Record<string, Style>to specific Partial interface with all 59 style properties - VariableStyle - Now includes all properties from schema: id (required), rawValue, name, variableName, collectionName, collectionId (all optional)
- FigmaStyle - Simplified to match schema: id (required), name (optional)
Fixed
- SlotProp.default - Now accepts both
nullandstringtypes - TextProp/IconProp nullable - Made nullable field optional (was incorrectly required)
- Metadata.source - Added missing nodeType field with enum: COMPONENT | COMPONENT_SET | FRAME
- Metadata.config - Added complete Config definition with processing, format, and include options
- StyleKey type - Expanded from incomplete list to all 59 valid style properties matching schema
[0.7.0]
Changed
- Added distinct
anatomytypes for line, ellipse, star, polygon and rectange, all which were previously vector - Removed autodetecion of icon assets based on
isAssetplugin since this function is unavailable in the REST API
[0.6.0] - 2026-01-27
Added
-
Styles schema: New
styles.schema.jsonproviding complete validation for style properties- Defines 60+ style properties (fills, opacity, cornerRadius, fontSize, etc.)
- Type-specific validation for each property based on Style processor classes
- Supports all style value types: primitives, variables, Figma styles, and prop bindings
- Documents which value shapes depend on config.format settings (simplifyVariables, simplifyStyles)
-
Style value types: Precise type definitions for style property values
NumberStyleValue: number | VariableStyle | nullBooleanStyleValue: boolean | VariableStyle | nullBooleanBindableStyleValue: boolean | VariableStyle | ReferenceValue | null (forvisibleonly)ColorStyleValue: string | VariableStyle | FigmaStyle | nullStringStyleValue: string | VariableStyle | nullMixedNumberStyleValue: number | “mixed” | VariableStyle | nullMixedStringStyleValue: string | VariableStyle | nullStrokeStyleValue: number | “mixed” | VariableStyle | nullCornerStyleValue: number | “mixed” | VariableStyle | nullFontStyleValue: string | number | “mixed” | nullLineHeightStyleValue: string | number | VariableStyle | nullStyleIdValue: string | FigmaStyle | null
-
Styles reference documentation: New
reference/styles.yamlproviding human-readable documentation- Maps style properties to their applicable element types (COMPONENT, FRAME, TEXT, etc.)
- Documents style processing categories from RAW_STYLES_LOOKUP
- Lists all 60+ style properties with descriptions and value types
- Generated from StyleKeys.ts and RawStyles.ts constants
Changed
-
VariableStyle definition: Removed internal-only
rawValueproperty- Only includes properties emitted by Style.data(): id, name, variableName, collectionName, collectionId
rawValueis used internally for rendering but never serialized
-
FigmaStyle definition: Improved descriptions
- Documents simplified vs full object output based on config.format.simplifyStyles
-
Styles property: Now references
styles.schema.json#/definitions/Styles- Replaces generic
additionalProperties: Stylewith precise property-level validation - Each style property validates against its specific value type union
- Replaces generic
[0.5.0] - 2025-12-29
Added
-
Instance of attribute: Added optional
instanceOfproperty to element definitions- Indicates the component or component set name that an instance element references
- Only present for instance elements (Figma INSTANCE nodes)
- Shows ComponentSet name for variant instances, or Component name for standalone instances
- Also added to anatomy element definitions
-
Text property: Added optional
textproperty to element definitions- Contains the text content for text elements
- Can be a string value or a
ReferenceValuewhen bound to a text prop
-
Unified property bindings: Property bindings now appear as
$refvalues on their natural propertieschildrencan now be aReferenceValue(for slot content bindings)instanceOfcan now be aReferenceValue(for instance swap bindings)visiblestyle can now be aReferenceValue(for boolean prop bindings)textcan be aReferenceValue(for text prop bindings)- Added
ReferenceValuedefinition with$refJSON pointer to props
Removed
- propBindings section: Removed separate
propBindingsproperty from elements- Bindings are now represented directly on their target properties using
$ref
- Bindings are now represented directly on their target properties using
[0.4.0] - 2025-12-27
Added
- Layout structure: Added optional
layoutproperty to variant definitions that provides hierarchical element structure as a nested tree- Layout represents element nesting relationships in a recursive format
- Leaf nodes are strings (element names)
- Parent nodes are objects with element name as key and children array as value
- Example:
{ "parentElement": ["childA", { "childB": ["grandchild"] }] }
Changed
- Schema now supports both structure representations:
- New:
layoutat variant level (hierarchical tree) - Existing:
parentandchildrenproperties in each element (maintained for backward compatibility)
- New:
Deprecated
includedproperty has been removed from element definitions- Element inclusion is now determined by presence in the
layouttree - Elements not present in
layoutare considered excluded from that variant
- Element inclusion is now determined by presence in the
[0.3.0] - Previous release
[0.12.1] - 2026-04-24
Dependency patch: picks up specs-from-figma 0.15.1 subcomponent indexer propagation fix.
Dependency updates
- @directededges/specs-from-figma 0.15.1 — Fixed subcomponent indexer propagation:
getMainComponentAsync()now correctly sets the indexer on parent COMPONENT_SET nodes, eliminating “[RestInstanceNode] No indexer set” warnings during subcomponent discovery.
[0.12.0] - 2026-04-24
Dependency update: picks up specs-schema 0.18.0 layout property renames and specs-from-figma 0.15.0 bi-axial spacing model.
Dependency updates
- @directededges/specs-schema 0.18.0 — Layout alignment properties renamed from Figma axis terminology to platform-neutral names:
primaryAxisAlignItems→mainAxisAlignment,counterAxisAlignItems→crossAxisAlignment,counterAxisAlignContent→wrapAlignment,layoutWrap→wrap.counterAxisSpacingconsolidated into a bi-axialitemSpacingmodel.layoutModenarrowed from generic style to strict'NONE' | 'HORIZONTAL' | 'VERTICAL'enum. - @directededges/specs-from-figma 0.15.0 — Wrap-enabled auto-layout frames now emit bi-axial
itemSpacingwithhorizontal/verticalfields instead of separateitemSpacing/counterAxisSpacingkeys. Alignment values remapped from Figma terminology (MIN→START,MAX→END).wrapAlignmentis only emitted whenwrap: true, stripped as dead otherwise.
[0.11.0] - 2026-04-16
Fix: config file split options (splitComponents, splitConcerns, useSubfolders) were ignored because Commander’s explicit false default shadowed the config values. Also makes generate and scan runnable with zero arguments using a default file path derived from config.
Added
generatenow runs without arguments. When no source argument is provided,generateresolves the manifest path to{dataDirectory}/{alias}.manifest.mdusing the source alias fromspecs.config.yaml(preferslibrary, otherwise the first source alias whosedataincludesfile). Runningspecs generatewith no flags now uses this default manifest plusoutputDirectoryfrom config — matching the zero-arg ergonomics ofscanandfetch. If no source alias hasdata: [file], an error suggests runningspecs scanfirst.scannow runs without arguments. The<file>positional argument is now optional. With one source configured inspecs.config.yaml(whosedataincludesfile),specs scanauto-resolves{dataDirectory}/{alias}.file.json. An explicit file path still works and takes precedence.scan --source <alias>flag. When two or more sources inspecs.config.yamlhavedata: [file, ...],scanfails loudly with the list of available aliases and requires--source <alias>to disambiguate. Passing both[file]and--sourceis rejected as a conflict. This stricter convention (vs.generate’s preference-based fallback) prevents silent behavior changes when a second source is added to config.
Fixed
- Config file split options now take effect. Commander boolean flags (
--split-components,--split-concerns,--use-subfolders) previously defaulted tofalse, which caused the nullish coalescing chain (options.flag ?? config.value ?? false) to short-circuit before consultingspecs.config.yaml. Removed the Commander defaults so the flags areundefinedwhen absent, allowing config values to flow through.
Docs
- Updated
docs/cli/(index, getting-started, claude-onboarding, examples, commands/generate, commands/scan, commands/index, commands/apply-custom-tokens) to document and demonstrate the zero-argspecs generateworkflow. Existingspecs generate components.mdexamples are preserved where they document explicit-argument option behavior.
Dependency updates
- No upstream package changes. Continues to target
@directededges/specs-schema ^0.17.0and@directededges/specs-from-figma ^0.14.2.
[0.10.2] - 2026-04-14
Dependency patch: picks up the DTCG-compliant token path separator fix from specs-from-figma.
Changed
- Bump
@directededges/specs-from-figmato ^0.14.2 — Picks up the fix for$tokenpath separators violating DTCG character restrictions.
Dependency updates
- @directededges/specs-from-figma 0.14.2 — Token references (
$tokenvalues) in TOKEN, TOKEN_NAME, TOKEN_FIGMA_EXTENSIONS, and CUSTOM profiles now use/as the path separator instead of.(period). The DTCG spec (§5.1.1) prohibits.in token and group names, and this aligns all profiles with the existing FIGMA_NAME behavior.
[0.10.1] - 2026-04-14
Dependency patch: picks up a fix from specs-from-figma for empty variant filtering in LAYERED mode.
Changed
- Bump
@directededges/specs-from-figmato ^0.14.1 — Picks up the fix for empty variant filtering in LAYERED mode, where variants with configuration but no element or layout differences were not being excluded whenemptyVariants: false.
Dependency updates
- @directededges/specs-from-figma 0.14.1 — Fixes the
emptyVariants: falsefilter in LAYERED mode. Previously, variants that had a configuration object but noelementsarray (e.g., variants with only layout differences removed) were incorrectly retained in output. The filter now correctly excludes variants that lack both element and layout differences from their layered baseline.
[0.10.0] - 2026-04-13
Renames audit → scan, sourceDirectory → dataDirectory, and the config file from .specs.config.yaml to specs.config.yaml. Adds a --data-dir flag and makes config format values case-insensitive. Config types now use ResolvedConfig for full default guarantees.
Added
--data-dirflag onfetch,scan, andgenerate— Override the configdataDirectoryper run from the command line. Onfetch,--outDiris retained as a deprecated alias.
Changed
- Config file renamed from
.specs.config.yamltospecs.config.yaml— The config file is no longer a hidden dotfile. Specs config is actively edited (Figma file keys, output directories, format options), so it belongs visible in the filesystem — consistent withvite.config.ts,tailwind.config.js, and other tools where config is a first-class part of the workflow. The JSON variant is also renamed from.specs.config.jsontospecs.config.json. The global config path (~/.specs/config.yaml) is unchanged since home-directory configs are conventionally hidden. auditcommand renamed toscan— Better describes the command’s purpose: scan a file and produce a manifest for component curation.specs auditstill works as a deprecated alias with a stderr warning.sourceDirectoryconfig field renamed todataDirectory— Aligns the field name with its default value (./data) and removes confusion with thesourcesconfig block.sourceDirectorystill works as a deprecated alias with a stderr warning.generatemanifest mode falls back toconfig.outputDirectory—--outputis no longer required whenoutputDirectoryis set in the config file, fixing the main flag/config inconsistency reported by users.- Config types now distinguish partial input from resolved output —
CLIConfig.configandConfigLoader.mergeConfiguseResolvedConfig(all defaults guaranteed) instead ofConfig(which now permits omitted fields) inittemplate annotates license-gated configs — The YAML config template generated byspecs initnow notes thatformat.tokensandinclude.invalidCombinationsrequire a license key to produce output.
Fixed
- Config format values are now case-insensitive — Lowercase values like
yaml,camel, orlayoutin config files were silently rejected by validation and reset to defaults (e.g.,output: yamlalways produced JSON). Allformat.*values are now normalized to uppercase before validation, matching how the CLI flag already works. - YAML options type annotation includes
CreateNodeOptions—FileWriter.YAML_OPTIONSwas typed withoutParseOptions & CreateNodeOptions, causing a compile error foraliasDuplicateObjects - Config merge test references legacy
subcomponentNamePattern— Updated to usesubcomponents(the current property shape) withtoEqualinstead oftoBe - Vitest mock type mismatch in
GenerateCommandtests — ReplacedReturnType<typeof vi.spyOn>withMockInstancefor vitest 3.x compatibility
Deprecated
sourceDirectoryconfig field — UsedataDirectoryinstead. Will be removed in a future release.specs auditcommand — Usespecs scaninstead. Will be removed in a future release.fetch --outDirflag — Use--data-dirinstead. Will be removed in a future release.
Dependency updates
- @directededges/specs-schema v0.17.0 — Introduces
ResolvedConfig(fully-resolved config with all defaults guaranteed) alongside the now-more-permissiveConfig(optional fields with defaults). Removes unusedVariant.nameandVariant.baselinefields from output. - @directededges/specs-from-figma v0.14.0 — Fixes
format.keysnot applying when config values are lowercase and not formattinginvalidVariantCombinationsdimension names. Internal types updated toResolvedConfig.
[0.9.0] - 2026-04-09
Adds complete config template generation, better Figma rate-limit error messages, and fixes for YAML-only-comments config crashes, output format not being respected, and stale command references.
Changed
initconfig template includes all settings — The YAML template generated byspecs initnow includes everyConfigfield andoutputsection option:codeOnlyPropsPattern,slotConstraints,inferNumberProps,emptyVariants,splitComponents,splitConcerns, anduseSubfolders. All new entries are commented out with their defaults. (#16)fetchrate-limit errors surface Figma response headers — When Figma returns HTTP 429, the error message now includes theRetry-Afterduration (formatted as seconds, minutes, hours, or days), seat tier (Viewer/CollaborDev/Full), plan tier, and a link to Figma’s rate limit documentation.
Fixed
- Config validation crashes on YAML sections with only comments — When
specs.config.yamlcontains sections likeinclude:with only commented-out fields, the YAML parser producesnullinstead of an empty object. The config validator’sdeepMergeandvalidateAndCorrectConfigmethods now guard against null values, preventingTypeError: Cannot convert undefined or null to objectcrashes. Null values from YAML parsing no longer overwrite defaults. (spec-demo) - File output ignores
config.format.output— Thegeneratecommand always wrote YAML files regardless of theformat.outputconfig setting. TheOutputFormattype,FileManifestextensions, and all writers now respect the configured format (JSON or YAML). TheDEFAULT_OUTPUT_CONFIG.defaultFormatis aligned with specs-schema’sDEFAULT_CONFIG.format.output(JSON). (#14) auditandinitterminal output references defunctbatchcommand — Post-run help text inauditandinittold users to runspecs batch …. Thebatchcommand was consolidated intogenerate; all references now point tospecs generate.audit --variablesflag not writing to manifest header — The-v/--variablesflag was parsed but never passed to the manifest generator. The**Variables:**metadata line is now written when the flag is provided, andManifestParserextracts it back into metadata. (#18)
Dependency updates
- @directededges/specs-from-figma v0.13.0 — Slot
anyOfvalues now respect the configured key format instead of using raw Figma component names. Width/height fallback warnings for rotated nodes are more informative when geometry data is absent.
[0.8.0] - 2026-04-07
Adds fetch UX improvements (animated spinner, elapsed time, --no-geometry), renames the config key from model to config, and fixes style reconciliation and large-file fetch crashes.
Added
fetchanimated spinner with elapsed time — Thefetchcommand now displays an animated braille spinner with a live elapsed-time counter while downloading each payload. The final success line includes the total duration per request (e.g.,✓ Downloaded: kds file (14s)). Non-TTY environments fall back to a static log line.fetch --no-geometryflag — Omits?geometry=pathsfrom Figma file requests, reducing payload size by roughly half. Vector path data (fillGeometry,strokeGeometry,size,relativeTransform) is excluded; width/height fall back toabsoluteBoundingBoxduring processing.auditdefault output path —-ois now optional. When omitted, the manifest writes to{sourceDirectory}/{alias}.manifest.mdusing the config’ssourceDirectoryand the input filename. Added--configflag for config file resolution.
Changed
- Config key rename:
model→config— The YAML keymodel:and internal propertyCLIConfig.modelare renamed toconfig:/CLIConfig.configto align with the upstreamConfigtype from @directededges/specs-schema. Updates source, tests, and all documentation.
Refactored
- Remove dead styles payload shapes from
loadFoundations— Removed two unused code paths: theall_stylessimplified format and thestylesobject-map format. Only the Figma REST API format (meta.styles) is retained. Updated JSDoc to describe the two actual data sources (file seed + styles endpoint). - Comment out false-defaulting include fields in config template —
invalidVariantsandinvalidCombinationsnow have schema-level defaults and no longer need explicit values in the generated config template.
Fixed
- Style
$customreconciliation inloadFoundations— Published Figma styles referenced by components use file-local IDs (e.g.,19108:2530) that differ from the styles endpoint’snode_id(e.g.,5115:6703). The sharedkeyhash now bridges these two sources, ensuring$customtoken objects from the styles endpoint are available when resolving style references during spec generation. generate -cuses component ID as output key — In file mode (generate <file> -c <id>), the component’s Figma node ID was used as the output key instead of its name. Now resolves the display name from the file’scomponentSetsorcomponentsmetadata.fetchwrites raw response directly to disk — Previously, fetch parsed the Figma response into JSON and re-serialized it with pretty-printing before writing. This causedInvalid string lengthcrashes on large files (400MB+). Now writes the raw response body straight to disk, eliminating unnecessary memory overhead.
Dependency updates
- @directededges/specs-from-figma v0.12.0 — When the REST API response lacks geometry data (e.g., when using
--no-geometry), width and height fall back toabsoluteBoundingBox. A console warning now surfaces when this fallback is used on a rotated node, where bounding box dimensions may be inflated.
[0.7.0] - 2026-04-05
Rebrands from anova-cli to specs-cli and publishes to npmjs.org. Updates all dependencies to published npm packages. Removes the deprecated variantNames config field per specs-schema v0.16.0.
Changed
- Package rename —
@directededges/anova-cli→@directededges/specs-cli - Config file names —
.anova.config.yaml→specs.config.yaml(and.jsonvariant) - Config search path —
~/.anova/config.yaml→~/.specs/config.yaml - Dependencies — switched from local
file:references to published npm packages:@directededges/specs-schema@^0.16.0,@directededges/specs-from-figma@^0.11.0 - Publishing target — npm registry (was GitHub Packages)
Removed
Config.include.variantNames— removed from config template, validation, and documentation per specs-schema v0.16.0 (ADR 034). AddedemptyVariantsto valid config include keys.
Dependency updates
- @directededges/specs-schema v0.16.0 — removes
variantNames, adds optionalemptyVariants, makesinvalidVariantsandinvalidCombinationsoptional with defaults - @directededges/specs-from-figma v0.11.0 — renames from
anova-transformer, adds pageId resolution, empty variant filtering, stroke align fix, license metadata in output
[0.6.0] - 2026-03-25
Adds a new applyCustomTokens CLI command for injecting custom token objects into fetched foundation data, enabling the CUSTOM token profile in the transformer. Implements anova v0.15.0 schema with restructured subcomponent configuration and corrected typography types, and anova-transformer v0.10.0 with expanded subcomponent discovery and custom token support.
Added
- License key support —
-l, --license <key>flag andANOVA_LICENSE_KEYenvironment variable for Pro features (token references, variable bindings, visibility bindings) - License status display — shows
License: PRO (active)or fallback reason (e.g.,License: FREE (invalid — key not recognized)) when a key is provided - Manifest mode for
generate—generatenow accepts markdown manifests directly, replacing the separatebatchcommand; processes all[x]marked components in a single pass applyCustomTokenscommand — New CLI command that injects$customtoken objects from a JSON mapping file into fetched variables and styles JSON files. Config-aware with-v/-soverride flags. Supports idempotent re-application, status summary, and validation of mapping entries.$custompassthrough inloadFoundations— Variables and styles map entries now preserve$customproperties when loading from JSON files, enabling the transformer to read custom token objects.
Changed
generatecommand unified — accepts both JSON files (with-cfor single component) and markdown manifests (for multiple components); all split flags (--split-components,--split-concerns,--use-subfolders) work in both modes- ManifestParser — fixed regex to correctly parse component entries from manifest markdown
- Documentation — updated all docs to reflect unified
generatecommand, removedbatchreferences, added license setup guide and Free vs. Pro feature comparison
Dependency updates
- @directededges/anova v0.15.0 — Subcomponent configuration moves from a flat
subcomponentNamePatternstring to a structuredsubcomponentsobject supporting multiple match patterns, an exclude list, and a page-level search scope. Subcomponent references in anatomy and element output now use$refpointers. Typography fieldsleadingTrim,fontFamily, andfontStyleare corrected to match actual Figma API values. - @directededges/anova-transformer v0.10.0 — Specs now support page-level subcomponent discovery (scanning beyond the component tree), multiple and excludable match patterns for subcomponent detection, and alphabetically ordered subcomponent output. Subcomponent references in
instanceOffields emit as$refpointers. The new CUSTOM token profile uses$customobjects from variables and styles as style property values. Fixes detection of subcomponents nested as instances and inconsistent slash spacing in Figma component names.
Removed
batchcommand — consolidated intogenerate; useanova generate manifest.mdinstead ofanova batch manifest.md
[0.5.0] - 2026-03-18
Supports code-only props extraction: the transformer now detects a configurable container layer, excludes it from anatomy, and surfaces its children as props with $extensions provenance metadata. Slot constraint code-only props are promoted to SlotProp.minItems, maxItems, and anyOf fields. Also introduces NumberProp inference from text-based code-only props when enabled.
Added
ConfigLoadervalidation for new processing config fields:codeOnlyPropsPattern(string),slotConstraints(boolean), andinferNumberProps(boolean)
Changed
- Compatible with
@directededges/anovav0.14.0 and@directededges/anova-transformerv0.9.0
Fixed
ConfigLoaderwas validatingiconNamePatterninstead ofglyphNamePattern, the field name used inConfig.processingsince v0.3.0. The validator now correctly referencesglyphNamePatternBatchCommandmanifest parser regex now handles component names containing parentheses
[0.4.0] - 2026-03-13
Changed
- Compatible with
@directededges/anovav0.13.0 and@directededges/anova-transformerv0.8.0
[0.3.0] - 2026-03-08
Added
iconNamePatternconfig support for detecting icon content assets
Changed
- Compatible with
@directededges/anovav0.12.0 and@directededges/anova-transformerv0.7.1
[0.2.0] - 2026-03-04
Changed
- Updated for
@directededges/anovav0.11.0 compatibility - Config defect fix
[0.1.0] - 2026-02-10
Added
- Initial CLI and MCP server for design system operations
Component.fromRestApiintegration with anova-transformer