A collection of single-purpose CLI tools that audit and maintain Figma design libraries. Each tool scans a Figma file, detects a specific category of issue, and returns a structured JSON report with direct links to every flagged node.
Two data sources are supported:
- Figma REST API — pass a personal access token and file key
- Figma MCP — extract data via the
use_figmatool from Claude Desktop or any MCP client, then pipe it in with--stdin(no token needed)
- Installation
- Data Sources
- Configuration
- Tools
- 1. Generic Layer Name Linter
- 2. Duplicate Sibling Name Detector
- 3. Unbound Auto-Layout Value Detector
- 4. Unbound Radius Value Detector
- 5. Hardcoded Text Style Detector
- 6. Component Description Coverage Checker
- 7. Property Naming Convention Auditor
- 8. Page Hygiene Scanner
- 9. Variant Linter
- 10. Layer Casing Linter
- 11. Canvas Hygiene Linter
- 12. Accessibility: Target Size Auditor
- 13. Accessibility: Missing States Auditor
- 14. Accessibility: Description Quality Auditor
- 15. Layer Ordering Linter
- Programmatic Usage
- Custom Scripts
- Exit Codes
- Development
Requires Node.js 18+.
git clone <repo-url>
cd figma-library-maintenance-tools
npm installRun any tool immediately with npx — no global install needed:
npx figma-lint-names
npx figma-lint-autolayout -p "Components"Or add shortcuts to package.json for the tools you use most:
"scripts": {
"lint:names": "node src/tools/lint-layer-names/cli.js",
"lint:autolayout": "node src/tools/lint-autolayout-values/cli.js"
}npm run lint:names -- -p "Components"
npm linkinstalls all commands globally (e.g.figma-lint-nameswithout a prefix) but requires re-running if the project moves. Use it if you run tools frequently from outside the project directory.
Every tool can receive Figma file data in one of two ways. The analysis logic is identical regardless of how the data arrives.
The default path. Pass a Figma personal access token and file key — the tool calls the Figma REST API directly.
figma-lint-names -f <file-key> -t <token>Or configure them once in .env (see Environment Variables) and run with no flags:
figma-lint-namesWhen running inside Claude Desktop or any MCP-connected environment, you can extract file data via the Figma MCP use_figma tool and pipe it into the CLI — no personal access token required.
How it works:
- The MCP
use_figmatool runs a Plugin API script inside the open Figma file and returns the document tree as JSON. - You pipe that JSON into any CLI tool with the
--stdinflag. - The tool runs its analysis on the pre-fetched data identically to the REST API path.
Step 1 — Extract data via MCP:
This project ships ready-made Plugin API scripts in src/shared/mcp-scripts.js:
import { getFileScript, getLocalVariablesScript, getTextStylesScript } from 'figma-library-maintenance-tools/src/shared/mcp-scripts.js'
// Returns a Plugin API JavaScript string to pass to use_figma
const fileScript = getFileScript({ pageNames: ['Components', 'Primitives'] })
// Only needed for the autolayout and radius linters
const variablesScript = getLocalVariablesScript({ collectionFilter: 'spac(e|ing)' })
// Only needed for the text style linter
const textStylesScript = getTextStylesScript()Each script returns data in the same shape as the corresponding Figma REST API endpoint, so all downstream detection functions work identically.
Step 2 — Pipe into CLI tools:
# Most tools only need the file tree
cat figma-data.json | npx figma-lint-names --stdin -f <file-key>
cat figma-data.json | npx figma-lint-duplicates --stdin -f <file-key>
cat figma-data.json | npx figma-check-descriptions --stdin -f <file-key>
cat figma-data.json | npx figma-audit-properties --stdin -f <file-key>
cat figma-data.json | npx figma-scan-pages --stdin -f <file-key>
cat figma-data.json | npx figma-lint-layer-order --stdin -f <file-key>
# The autolayout and radius linters also need variable data:
# stdin JSON must include both keys: { "fileData": { ... }, "variablesData": { ... } }
cat figma-full.json | npx figma-lint-autolayout --stdin -f <file-key>
cat figma-full.json | npx figma-lint-radius --stdin -f <file-key>
# The text style linter optionally accepts text style data for suggestions:
# { "fileData": { ... }, "textStylesData": [ ... ] }
cat figma-full.json | npx figma-lint-text-styles --stdin -f <file-key>Note:
--stdinmakes--tokenoptional, but--file-keyis still required — it's used to generate direct Figma URLs in the report.
MCP response size limit: The
use_figmatool caps responses at ~20kb. For large files, extract one page at a time (getFileScript({ pageNames: ['Components'] })) and concatenate the pagechildrenarrays before piping.
Programmatic usage (skip the REST API entirely):
Every orchestrator accepts a fileData option. When present, the REST API client is never instantiated:
import { lintLayerNames } from 'figma-library-maintenance-tools/src/tools/lint-layer-names/index.js'
const report = await lintLayerNames({
fileKey: 'abc123',
fileData: mcpResult, // the object returned by use_figma — { document: { ... } }
})All tools support fileData. The autolayout and radius linters additionally accept variablesData. The text style linter accepts textStylesData.
When creating a Figma personal access token, the required scopes depend on which tools you run:
| Scope | Required by |
|---|---|
file_content:read |
All tools |
file_variables:read |
figma-lint-autolayout, figma-lint-radius |
If you see a 403 error mentioning scope, generate a new token with file_variables:read checked.
Create a .env file in the project root (see .env.example). The tools load it automatically via dotenv — no need to source or export manually:
FIGMA_ACCESS_TOKEN=figd_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
FIGMA_FILE_KEY=abcDEF123ghiJKL456
FIGMA_BRANCH_KEY= # optional — set when working on a branch
FIGMA_EXCLUDE_PAGES= # optional — comma-separated page names to always skipTip: Variables already set in your shell or CI environment are never overwritten by the
.envfile.
When working on a Figma branch, provide the branch's own file key via --branch / -b or FIGMA_BRANCH_KEY. When a branch key is present it is used for all API calls and Figma URLs instead of the base file key.
figma-lint-names -b <branch-file-key>Flags override environment variables:
| Flag | Short | Description |
|---|---|---|
--file-key |
-f |
Figma file key |
--token |
-t |
Figma personal access token |
--branch |
-b |
Figma branch key (overrides file key for API calls and URLs) |
--pages |
-p |
Comma-separated page names to include |
--exclude-pages |
-x |
Comma-separated page names to exclude (takes precedence over --pages) |
--scope |
-s |
Scan scope: all (default) or components |
--stdin |
Read pre-fetched Figma data from stdin (no token needed) | |
--summary |
Deduplicate issues into unique patterns with occurrence counts | |
--format |
Output format: json (default) or text |
|
--help |
-h |
Show help for a specific tool |
Detects layers that still carry Figma's default names — Frame 1, Group 2, Rectangle 3, Vector, Ellipse 1, etc.
figma-lint-names
figma-lint-names -p "Components,Primitives"
figma-lint-names -x ".explorations,.archive"
figma-lint-names -s components # only scan inside components
figma-lint-names --stdin < data.jsonScan scope (--scope / -s):
| Scope | Description |
|---|---|
all (default) |
Scans every node on every page — inside components and outside. Nodes inside components retain full component/variant context; nodes outside components are reported with the page name as context. |
components |
Only scans layers inside component sets and standalone components. |
What it reports: component name (or page name), variant name, layer name, layer type, node ID, direct Figma URL, parent name, child names, and a suggested rename ({childName}-wrapper, container, or the lowercased type).
Detects direct children of the same parent that share identical names — e.g., four children all named flex inside a single frame.
figma-lint-duplicates
figma-lint-duplicates -p "Components"
figma-lint-duplicates --stdin < data.jsonWhat it reports: component name, variant name, parent layer name, the duplicated name, occurrence count, and each occurrence's type/ID/index.
Detects auto-layout frames where padding or gap values are not bound to a spacing variable — including zero values.
Note: When using the REST API, your token must include the
file_variables:readscope. When using MCP with--stdin, pass bothfileDataandvariablesDatain the JSON payload.
figma-lint-autolayout
figma-lint-autolayout --summary # deduplicate into unique patterns
figma-lint-autolayout --stdin < full.json # stdin must include variablesDataWhat it reports: Each unbound value is classified as bindable (exact variable match exists), off-scale (no match — reports two nearest scale values), or exception (negative value). The --summary flag groups identical patterns with occurrence counts.
Detects border radius values (topLeftRadius, topRightRadius, bottomLeftRadius, bottomRightRadius) that are not bound to a radius variable.
Note: Same variable data requirements as the autolayout linter — REST API needs
file_variables:readscope;--stdinneedsvariablesDatain the payload.
figma-lint-radius
figma-lint-radius --stdin < full.jsonWhat it reports: Each unbound radius is classified as bindable or off-scale, with the same pattern as the autolayout linter.
Detects text nodes with no text style applied — meaning they have hardcoded font size, family, and weight instead of referencing a shared text style.
figma-lint-text-styles
figma-lint-text-styles --stdin < data.jsonWhen text style data is available (via textStylesData in the JSON payload or from the MCP getTextStylesScript), each issue includes a suggestedStyle — the closest matching text style by font size and weight.
What it reports: component name, layer name, node ID, font size, font family, font style, and suggested text style (if available).
Detects published components and component sets with empty or missing description fields.
figma-check-descriptions
figma-check-descriptions --stdin < data.jsonWhat it reports: component name, page, node ID, component type. Summary: X of Y components have descriptions (Z%).
Detects component properties that violate the library's naming conventions.
figma-audit-properties
figma-audit-properties -p "Components,.labs"
figma-audit-properties --stdin < data.jsonWhat it checks:
| Violation | Example | Rule |
|---|---|---|
| Capitalized names | Size instead of size |
First letter must be lowercase |
| Default names | Property 1 |
Must be renamed from Figma's default |
| Toggle inconsistency | show icon + with avatar in same library |
Pick one boolean prefix convention |
| Boolean/variant conflict | focused boolean + state variant with focused value |
Two controls for the same concept |
| Dependency prefix order | ↳ text appearing before show text |
↳ prefixed properties must follow their parent toggle |
What it reports: Each violation with component name, property name, type, and message. Boolean/variant conflicts show both the boolean name and the conflicting variant property. Dependency prefix issues show the dependent property and its expected parent. Also includes a toggle convention summary.
Detects non-component items at the top level of published pages — stray instances, loose frames, groups, etc. Also checks Section naming conventions.
figma-scan-pages
figma-scan-pages -p "Components,Primitives,Icons"
figma-scan-pages --stdin < data.jsonWhat it reports: page name, item name, item type, node ID. Expected types: COMPONENT_SET, COMPONENT, SECTION. Everything else is flagged as unexpected. Also flags Sections whose names don't reference any of their child component names (e.g., a Section named "Controls" containing Checkbox, Switch, and Radio).
Detects variant-related issues in component sets: single-value variant properties (dead-end dropdowns), duplicate variant name strings (which break API access), and missing combinations in the variant matrix.
figma-lint-variants
figma-lint-variants -p "Components"
figma-lint-variants --matrix # include coverage gap analysis
figma-lint-variants -x ".labs,.explorations"
figma-lint-variants --stdin < data.jsonIssue types:
| Type | Description |
|---|---|
single-value-variant |
A variant property with only one selectable option |
duplicate-variant-name |
Identical variant name strings — breaks componentPropertyDefinitions API |
coverage-gap |
Missing combination in the variant matrix (only with --matrix) |
What it reports: Component name, property name, single value (single-value issues). Duplicate name string, count, node IDs (duplicate issues). Missing variant name string (coverage gaps). Summary with counts by type.
Detects layer names inside published components that violate the lowercase naming convention. By default checks only TEXT layers; use --all-layers for all layer types.
figma-lint-casing
figma-lint-casing -p "Components"
figma-lint-casing --all-layers # check FRAME, GROUP, etc. too
figma-lint-casing --stdin < data.jsonWhat it checks: TEXT layer names inside component sets and standalone components. Instance layers (INSTANCE type) are exempt — they conventionally use PascalCase component names.
What it reports: Component name, variant name, current layer name, expected (lowercased) name, node ID.
Detects canvas-level hygiene issues: pages whose content doesn't start at (0, 0), and page names with leading or trailing whitespace.
figma-lint-canvas
figma-lint-canvas -p "Icons,.internal,.explorations"
figma-lint-canvas --stdin < data.jsonIssue types:
| Type | Description |
|---|---|
origin-drift |
Page content does not start at canvas origin (0, 0) |
page-name-whitespace |
Page name has invisible leading or trailing spaces |
Audits interactive components for WCAG 2.5.8 target size compliance. Finds the smallest variant in each interactive component set and checks whether its minimum dimension meets the 24×24px threshold.
figma-audit-a11y-target-sizes
figma-audit-a11y-target-sizes -p "Components"
figma-audit-a11y-target-sizes --stdin < data.jsonSeverity levels:
| Severity | Condition |
|---|---|
high |
Minimum dimension below 17px |
medium |
Minimum dimension 17–23px (below AA minimum) |
Interactive components checked: Button, Checkbox, Radio, Switch, Select, TextInput, TextArea, MenuItem, Autocomplete, TabList, Avatar, Badge, Toast.
Audits interactive components for missing state variants — focused, disabled, invalid, and readOnly — that are required for accessible keyboard and screen reader interaction.
figma-audit-a11y-states
figma-audit-a11y-states -p "Components"
figma-audit-a11y-states --stdin < data.jsonSeverity levels:
| Missing State | Severity | WCAG |
|---|---|---|
focused |
high |
2.4.7 (Focus Visible) |
disabled |
medium |
4.1.2 (Name, Role, Value) |
invalid |
medium |
3.3.1 (Error Identification) |
readOnly |
low |
— |
Expected states per component: Each interactive component has a defined set of required states. For example, TextInput expects focused, disabled, invalid, and readOnly. Checkbox expects focused and disabled. The full map is in detect.js.
Checks whether interactive component descriptions include accessibility-relevant documentation — keyboard behavior, ARIA roles, focus management, or screen reader expectations.
figma-audit-a11y-descriptions
figma-audit-a11y-descriptions -p "Components"
figma-audit-a11y-descriptions --stdin < data.jsonSeverity levels:
| Severity | Condition |
|---|---|
high |
Complex widget (Dialog, Menu, Autocomplete, Popover, TabList, Toast) missing a11y notes |
medium |
Simple control (Button, Checkbox, Select, etc.) missing a11y notes |
Each issue includes a recommendation field with specific guidance on what accessibility information should be added to that component's description.
Audits layer ordering across component set variants. Checks that shared layers appear in the same relative order in every variant, that absolutely positioned background layers appear at the bottom of the layer panel, that overlay layers appear at the top, that variant layer names are structurally consistent, and that variants are ordered to match their canvas spatial layout.
Enforces the rules defined in Section 6 (Layer Ordering) of the Figma Library Structure Guidelines.
figma-lint-layer-order
figma-lint-layer-order -p "Components"
figma-lint-layer-order --summary
figma-lint-layer-order --stdin < data.jsonWhat it checks:
| Check | Category | Description |
|---|---|---|
| Variant consistency | variantInconsistency |
Shared layers appear in the same relative order across all variants in a component set. If variant A has flex-leading → flex-content → flex-trailing and variant B has flex-leading → flex-trailing → flex-content, that's a failure. |
| Background position | backgroundPosition |
Absolutely positioned layers with background names (border, .focusRing, backdrop, bg, fill, card) appear at the bottom of the layer panel (first in the children array) so they render behind content. |
| Overlay position | overlayPosition |
Absolutely positioned layers with overlay names (closeButton, overlay, close-button) appear at the top of the layer panel (last in the children array) so they render above content. |
| Naming mismatch | namingMismatch |
Variants have different layer names from the canonical variant (the first variant in the set). Reports which names are missing and which are unexpected. This is a structural issue, not an ordering issue — the tool distinguishes the two. |
| Variant order | variantOrder |
Variants within a component set are ordered so the layer panel reads in canvas spatial order — top-left variant at top of panel, scanning left-to-right across columns then top-to-bottom across rows. Reports the number of misplaced variants. |
What it reports: component name, variant name, page name, issue category, expected order, actual order, node ID, direct Figma URL.
Summary mode: --summary deduplicates issues by component set, showing unique patterns with occurrence counts.
Fix script: A ready-made Plugin API fix script is included at src/tools/lint-layer-order/fix-script.js. Set COMPONENT_SET_ID to the target component set node ID and run via use_figma. The script reorders shared layers to match the canonical order (derived from the first variant or an explicit override). It skips structural mismatches that require manual intervention, handles duplicate layer names safely, and supports dry-run mode.
// fix-script.js configuration (edit these before running)
const COMPONENT_SET_ID = '4481:2862'; // MenuItem component set
const CANONICAL_ORDER = null; // null = derive from first variant
const DRY_RUN = false; // true = preview without applyingMCP path: A self-contained Plugin API script is available via the getLayerOrderLintScript() function in src/tools/lint-layer-order/script.js. This runs the full audit inside Figma and returns the report — no REST API token needed.
Every tool can be imported into a Node.js application. All functions accept fileData to skip the REST API:
import { lintLayerNames } from 'figma-library-maintenance-tools/src/tools/lint-layer-names/index.js'
import { lintDuplicateSiblings } from 'figma-library-maintenance-tools/src/tools/lint-duplicate-siblings/index.js'
import { lintAutolayoutValues } from 'figma-library-maintenance-tools/src/tools/lint-autolayout-values/index.js'
import { lintRadiusValues } from 'figma-library-maintenance-tools/src/tools/lint-radius-values/index.js'
import { lintTextStyles } from 'figma-library-maintenance-tools/src/tools/lint-text-styles/index.js'
import { checkDescriptionCoverage } from 'figma-library-maintenance-tools/src/tools/check-descriptions/index.js'
import { auditPropertyNames } from 'figma-library-maintenance-tools/src/tools/audit-property-names/index.js'
import { scanPageHygiene } from 'figma-library-maintenance-tools/src/tools/scan-page-hygiene/index.js'
import { lintVariants } from 'figma-library-maintenance-tools/src/tools/lint-variants/index.js'
import { lintCasing } from 'figma-library-maintenance-tools/src/tools/lint-casing/index.js'
import { lintCanvas } from 'figma-library-maintenance-tools/src/tools/lint-canvas/index.js'
import { auditA11yTargetSizes } from 'figma-library-maintenance-tools/src/tools/audit-a11y-target-sizes/index.js'
import { auditA11yMissingStates } from 'figma-library-maintenance-tools/src/tools/audit-a11y-missing-states/index.js'
import { auditA11yDescriptionCoverage } from 'figma-library-maintenance-tools/src/tools/audit-a11y-descriptions/index.js'
import { lintLayerOrder } from 'figma-library-maintenance-tools/src/tools/lint-layer-order/index.js'
// Via REST API
const report = await lintLayerNames({
accessToken: 'figd_...',
fileKey: 'abcDEF123',
branchKey: 'branchXYZ789', // optional
pages: ['Components', 'Primitives'], // optional
excludePages: ['.archive'], // optional
scope: 'all', // optional — 'all' or 'components'
})
// Via pre-fetched data (from MCP or saved JSON)
const report = await lintLayerNames({
fileKey: 'abcDEF123',
fileData: mcpResult, // { document: { ... } }
})All functions return a report with this shape:
{
title: 'Report Name',
summary: { /* tool-specific counts */ },
issues: [
{
nodeId: '123:456',
figmaUrl: 'https://www.figma.com/design/<key>/?node-id=123-456',
// ... tool-specific fields
}
],
}Lower-level detection functions are also available for custom integrations:
import { isGenericName, suggestName } from 'figma-library-maintenance-tools/src/tools/lint-layer-names/detect.js'
import { findDuplicateSiblings } from 'figma-library-maintenance-tools/src/tools/lint-duplicate-siblings/detect.js'
import { isAutoLayoutNode, classifyValue, buildSpaceScale } from 'figma-library-maintenance-tools/src/tools/lint-autolayout-values/detect.js'
import { hasRadiusValues, buildRadiusScale } from 'figma-library-maintenance-tools/src/tools/lint-radius-values/detect.js'
import { isHardcodedText, buildTextStyleMap } from 'figma-library-maintenance-tools/src/tools/lint-text-styles/detect.js'
import { hasValidDescription } from 'figma-library-maintenance-tools/src/tools/check-descriptions/detect.js'
import { cleanPropertyName, isDefaultName, isCapitalized } from 'figma-library-maintenance-tools/src/tools/audit-property-names/detect.js'
import { classifyTopLevelItem, scanPage } from 'figma-library-maintenance-tools/src/tools/scan-page-hygiene/detect.js'
import { checkVariantConsistency, checkAbsolutePositioning, auditLayerOrder, compareSharedOrder, detectNamingMismatch } from 'figma-library-maintenance-tools/src/tools/lint-layer-order/detect.js'
// MCP extraction scripts
import { getFileScript, getLocalVariablesScript, getTextStylesScript } from 'figma-library-maintenance-tools/src/shared/mcp-scripts.js'
// Utilities
import { getEffectiveFileKey } from 'figma-library-maintenance-tools/src/shared/cli-utils.js'
import { buildFigmaUrl, enrichIssuesWithUrls } from 'figma-library-maintenance-tools/src/shared/figma-urls.js'The built-in tools cover common auditing tasks, but sometimes you need to do something specific: create a node, rename a batch of layers, extract a custom report, or run a one-off fix. Custom scripts let you write arbitrary Figma Plugin API code and run it via the MCP use_figma tool.
A custom script is a plain .js file containing Figma Plugin API code. The code runs inside Figma's Plugin API sandbox — the same environment that Figma plugins use. It has access to the figma global and can read, create, modify, and delete any object in the file.
# Output a script's contents (for piping to an agent or use_figma)
figma-run-script examples/add-rectangle.jsIn an agentic workflow, the agent reads the script output and passes it to the Figma MCP use_figma tool as the code parameter. The script executes inside the open Figma file and the result is returned.
Scripts must follow these rules:
- Self-contained — no
import,require, orexport. Everything must be inline. The Plugin API sandbox has no module system. - Synchronous access to
figma— thefigmaglobal is available. Use it to access pages, nodes, variables, styles, and everything else in the Plugin API. - Return results with
return— the lastreturnstatement sends data back through MCP. Return a plain object or array — it will be serialized as JSON. - No network access — no
fetch,XMLHttpRequest, or any external communication. The sandbox is isolated. - No
console.log— output goes nowhere. Usereturnfor all results.
// examples/add-rectangle.js — creates a rectangle on the current page
const page = figma.currentPage
const rect = figma.createRectangle()
rect.name = 'example-rectangle'
rect.resize(200, 100)
rect.x = 0
rect.y = 0
rect.fills = [{ type: 'SOLID', color: { r: 0.22, g: 0.27, b: 0.36 } }]
rect.cornerRadius = 8
page.appendChild(rect)
return { nodeId: rect.id, name: rect.name, page: page.name }When run via use_figma, this creates a 200×100 dark rectangle on the current page and returns its node ID.
The examples/ directory contains working scripts:
| Script | What it does |
|---|---|
add-rectangle.js |
Creates a rectangle and returns its node ID |
list-pages.js |
Lists all pages with child counts (read-only) |
rename-generic-layers.js |
Renames Frame 1, Group 2, etc. on the current page |
Start read-only. Test with scripts that inspect without modifying. Add writes once you're confident the logic is correct.
Return structured data. Return objects with named fields, not bare strings. { nodeId: rect.id, name: rect.name } is more useful downstream than rect.id.
Keep scripts small. The Plugin API has execution time limits. If a script needs to process thousands of nodes, batch the work or limit scope with page filters.
Use var instead of const/let in loops when targeting the Plugin API sandbox — some execution environments handle block scoping differently than Node.js.
All tools use consistent exit codes:
| Code | Meaning |
|---|---|
0 |
No issues found |
1 |
Issues found (report on stdout) |
2 |
Runtime error (missing token, API failure, etc.) |
figma-lint-names > report.json || echo "Issues found"npm test # run all tests
npm run test:watch # watch mode
npm run test:coverage # with coveragefigma-library-maintenance-tools/
├── package.json
├── vitest.config.js
├── .env.example
├── examples/ # Custom Plugin API scripts
│ ├── add-rectangle.js
│ ├── list-pages.js
│ └── rename-generic-layers.js
└── src/
├── shared/
│ ├── figma-client.js # Figma REST API client
│ ├── tree-traversal.js # Node tree traversal utilities
│ ├── cli-utils.js # CLI arg parsing, report formatting, summary dedup
│ ├── figma-urls.js # Figma node URL builder
│ ├── env.js # .env file loader (dotenv wrapper)
│ ├── stdin.js # Reads pre-fetched Figma data from stdin
│ └── mcp-scripts.js # Plugin API extraction scripts for Figma MCP
└── tools/
├── lint-layer-names/ # Generic layer name linter
├── lint-duplicate-siblings/ # Duplicate sibling name detector
├── lint-autolayout-values/ # Unbound auto-layout value detector
├── lint-radius-values/ # Unbound radius value detector
├── lint-text-styles/ # Hardcoded text style detector
├── check-descriptions/ # Component description coverage checker
├── audit-property-names/ # Property naming convention auditor
├── scan-page-hygiene/ # Page hygiene scanner
├── lint-variants/ # Variant linter
├── lint-casing/ # Layer casing linter
├── lint-canvas/ # Canvas hygiene linter
├── lint-layer-order/ # Layer ordering linter
├── audit-a11y-target-sizes/ # Accessibility: target size auditor
├── audit-a11y-missing-states/ # Accessibility: missing states auditor
├── audit-a11y-descriptions/ # Accessibility: description quality auditor
└── run-script/ # Custom script runner
Each tool directory contains:
detect.js— Pure detection functions. No API calls, no side effects.detect.test.js— Thorough tests for detection logic.index.js— Orchestrator. Fetches data (or acceptsfileData), calls detection, assembles the report.index.test.js— Tests with mocked API responses.cli.js— Thin CLI wrapper. Parses arguments, handles--stdin, formats output.
Some tools include additional files:
script.js— Self-contained Plugin API script emitter for running the tool inside Figma via MCP (no REST API token needed).fix-script.js— Plugin API script that fixes detected issues directly in the Figma file.
Shared utilities in src/shared/ handle API communication, tree traversal, argument parsing, .env loading, stdin reading, MCP script generation, branch key resolution, URL building, and report formatting.