# Primitiv
**The design system contract keeping teams and agents in sync.**
Retrieval gives you data. Reconciliation gives you truth.
Primitiv sits above your design sources — Figma, codebase, Storybook, token files — scans them, reconciles conflicts between them, and exposes a single machine-readable contract via MCP. Any agent that connects gets one authoritative answer before it builds. Your code never leaves your machine.
## Quick start
```bash
npx @ai-by-design/primitiv init # detect your stack, write config + MCP registration
npx @ai-by-design/primitiv build # scan sources, resolve conflicts, write the contract
npx @ai-by-design/primitiv serve # start the MCP server
```
`init` writes a `.mcp.json` to your project root, so Cursor, Claude Code, Codex, Windsurf, and any other MCP-compatible tool pick up the server without manual config. `primitiv init` is safe to re-run — it keeps your `primitiv.config.js` and refreshes the other wiring (MCP config, agent instructions, skill files, CI workflow).
If your project is on GitHub, `init` also installs `.github/workflows/primitiv-verify.yml` — a workflow that runs `primitiv verify --strict` on every PR and push. Pair it with branch protection on your default branch and the merge is blocked when an agent ships UI that breaks the contract or sneaks a hardcoded value past it.
From here, every agent that builds UI calls `get_design_context` first, then `get_violations` before generating any literal value — and gets your resolved design contract back.
## The problem
Design-relevant information is spread across Figma, tokens, Storybook, and the codebase itself — sources that were never meant to stay in sync. Humans reconcile the drift by inference; agents can't, so they fall back on training-data patterns and build UI that works but doesn't fit.
Primitiv resolves it: one contract, built from every source, served live to every agent.
## How it works
```
Any source Primitiv Your agent
Figma ──┐
Codebase ──┤──► scan ──► reconcile ──► contract ──► MCP ──► Cursor / Claude Code / Codex / Windsurf / any MCP-compatible tool
Storybook ──┤
Tokens file ──┤
Any adapter ──┘
```
1. **Scan** — Primitiv ingests from any configured source via adapters. The codebase scanner parses TypeScript/JSX structurally (AST, not regex): every component in a file is captured at its definition site and classified by `kind`, theme tokens are pulled across all categories, and component-internal CSS variables are separated from the global design-token scale. Theme-variant values (`.dark`, `[data-theme="dim"]`, `@media (prefers-color-scheme: …)`) are captured as the token's `modes` rather than dropped or duplicated, and a token defined twice in one source with two different values surfaces as a pending conflict instead of silently losing one
2. **Reconcile** — Conflicts between sources are surfaced and resolved according to your governance configuration
3. **Infer** — Design rules are extracted from actual codebase patterns and written into the contract
4. **Contract** — A single `primitiv.contract.json` is written as the canonical reference
5. **MCP** — Agents call `get_design_context` before building and receive the resolved contract
---
## Install
```bash
npm install @ai-by-design/primitiv
# or
bun add @ai-by-design/primitiv
```
### CLI
| Command | Description |
|---------|-------------|
| `primitiv init [dir]` | Detect your project (framework, TypeScript, Tailwind, Figma, Storybook, package manager) and generate `primitiv.config.js`. Also writes a project-scoped MCP config, refreshes a Primitiv block in `AGENTS.md` and `CLAUDE.md`, installs a `verify --strict` GitHub Actions workflow, and drops a `/build-component` skill for Claude Code. |
| `primitiv build [config]` | Scan sources, resolve conflicts, lint for token misuse, write the contract. With `governance.onConflict: "error"`, exits `2` when pending conflicts remain (the contract is still written). |
| `primitiv serve [config]` | Start the MCP server against the built contract. Hot-reloads when the contract file changes. |
| `primitiv verify [config]` | Re-run `build` and exit non-zero if conflicts are unresolved, token misuse is detected, or the contract is stale. Flags: `--strict`, `--json`, `--fast`. Intended for CI. |
`init` detects Next, Nuxt, Astro, SvelteKit, Remix, Expo, Qwik, Vite, Solid, and React. Package manager is read from the lockfile (`bun.lock` → `bunx`, `pnpm-lock.yaml` → `pnpm dlx`, `yarn.lock` → `yarn dlx`, otherwise `npx`) and used in the generated MCP config.
### MCP tools
| Tool | Description |
|------|-------------|
| `get_design_context` | Get tokens, components, conflicts, inferred rules, and violation count. Default (no category) returns a summary with counts. Pass `category: "all" \| "tokens" \| "components" \| "conflicts"` for full detail. Filter tokens with `tokenCategory`: `colors`, `spacing`, `sizes`, `typography`, `borderRadius`, `shadows`, `zIndex`, `breakpoints`, `motion`. |
| `get_token` | Look up a specific token by name. Pass `category` to narrow search (e.g. `"colors"`, `"spacing"`, `"borderRadius"`); aliases like `"radius"` / `"z-index"` are normalized. |
| `get_component` | Look up a specific component by name. Returns props, variants, source provenance, and `kind` — `component`, `screen`, `provider`, `icon`, or `other`, so agents reuse real UI and skip screens/providers/icons. |
| `get_conflicts` | Get conflicts between sources. Pass `type: "all" \| "token" \| "component"` and `status: "all" \| "pending" \| "resolved"`. Returns `actionableCount` and `pendingDecisionCount` alongside the list. |
| `get_inferred_rules` | Get the design rules Primitiv has extracted from your codebase patterns. Pass `category` to filter. |
| `get_violations` | List token-misuse violations — hardcoded literals in source that bypass the contract. Smart-matched to a suggested token when the literal matches one. Pass `category: "all" \| "colors" \| "spacing"` to filter. |
All tools are read-only — they never modify your code or contract. Primitiv works with any tool that speaks MCP — it is not tied to a specific editor or agent ecosystem.
### Using Primitiv across multiple projects
Primitiv runs **one MCP server process per project**, each pointed at that project's contract. This is intentional — each project has its own resolved contract, and there is no global shared state.
`primitiv init` writes a project-scoped MCP config automatically. **Do not add Primitiv to your editor's global MCP config** — if you do, the global server will keep serving one project's contract regardless of which project your agent is working in.
#### Per-editor setup
| Editor | Project-level config | Global config (avoid for Primitiv) |
|--------|---------------------|--------------------------------------|
| **Claude Code** | `.mcp.json` at repo root ✅ | `~/.claude/settings.json` |
| **Cursor** | `.cursor/mcp.json` at repo root ✅ | `~/.cursor/mcp.json` |
| **Windsurf** | `.windsurf/mcp_config.json` at repo root ✅ | `~/.codeium/windsurf/mcp_config.json` |
| **Zed** | `.zed/settings.json` at repo root ✅ | `~/.config/zed/settings.json` |
`primitiv init` detects which editor config exists and writes to the right project-level file. If none exists, it creates `.mcp.json` (works with Claude Code and most modern editors).
#### Switching between projects
Each project needs its own `primitiv init` + `primitiv build`. When you switch projects in your editor, the project-scoped MCP config is loaded automatically — no manual switching needed, as long as you haven't added Primitiv to the global config.
If you already added Primitiv to your global editor config, remove it:
```bash
# Cursor — edit ~/.cursor/mcp.json and remove the "primitiv" entry
# Windsurf — edit ~/.codeium/windsurf/mcp_config.json and remove "primitiv"
```
#### Stale or mismatched contract warnings
If `get_design_context` returns a `warnings` array, stop and resolve before proceeding:
- **`STALE CONTRACT`** — the contract is outdated. The warning includes the exact command to rebuild, e.g.: `npx @ai-by-design/primitiv build /path/to/your/primitiv.config.js`
- **`CONTRACT MISMATCH`** — the server is serving a contract from a different project. This usually means Primitiv is in your global editor MCP config. Remove it from there and re-run `primitiv init` in the correct project.
### CI / GitHub Actions
`primitiv init` auto-installs `.github/workflows/primitiv-verify.yml` when the project's remote is on GitHub. The workflow runs `verify --strict` on every pull request and push to your default branch, and fails the check on **unresolved conflicts**, **token misuse**, or a **stale contract**.
To turn the failed check into a hard merge gate, enable branch protection on the default branch — `init` prints the exact settings URL after install (`https://github.com///settings/branches`).
The workflow uses `npx --yes @ai-by-design/primitiv verify --strict`, so it works regardless of your project's package manager. Two notes:
- **Monorepos** — the workflow runs at repo root. If `primitiv.config.js` lives in a subdirectory, add a `working-directory:` to the verify step in the generated YAML.
- **Pinning** — `npx --yes @ai-by-design/primitiv` fetches the latest version. To pin, edit the workflow's `run:` line to `npx --yes @ai-by-design/primitiv@1.5.2 verify --strict` (or your chosen version).
#### Verify exit codes
| Code | Meaning |
|------|---------|
| `0` | Contract is fresh, conflicts resolved, no token misuse |
| `1` | Stale contract OR token misuse detected (warning level, default) |
| `2` | Unresolved conflicts, OR `--strict` escalation of stale / token misuse / failed source scans |
| `3` | No config or contract found, or the contract is malformed |
#### Build exit codes
| Code | Meaning |
|------|---------|
| `0` | Contract written — a failed optional source is recorded in the contract's `sourceStatuses` and the build continues |
| `1` | Build failed (bad config, or the `governance.sourceOfTruth` / a source marked `optional: false` failed to scan — no contract written) |
| `2` | Pending conflicts with `governance.onConflict: "error"` — the contract is still written first |
#### Failed sources
Every build records a `sourceStatuses` field on the contract — `ok` (with token/component counts), `failed` (with a sanitized error), or `skipped` (not configured) per source — so consumers can distinguish "not configured" from "configured but failed". When a remote source (Figma, Storybook) is unreachable, the build warns, records the failure, and continues; `verify` skips drift reporting for that source's entries instead of reporting them all as removed. The one exception: if the failed source **is** your `governance.sourceOfTruth` (or is marked `optional: false`), the build hard-fails and writes nothing — conflict resolution without its authority would be worse than no contract.
The workflow is idempotent — re-running `primitiv init` refreshes the block between `# ` markers, preserving any content you've added outside them.
### Token misuse
Every `primitiv build` and `primitiv verify` scans your source for hardcoded literals that bypass the contract — Tailwind arbitrary values like `bg-[#ff0000]` or `p-[8px]`. When a literal matches the value of a token already in your contract, the violation is smart-matched to a suggested replacement:
```
$ primitiv verify
! 3 token misuses detected:
- src/components/Button.tsx:14 bg-[#ff0000] → use --color-destructive
- src/components/Card.tsx:7 p-[8px] → use --spacing-2
- src/components/Hero.tsx:22 text-[#abcdef] → no matching token
```
Violations surface through the `get_violations` MCP tool so agents see the same list before generating UI. Suppress an intentional one-off with a directive on the line above:
```tsx
// primitiv-ignore-next-line
// intentional — promo banner
```
### Rationale layer
A YAML sidecar (`primitiv.rationale.yml`) lets you annotate tokens with **why** they exist, **when** to use them, whether they're **deprecated**, and what **alternatives** to prefer:
```yaml
# primitiv.rationale.yml
color.brand:
why: "Primary action colour across all surfaces"
when: "CTAs, primary navigation, focus rings"
color.primary.old:
deprecated: true
why: "Replaced by color.brand in v2 — old hue failed WCAG AA on dark surfaces"
alternatives: [color.brand]
```
Agents reading the contract via MCP prefer annotated tokens, refuse deprecated ones, and surface the reasoning when asked. The rationale layer is authored by humans — it documents intent, not structure.
### Configuration
```js
// primitiv.config.js
module.exports = {
sources: {
codebase: {
root: "./src",
patterns: ["**/*.css", "**/*.ts", "**/*.tsx"],
ignore: ["node_modules", "dist", ".next"]
},
// figma: {
// token: process.env.FIGMA_ACCESS_TOKEN,
// fileId: "your-figma-file-id"
// // optional: false, // fail the build when this source can't be scanned
// },
// storybook: {
// url: "http://localhost:6006"
// }
},
governance: {
sourceOfTruth: "codebase", // "codebase" | "figma" | "storybook" | "manual"
// "warn": record conflicts as pending; build succeeds (default)
// "error": write the contract, then fail the build (exit 2) while conflicts are pending
// "auto-resolve": conflicts the sourceOfTruth decides are marked resolved; standoffs stay pending
// Same-source conflicts (one source defining a token twice with different values) are never
// auto-resolved — a source can't arbitrate itself — so they stay pending under every value.
onConflict: "warn"
},
output: {
path: "./primitiv.contract.json"
}
}
```
---
## Contributing
Bug reports, feature ideas, and PRs are welcome. See [CONTRIBUTING.md](./CONTRIBUTING.md) for local setup, the architecture map, commit conventions, and the release flow. Participation is governed by the [Code of Conduct](./CODE_OF_CONDUCT.md). For security reports, see [SECURITY.md](./SECURITY.md) — please don't open a public issue.
---
## Design principles
**Source-agnostic** — Primitiv does not assume any particular toolchain. Sources are configured via adapters, and new adapters can be added for any system that holds design-relevant information. Works with Figma, Storybook, token files, raw codebase — or any combination.
**Contract over documentation** — The output is a machine-readable contract, not human-readable documentation. It is designed to be consumed by agents, not read by people.
**Active reconciliation, not retrieval** — Primitiv does not answer questions about what exists in your codebase. It resolves conflicts between sources and produces something authoritative. The distinction matters: retrieval gives you data, reconciliation gives you truth.
**Inferred before prescribed** — Primitiv surfaces the rules your codebase is already following before asking you to write any. The inferred rules are a starting point, not a final answer.
**Governance is explicit** — When sources conflict, the resolution is not silent. Conflicts are surfaced, logged, and resolved according to rules you define. Nothing is resolved by guessing.
**Local-first and private** — Primitiv runs entirely on your machine. Your codebase is never sent to an external service. The contract is a local file; the MCP server is a local process.
**Incrementally adoptable** — Start with a single source. Add more as needed. The contract remains valid at any level of completeness.
## Roadmap
- [x] Codebase scanner (CSS variables, TypeScript tokens, React components)
- [x] Contract builder with conflict detection
- [x] MCP server with 6 read-only tools
- [x] `primitiv init` — project detection and config generation across Next, Nuxt, Astro, SvelteKit, Remix, Expo, Qwik, Vite, Solid, and React
- [x] Package-manager auto-detection — `init` reads the lockfile and writes `bunx` / `pnpm dlx` / `yarn dlx` / `npx` into the generated MCP config
- [x] Inferred rules — extract design rules from actual codebase patterns
- [x] AGENTS.md / CLAUDE.md integration — `primitiv init` writes agent instructions so `get_design_context` is called before any UI build without manual prompting
- [x] Project-scoped MCP config — `primitiv init` writes a project-level MCP config so the server is scoped to the current project, not a global user-level server
- [x] `build-component` skill — `primitiv init` installs a Claude Code slash command that queries the contract before building any UI component
- [x] Remediation steps on conflicts — conflicts include a `suggestedFix` and `actionable` flag so agents know exactly what to do, not just what's wrong
- [x] Published to npm — available as `@ai-by-design/primitiv`
- [x] Figma source adapter — scan Figma Variables and components via the Figma REST API
- [x] Storybook source adapter — scan components and variants via the Storybook manifest
- [x] Source provenance — every token and component in the contract traces back to its origin (file, line number, Figma variable ID, Storybook story ID)
- [x] Token-misuse detection — `build` and `verify` lint for hardcoded Tailwind arbitrary values and smart-match them to existing tokens; exposed via the `get_violations` MCP tool
- [x] Rationale layer — annotate tokens with `why` / `when` / `deprecated` / `alternatives` via `primitiv.rationale.yml`; agents prefer annotated tokens and refuse deprecated ones
- [x] CI enforcement — `primitiv init` auto-installs a GitHub Actions workflow that runs `verify --strict` on every PR, failing on conflicts, token misuse, or stale contracts
- [x] Structural (AST) extraction — the codebase scanner parses TypeScript/JSX with an AST instead of regex: every component is captured at its definition site (incl. `forwardRef` / `memo` / `styled` / factory wrappers) and classified by `kind`; theme tokens are extracted across nine categories (colors, spacing, sizes, typography, border-radius, shadows, z-index, breakpoints, motion) with Tailwind class strings rejected and scale aliases resolved; component-internal CSS variables are separated from the global design-token scale
- [x] Theme modes — theme-variant values (`.dark`, `[data-theme="dim"]`, `@media (prefers-color-scheme: …)`) are captured as a token's `modes` map instead of being dropped or duplicated into `-dark` names; tokens defined only under a theme scope still enter the contract
- [x] Same-source redefinition surfacing — a token defined twice within one source with two different values becomes a pending conflict (both provenances, a `suggestedFix` naming each `file:line`) instead of silently keeping one; never auto-resolved
- [ ] Token relationships — document how tokens relate and what constraints exist between them
## Part of a larger system
Primitiv is the contract layer. It works alongside [Design-workflow](https://github.com/AI-by-design/Design-workflow) — a build system for going from idea to working product with agents. Design-workflow gives agents the process. Primitiv gives them the source of truth.
## License
Apache-2.0