Set up
Runs on your computer. Pick your agent and operating system.
This project does not publish an install method we can turn into a config. Follow the README instead.
README
synced Oct 26, 2026Dalí
Local search and indexing engine for Figma .fig files.
Dalí parses Figma .fig files on disk, builds a rebuildable local SQLite index
(full-text, structural and vector), and exposes search through a CLI and an MCP
server. Figma does not need to be open, and designs stay local.
The .fig file remains the source of truth. Everything Dalí generates is a
rebuildable local index under .dali/.
Why Dalí
Unlike other .fig tools that dump a tree or relay the Figma REST API, Dalí is
the only offline engine that combines full-text, structural and semantic search
with analysis and code generation in a single binary.
| Dalí | FigReader MCP | fig-mcp | figmatojson | figmingo MCP | OpenPencil | |
|---|---|---|---|---|---|---|
Offline .fig parsing | ✅ | ✅ | ✅ | ✅ | ❌ (REST) | ✅ |
| Persistent local index | ✅ SQLite | ❌ | ❌ | ❌ | ✅ | ❌ |
| Full-text search | ✅ FTS5 | partial | ❌ | ❌ | ✅ | ❌ |
| Semantic/vector search | ✅ hybrid | ❌ | ❌ | ❌ | ❌ | ❌ |
| Structural DSL + XPath | ✅ | ❌ | ❌ | ❌ | ❌ | find/tree |
| Lint, geometry, diff | ✅ | ❌ | ❌ | ❌ | ❌ | lint |
| Codegen + tokens | ✅ | ❌ | render | JSON/repack | HTML | ❌ |
| MCP server | ✅ ~22 tools | ✅ | ✅ | ❌ | ✅ | ✅ 90 tools |
| CLI | ✅ | ❌ | partial | ✅ | ❌ | ✅ |
Features
Search
- Structural — query the document tree and node relationships
(
query_figmaDSL, XPath subset via a leading/). - Full-text — SQLite FTS5 over node names and text content.
- Semantic — conceptual vector search via
sqlite-vec, with local embeddings (llama.cpp+ EmbeddingGemma), Ollama, or a remote provider. Only missing or changed embeddings are rebuilt. - Name suggestions — near-miss hints when a search or filter matches nothing.
Index
- Local
.figparsing — decode Figma's Kiwi binary format directly, no Figma running, so any design file is searchable offline. - SQLite index — persistent, rebuildable index stored under
.dali/. - Asset extraction — images written next to the original
.fig.
Analysis
- Design linting — accessibility and design-quality checks with presets.
- Geometry analysis — layout gaps, repeated sizes, sibling overlaps.
- Version diff — match two
.figfiles by stable node ID and report added, removed, modified nodes.
Codegen
- Component code generation — semantic HTML with inline CSS (or Tailwind) plus design tokens (CSS variables, Style Dictionary JSON, Figma Tokens JSON).
MCP
- MCP server — expose one or many
.figfiles to MCP clients.
Architecture
.fig
│
parser.Open ── kiwi decode
│
model.Build ── document tree
│
┌──────┴───────┐
▼ ▼
SQLite Semantic
index indexing
FTS5 │
query/ ▼
embeddings (sqlite-vec)
│ │
└──────┬───────┘
▼
query.Service
│ │
CLI MCP
The same query.Service powers the CLI and the MCP server.
Core pipeline: internal/kiwi/ → internal/parser/ → internal/model/ → internal/index/ → internal/query/, plus
internal/embed/, internal/semantic/, internal/mcp/ and cmd/dali/.
Installation
Prebuilt binary
Requires only sh and curl. The latest release is downloaded, its checksum
verified, and the binary installed into ~/.local/bin:
curl -fsSL https://raw.githubusercontent.com/salvadorsru/dali/master/install.sh | sh
Environment knobs: DALI_BIN overrides the install directory, DALI_VERSION
pins a release tag, DALI_REPO targets a fork:
DALI_VERSION=v1.2.0 DALI_BIN=$HOME/bin sh install.sh
If the install directory is not on your PATH, add it:
export PATH="$HOME/.local/bin:$PATH"
Verify the install:
dali version
From source
Requires Go. Clone and build:
git clone https://github.com/salvadorsru/dali
cd dali
go build -o dali ./cmd/dali
or install straight from the module:
go install github.com/salvadorsru/dali/cmd/dali@latest
Updating
dali update
Resolves the latest release, verifies it against the published checksums and
replaces the running binary in place; it reports when there is nothing to do.
Re-running install.sh does the same thing from outside the tool.
Usage
The general form is:
dali [<file.fig>] <command> [args]
The file is optional: when omitted, Dalí uses the single .fig in the current
directory (dali . does the same). The first command builds the
.dali/<name>.db index on demand — a SQLite index that is rebuilt
automatically whenever the source file changes. The .fig itself is never
modified.
Orient yourself
dali design.fig inspect # document counts
dali design.fig components # list components
dali design.fig fonts # fonts in use, ranked by count
dali design.fig variables # design tokens (variables) per mode
dali design.fig styles # style definitions: kind, value, usage
dali design.fig assets # image assets
Explore the tree
dali design.fig path 123:456 # root-to-node path
dali design.fig context 123:456 # path, parent, children, styles, assets
dali design.fig children 123:456 # direct children
dali design.fig parent 123:456 # parent node
dali design.fig subtree 123:456 # whole subtree (--depth N to cap it)
dali design.fig node 123:456 # one node's geometry and styles
dali design.fig vector 123:456 # exact SVG of the vectors under it
Search
Full-text over node names and text content:
dali design.fig search "payment"
dali design.fig search "card" --limit 20
Structural queries, as a DSL or XPath subset:
dali design.fig query 'type=FRAME name~"card"'
dali design.fig query 'text~price AND width>=200'
dali design.fig query '//FRAME[@name~"card"]' --limit 10 # leading / = XPath subset
The DSL terms combine with AND: type=, name~, text~, font=,
page~, component=, visible=, width>=, height<=, descendants().
A leading / routes to the XPath subset (//FRAME[@name~'card'],
/DOCUMENT/CANVAS, //*[contains(@name,'button')]). When nothing matches,
Dalí suggests near-miss names.
Semantic search for conceptual queries — it builds the vector index on first
run and needs a download confirmed once (skip the prompt with --yes or
DALI_EMBED_YES=1):
dali design.fig semantic-search "checkout flow"
dali design.fig semantic-search "checkout flow" --hybrid --limit 10
See Semantic search for how the local embedding server works.
Lint and geometry
dali design.fig lint --preset recommended # recommended | strict | accessibility
dali design.fig analyze spacing # spacing | clusters | overlaps
dali design.fig analyze overlaps --min-ratio 0.3
dali diff old.fig new.fig # added, removed, modified nodes
Code generation
dali design.fig generate # all components into ./generated
dali design.fig generate --id 1:123 --out ./components
dali design.fig generate --name "VideoCaption" --out ./components
dali design.fig generate --tokens css # css | style-dict | figma
dali design.fig generate --style tailwind # inline (default) | tailwind
Assets
dali design.fig assets # list assets
dali design.fig extract-assets # write them to design.assets/images/
Global flags
--json prints machine-readable output, --yes skips download prompts, and
-q/--quiet hides progress. dali --help lists every command and flag.
MCP
Mount one or many .fig files and let MCP clients query them through Dalí's
tools. Config helpers cover OpenCode, Cursor, Claude Code, VS Code, Cline,
Roo Code, and Windsurf. Generate per-client configs with:
dali mcp-config --opencode # also: cursor, claude, vscode, cline, roo, windsurf, all
--all writes configs for every supported editor; existing config files are
merged, never clobbered.
Starting the server
The argument decides the mode:
dali /designs mcp # context: every design under the directory
dali /designs/woments/woments.fig mcp # figma: one design, mounted at startup
- Context mode (
dali /designs mcp, or no argument for the current directory): every.figand.deckbelow the directory is a project, named by its path relative to the directory without the extension (woments/woments). A directory that holds a single design still starts in context mode: it is a root with one project, not a mounted file. - Figma mode (
dali /designs/woments/woments.fig mcp): the server serves exactly that design.list_figsanduse_figmaare not registered, tools never takefile, and naming another design returns an error that explains the mode.
Catalog, selection, queries
list_figslists the projects under the root. It accepts an optionalnamefilter andlimit(default 50), and reportstruncatedwhen more projects matched. The catalog is scanned once per session; passrefreshto rescan the directory.use_figmaselects one project by its name fromlist_figsand returns the confirmed name and absolute path. A partial name resolves when it fits a single design, and a directory holding one design answers to its own name.- Every later tool call defaults to that project and omits
file. Passingfileswitches to another project for that call. Every response reports theprojectthat answered it, and a search that matches nothing adds ahintnaming the other projects when the catalog holds more than one.
When a request could match more than one design, list the candidates and ask the user; never guess.
| Client | Configuration |
|---|---|
| OpenCode | opencode.json |
| Cursor | .cursor/mcp.json |
| Claude Code | .mcp.json |
| VS Code | .vscode/mcp.json |
| Cline | .cline/mcp.json |
| Roo Code | .roo/mcp.json |
| Windsurf | ~/.codeium/windsurf/mcp_config.json |
Tools
Tools exposed to MCP clients (list_figs and use_figma only in context mode):
analyze_figma design-quality checks (spacing, clusters, overlaps)
find_assets list image assets
find_components list components
find_instances list component instances
generate_figma semantic HTML codegen (inline CSS or Tailwind) for components and tokens
get_assets list image assets
get_context structural context for a node
get_descendants fetch a whole subtree in one call
get_dependents impact analysis of component placement
get_styles list style definitions
get_tokens aggregate design tokens (incl. fonts, variables)
get_usages list page placements of a component
get_vector exact SVG of the vectors under a node, with local overrides
index_embeddings build the vector index (the only embedding builder)
list_figs list projects under the designs directory (context mode)
list_pages list document pages
lint_figma design-quality and accessibility linting
query_figma structural DSL query over the document tree (leading "/" = XPath)
search_figma find nodes: mode = text | semantic | hybrid
section_figma resolve a named section/component with its subtree
use_figma select the project later calls default to (context mode)
Search contracts
search_figmais the only search entry point. Arguments:mode,query,type,page,limit(default 20), plus structural filters. Search never builds embeddings.index_embeddingsis the only tool that builds embeddings. Searches never do — callindex_embeddingsfirst when semantic/hybrid search fails with a hint.query_figmaoperates structurally and never uses embeddings; a leading/routes to the XPath subset.- Instances are found via
find_instances; assets viafind_assets.
| Tool | Embeddings | Indexes |
|---|---|---|
search_figma (mode=text, query_figma, ...) | never | never |
search_figma (mode=semantic/hybrid) | only if vector index exists + DALI_EMBEDDINGS=1 | never |
index_embeddings | yes — the only tool that does | yes |
Semantic search
mode=auto manages an official llama.cpp llama-server bundling
ggml-org/embeddinggemma-300M-GGUF, downloaded into ~/.cache/dali. The server
stays alive so later commands reuse it.
Supported providers: auto, ollama, llama, remote. Set
DALI_EMBED_PROVIDER=off to disable.
Configuration
| Variable | Purpose |
|---|---|
DALI_EMBED_PROVIDER | auto, ollama, llama, remote, or off |
DALI_EMBEDDINGS=1 | opt-in: enable embeddings in the MCP server |
DALI_CACHE_DIR | Dalí cache directory |
DALI_LLAMA_URL | reuse an existing llama-server |
DALI_EMBED_URL | Ollama embedding endpoint |
DALI_EMBED_YES=1 | skip download confirmation |
DALI_LLAMA_KEEP=0 | stop the managed server after each command |
DALI_LLAMA_PARALLEL | override llama-server parallelism |
DALI_LLAMA_THREADS | override llama-server threads |
DALI_EMBED_CONCURRENCY | embedding worker concurrency |
DALI_EMBED_BATCH | embedding batch size |
DALI_NET_TEST=1 | enable live network tests |
DALI_MANAGED_TEST=1 | enable managed-server tests |
Embedding generation uses a worker pool and a single SQLite writer. If the
model or semantic.GeneratorVersion changes, the vector index is rebuilt.
Files
Dalí never modifies the original .fig.
design.fig original (untouched)
.dali/design.db rebuildable SQLite index (deletable)
design.assets/
images/ extracted user-facing assets
Deleting .dali/ removes generated index data only; the design stays intact.
Development
Requirements: Go.
go build -o dali ./cmd/dali
go test ./...
go vet ./...
Integration tests and the parser benchmark read ../dali.fig and are skipped
automatically when absent.
Note: never downgrade modernc.org/sqlite — FTS5 is compiled into it.
sqlite-vec is provided via modernc.org/sqlite/vec and registers itself
through a blank import.
License
GNU General Public License v3.0