@layoutdesign/context 0.1.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +424 -0
- package/dist/bin/cli.d.ts +3 -0
- package/dist/bin/cli.d.ts.map +1 -0
- package/dist/bin/cli.js +57 -0
- package/dist/bin/cli.js.map +1 -0
- package/dist/src/cli/import-zip.d.ts +2 -0
- package/dist/src/cli/import-zip.d.ts.map +1 -0
- package/dist/src/cli/import-zip.js +156 -0
- package/dist/src/cli/import-zip.js.map +1 -0
- package/dist/src/cli/init.d.ts +4 -0
- package/dist/src/cli/init.d.ts.map +1 -0
- package/dist/src/cli/init.js +104 -0
- package/dist/src/cli/init.js.map +1 -0
- package/dist/src/cli/install.d.ts +5 -0
- package/dist/src/cli/install.d.ts.map +1 -0
- package/dist/src/cli/install.js +192 -0
- package/dist/src/cli/install.js.map +1 -0
- package/dist/src/cli/list.d.ts +2 -0
- package/dist/src/cli/list.d.ts.map +1 -0
- package/dist/src/cli/list.js +36 -0
- package/dist/src/cli/list.js.map +1 -0
- package/dist/src/cli/serve.d.ts +2 -0
- package/dist/src/cli/serve.d.ts.map +1 -0
- package/dist/src/cli/serve.js +9 -0
- package/dist/src/cli/serve.js.map +1 -0
- package/dist/src/cli/use.d.ts +2 -0
- package/dist/src/cli/use.d.ts.map +1 -0
- package/dist/src/cli/use.js +54 -0
- package/dist/src/cli/use.js.map +1 -0
- package/dist/src/compliance/checker.d.ts +23 -0
- package/dist/src/compliance/checker.d.ts.map +1 -0
- package/dist/src/compliance/checker.js +31 -0
- package/dist/src/compliance/checker.js.map +1 -0
- package/dist/src/compliance/rules.d.ts +11 -0
- package/dist/src/compliance/rules.d.ts.map +1 -0
- package/dist/src/compliance/rules.js +147 -0
- package/dist/src/compliance/rules.js.map +1 -0
- package/dist/src/index.d.ts +9 -0
- package/dist/src/index.d.ts.map +1 -0
- package/dist/src/index.js +6 -0
- package/dist/src/index.js.map +1 -0
- package/dist/src/kit/loader.d.ts +16 -0
- package/dist/src/kit/loader.d.ts.map +1 -0
- package/dist/src/kit/loader.js +98 -0
- package/dist/src/kit/loader.js.map +1 -0
- package/dist/src/kit/parser.d.ts +21 -0
- package/dist/src/kit/parser.d.ts.map +1 -0
- package/dist/src/kit/parser.js +98 -0
- package/dist/src/kit/parser.js.map +1 -0
- package/dist/src/kit/registry.d.ts +4 -0
- package/dist/src/kit/registry.d.ts.map +1 -0
- package/dist/src/kit/registry.js +91 -0
- package/dist/src/kit/registry.js.map +1 -0
- package/dist/src/kit/types.d.ts +51 -0
- package/dist/src/kit/types.d.ts.map +1 -0
- package/dist/src/kit/types.js +11 -0
- package/dist/src/kit/types.js.map +1 -0
- package/dist/src/mcp/server.d.ts +6 -0
- package/dist/src/mcp/server.d.ts.map +1 -0
- package/dist/src/mcp/server.js +56 -0
- package/dist/src/mcp/server.js.map +1 -0
- package/dist/src/mcp/tools/check-compliance.d.ts +16 -0
- package/dist/src/mcp/tools/check-compliance.d.ts.map +1 -0
- package/dist/src/mcp/tools/check-compliance.js +44 -0
- package/dist/src/mcp/tools/check-compliance.js.map +1 -0
- package/dist/src/mcp/tools/design-in-figma.d.ts +24 -0
- package/dist/src/mcp/tools/design-in-figma.d.ts.map +1 -0
- package/dist/src/mcp/tools/design-in-figma.js +202 -0
- package/dist/src/mcp/tools/design-in-figma.js.map +1 -0
- package/dist/src/mcp/tools/get-component.d.ts +16 -0
- package/dist/src/mcp/tools/get-component.d.ts.map +1 -0
- package/dist/src/mcp/tools/get-component.js +52 -0
- package/dist/src/mcp/tools/get-component.js.map +1 -0
- package/dist/src/mcp/tools/get-design-system.d.ts +16 -0
- package/dist/src/mcp/tools/get-design-system.d.ts.map +1 -0
- package/dist/src/mcp/tools/get-design-system.js +51 -0
- package/dist/src/mcp/tools/get-design-system.js.map +1 -0
- package/dist/src/mcp/tools/get-screenshots.d.ts +23 -0
- package/dist/src/mcp/tools/get-screenshots.d.ts.map +1 -0
- package/dist/src/mcp/tools/get-screenshots.js +78 -0
- package/dist/src/mcp/tools/get-screenshots.js.map +1 -0
- package/dist/src/mcp/tools/get-tokens.d.ts +20 -0
- package/dist/src/mcp/tools/get-tokens.d.ts.map +1 -0
- package/dist/src/mcp/tools/get-tokens.js +50 -0
- package/dist/src/mcp/tools/get-tokens.js.map +1 -0
- package/dist/src/mcp/tools/list-components.d.ts +11 -0
- package/dist/src/mcp/tools/list-components.d.ts.map +1 -0
- package/dist/src/mcp/tools/list-components.js +38 -0
- package/dist/src/mcp/tools/list-components.js.map +1 -0
- package/dist/src/mcp/tools/preview.d.ts +21 -0
- package/dist/src/mcp/tools/preview.d.ts.map +1 -0
- package/dist/src/mcp/tools/preview.js +63 -0
- package/dist/src/mcp/tools/preview.js.map +1 -0
- package/dist/src/mcp/tools/push-to-figma.d.ts +24 -0
- package/dist/src/mcp/tools/push-to-figma.d.ts.map +1 -0
- package/dist/src/mcp/tools/push-to-figma.js +101 -0
- package/dist/src/mcp/tools/push-to-figma.js.map +1 -0
- package/dist/src/mcp/tools/update-tokens.d.ts +21 -0
- package/dist/src/mcp/tools/update-tokens.d.ts.map +1 -0
- package/dist/src/mcp/tools/update-tokens.js +187 -0
- package/dist/src/mcp/tools/update-tokens.js.map +1 -0
- package/dist/src/mcp/tools/url-to-figma.d.ts +29 -0
- package/dist/src/mcp/tools/url-to-figma.d.ts.map +1 -0
- package/dist/src/mcp/tools/url-to-figma.js +103 -0
- package/dist/src/mcp/tools/url-to-figma.js.map +1 -0
- package/dist/src/preview/server.d.ts +15 -0
- package/dist/src/preview/server.d.ts.map +1 -0
- package/dist/src/preview/server.js +146 -0
- package/dist/src/preview/server.js.map +1 -0
- package/dist/src/preview/static/index.html +493 -0
- package/dist/src/preview/transpile.d.ts +10 -0
- package/dist/src/preview/transpile.d.ts.map +1 -0
- package/dist/src/preview/transpile.js +40 -0
- package/dist/src/preview/transpile.js.map +1 -0
- package/dist/src/preview/ws.d.ts +17 -0
- package/dist/src/preview/ws.d.ts.map +1 -0
- package/dist/src/preview/ws.js +66 -0
- package/dist/src/preview/ws.js.map +1 -0
- package/kits/linear-lite/DESIGN.md +421 -0
- package/kits/linear-lite/kit.json +12 -0
- package/kits/linear-lite/tokens.css +46 -0
- package/kits/linear-lite/tokens.json +47 -0
- package/kits/notion-lite/DESIGN.md +528 -0
- package/kits/notion-lite/kit.json +12 -0
- package/kits/notion-lite/tokens.css +50 -0
- package/kits/notion-lite/tokens.json +51 -0
- package/kits/stripe-lite/DESIGN.md +539 -0
- package/kits/stripe-lite/kit.json +12 -0
- package/kits/stripe-lite/tokens.css +57 -0
- package/kits/stripe-lite/tokens.json +58 -0
- package/package.json +63 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 SuperDuper UI
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,424 @@
|
|
|
1
|
+
# @layoutdesign/context
|
|
2
|
+
|
|
3
|
+
**Give your AI agent a design system in one command.**
|
|
4
|
+
|
|
5
|
+
[](https://www.npmjs.com/package/@layoutdesign/context)
|
|
6
|
+
[](./LICENSE)
|
|
7
|
+
[](https://nodejs.org)
|
|
8
|
+
|
|
9
|
+
An MCP server and CLI that gives AI coding agents structured design system context — tokens, components, rules — so they produce on-brand UI instead of generic code.
|
|
10
|
+
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
## Quick Start
|
|
14
|
+
|
|
15
|
+
Get set up in 60 seconds.
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
# Initialise your project with a starter kit
|
|
19
|
+
npx @layoutdesign/context init --kit linear-lite
|
|
20
|
+
|
|
21
|
+
# Auto-configure your AI coding agent
|
|
22
|
+
npx @layoutdesign/context install
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
That's it. The `install` command detects Claude Code, Cursor, and Windsurf automatically and configures the MCP server for the current project.
|
|
26
|
+
|
|
27
|
+
Your agent now has access to your full design system on every request.
|
|
28
|
+
|
|
29
|
+
---
|
|
30
|
+
|
|
31
|
+
## What It Does
|
|
32
|
+
|
|
33
|
+
AI coding agents don't know your design system. They produce UI that looks generic, uses hardcoded colours, ignores your spacing scale, and references components that don't exist.
|
|
34
|
+
|
|
35
|
+
`@layoutdesign/context` solves this by exposing your design system — tokens, components, rules — as MCP tools. Your agent calls `get_design_system` before writing UI, `get_tokens` when it needs exact values, and `check_compliance` before it finishes. The result is on-brand code from the first attempt.
|
|
36
|
+
|
|
37
|
+
---
|
|
38
|
+
|
|
39
|
+
## MCP Tools
|
|
40
|
+
|
|
41
|
+
Ten tools are registered with the MCP server automatically.
|
|
42
|
+
|
|
43
|
+
| Tool | Description |
|
|
44
|
+
|------|-------------|
|
|
45
|
+
| `get_design_system` | Returns the full DESIGN.md, or a filtered section (colours, typography, spacing, components). Use this before writing any UI. |
|
|
46
|
+
| `get_tokens` | Returns design tokens in CSS custom properties, W3C DTCG JSON, or Tailwind config format. |
|
|
47
|
+
| `get_component` | Returns the spec and code example for a named component. |
|
|
48
|
+
| `list_components` | Lists all components defined in the active kit. |
|
|
49
|
+
| `check_compliance` | Validates a code snippet against the design system — flags hardcoded colours, bad spacing, unknown tokens, and unrecognised components. |
|
|
50
|
+
| `preview` | Pushes a component to the local live preview canvas at `localhost:4321`. Requires the preview server to be running. |
|
|
51
|
+
| `push_to_figma` | Bridges to the Figma MCP server to create an editable Figma frame from component code. Requires Figma MCP to be configured separately. |
|
|
52
|
+
| `design_in_figma` | Takes a natural language prompt (e.g. "A pricing card with 3 tiers") and returns design tokens, component specs, and step-by-step instructions for calling Figma MCP's `generate_figma_design`. Enables AI agents to design in Figma before writing code. Inputs: `prompt` (required), `fileKey` (optional), `viewports` (optional: desktop/tablet/mobile). |
|
|
53
|
+
| `url_to_figma` | Captures a live website URL as editable Figma frames with auto-layout. Inputs: `url`, `viewports`, `outputMode` (newFile/existingFile/clipboard), `fileKey`. Requires both Figma MCP and Playwright MCP servers. |
|
|
54
|
+
| `update_tokens` | Updates token values in `tokens.css`, `tokens.json`, and `DESIGN.md` simultaneously. Use when tweaking colours, spacing, or other tokens without re-extracting. Keeps the entire design system consistent. |
|
|
55
|
+
|
|
56
|
+
---
|
|
57
|
+
|
|
58
|
+
## CLI Commands
|
|
59
|
+
|
|
60
|
+
| Command | Description |
|
|
61
|
+
|---------|-------------|
|
|
62
|
+
| `init` | Initialise `.layout/` in the current directory, optionally with a starter kit. |
|
|
63
|
+
| `init --kit <name>` | Initialise with a specific kit (e.g. `linear-lite`). |
|
|
64
|
+
| `serve` | Start the MCP server. This is what your AI agent connects to. |
|
|
65
|
+
| `install` | Auto-configure MCP settings for Claude Code, Cursor, and Windsurf in one step. |
|
|
66
|
+
| `install --target <tool>` | Target a specific tool: `claude`, `cursor`, or `windsurf`. |
|
|
67
|
+
| `install --global` | Install globally so the MCP server is available in all projects (Claude Code only). |
|
|
68
|
+
| `list` | List all available kits (free and pro). |
|
|
69
|
+
| `use <kit>` | Switch the active kit in an existing `.layout/` directory. |
|
|
70
|
+
| `import <path>` | Import a design system bundle exported from Layout (`.zip`). |
|
|
71
|
+
|
|
72
|
+
**Examples:**
|
|
73
|
+
|
|
74
|
+
```bash
|
|
75
|
+
# Start with the Linear-inspired dark kit
|
|
76
|
+
npx @layoutdesign/context init --kit linear-lite
|
|
77
|
+
|
|
78
|
+
# Start with a blank template and write your own DESIGN.md
|
|
79
|
+
npx @layoutdesign/context init
|
|
80
|
+
|
|
81
|
+
# Auto-configure MCP settings for all supported editors
|
|
82
|
+
npx @layoutdesign/context install
|
|
83
|
+
|
|
84
|
+
# Auto-configure for Claude Code only
|
|
85
|
+
npx @layoutdesign/context install --target claude
|
|
86
|
+
|
|
87
|
+
# Install globally (available in all projects — each project uses its own .layout/)
|
|
88
|
+
npx @layoutdesign/context install --global
|
|
89
|
+
|
|
90
|
+
# Switch to a different kit
|
|
91
|
+
npx @layoutdesign/context use stripe-lite
|
|
92
|
+
|
|
93
|
+
# See all available kits
|
|
94
|
+
npx @layoutdesign/context list
|
|
95
|
+
|
|
96
|
+
# Import a bundle from Layout
|
|
97
|
+
npx @layoutdesign/context import ./my-design-export.zip
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
---
|
|
101
|
+
|
|
102
|
+
## Available Kits
|
|
103
|
+
|
|
104
|
+
Three free starter kits are included. Premium kits are available at [layout.design/kits](https://layout.design/kits).
|
|
105
|
+
|
|
106
|
+
### Free
|
|
107
|
+
|
|
108
|
+
| Kit | Aesthetic | Description |
|
|
109
|
+
|-----|-----------|-------------|
|
|
110
|
+
| `linear-lite` | Dark, minimal, developer-focused | Developer tool design system inspired by Linear |
|
|
111
|
+
| `stripe-lite` | Light, clean, high-trust | Payment UI design system inspired by Stripe |
|
|
112
|
+
| `notion-lite` | Light, content-first, block-based | Productivity design system inspired by Notion |
|
|
113
|
+
|
|
114
|
+
### Pro
|
|
115
|
+
|
|
116
|
+
| Kit | Aesthetic | Components |
|
|
117
|
+
|-----|-----------|------------|
|
|
118
|
+
| `linear` | Dark, minimal, developer-focused | 24 components, all tokens |
|
|
119
|
+
| `stripe` | Light, clean, high-trust | 20 components, all tokens |
|
|
120
|
+
| `notion` | Light, content-first, block-based | 22 components, all tokens |
|
|
121
|
+
| `revolut` | Dark fintech, data-rich | 30 components, all tokens |
|
|
122
|
+
| `airbnb` | Warm, rounded, photo-forward | 25 components, all tokens |
|
|
123
|
+
| `tiktok` | Dark, vibrant, video-first | 28 components, all tokens |
|
|
124
|
+
| `netflix` | Dark, cinematic, content-forward | 18 components, all tokens |
|
|
125
|
+
|
|
126
|
+
---
|
|
127
|
+
|
|
128
|
+
## Setup Guides
|
|
129
|
+
|
|
130
|
+
The easiest way to configure any supported editor is:
|
|
131
|
+
|
|
132
|
+
```bash
|
|
133
|
+
npx @layoutdesign/context install
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
This auto-detects Claude Code, Cursor, and Windsurf and configures the MCP server. For Claude Code it uses `claude mcp add` (the reliable method); for Cursor and Windsurf it writes the appropriate config file.
|
|
137
|
+
|
|
138
|
+
### Per-Project vs Global
|
|
139
|
+
|
|
140
|
+
By default, the MCP server is registered **per-project**. Each project needs its own `install`.
|
|
141
|
+
|
|
142
|
+
For users working across multiple projects, install globally:
|
|
143
|
+
|
|
144
|
+
```bash
|
|
145
|
+
npx @layoutdesign/context install --global
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
The MCP server always reads `.layout/` from the current working directory, so each project uses its own design system — even with a global install.
|
|
149
|
+
|
|
150
|
+
### Manual Setup
|
|
151
|
+
|
|
152
|
+
If you prefer to configure manually:
|
|
153
|
+
|
|
154
|
+
**Claude Code** (`.claude/settings.json`):
|
|
155
|
+
|
|
156
|
+
```json
|
|
157
|
+
{
|
|
158
|
+
"mcpServers": {
|
|
159
|
+
"layout": {
|
|
160
|
+
"command": "npx",
|
|
161
|
+
"args": ["-y", "@layoutdesign/context", "serve"]
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
**Cursor** (`.cursor/mcp.json`):
|
|
168
|
+
|
|
169
|
+
```json
|
|
170
|
+
{
|
|
171
|
+
"mcpServers": {
|
|
172
|
+
"layout": {
|
|
173
|
+
"command": "npx",
|
|
174
|
+
"args": ["-y", "@layoutdesign/context", "serve"]
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
**Windsurf** (`.windsurf/mcp.json`):
|
|
181
|
+
|
|
182
|
+
```json
|
|
183
|
+
{
|
|
184
|
+
"mcpServers": {
|
|
185
|
+
"layout": {
|
|
186
|
+
"command": "npx",
|
|
187
|
+
"args": ["-y", "@layoutdesign/context", "serve"]
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
---
|
|
194
|
+
|
|
195
|
+
## Live Preview
|
|
196
|
+
|
|
197
|
+
The `preview` MCP tool and the preview canvas work together to give you visual verification without leaving your agent session.
|
|
198
|
+
|
|
199
|
+
Start the preview server alongside the MCP server:
|
|
200
|
+
|
|
201
|
+
```bash
|
|
202
|
+
npx @layoutdesign/context serve
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
The preview canvas opens at `http://localhost:4321`. It features:
|
|
206
|
+
|
|
207
|
+
- Dark UI with your active kit's design tokens applied
|
|
208
|
+
- WebSocket-powered live updates — components render as the agent pushes them
|
|
209
|
+
- Viewport toggles (mobile, tablet, desktop)
|
|
210
|
+
- Source code panel to inspect what was rendered
|
|
211
|
+
- TSX transpilation on the server — no browser-side build step
|
|
212
|
+
|
|
213
|
+
When your agent calls the `preview` tool with a component, it appears in the canvas within milliseconds. Open the canvas in a browser tab alongside your editor and you have a live design review loop without any manual copy-paste.
|
|
214
|
+
|
|
215
|
+
---
|
|
216
|
+
|
|
217
|
+
## Figma Integration
|
|
218
|
+
|
|
219
|
+
Three MCP tools bridge the gap between design and code using the [Figma MCP server](https://www.figma.com/developers/mcp): `push_to_figma`, `design_in_figma`, and `url_to_figma`.
|
|
220
|
+
|
|
221
|
+
### Setup
|
|
222
|
+
|
|
223
|
+
Add the Figma MCP server to your agent config alongside `design-context`. It uses OAuth — no API key required:
|
|
224
|
+
|
|
225
|
+
```bash
|
|
226
|
+
claude mcp add --transport http figma https://mcp.figma.com/mcp
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
Then authenticate when prompted. The Figma MCP server will open a browser tab for OAuth consent.
|
|
230
|
+
|
|
231
|
+
### Figma Tools
|
|
232
|
+
|
|
233
|
+
| Tool | What It Does |
|
|
234
|
+
|------|--------------|
|
|
235
|
+
| `push_to_figma` | Creates an editable Figma frame from component code. Returns a structured prompt for Figma MCP's `generate_figma_design`. |
|
|
236
|
+
| `design_in_figma` | Takes a natural language prompt and returns design tokens, component specs, and step-by-step instructions for `generate_figma_design`. Design in Figma before writing any code. |
|
|
237
|
+
| `url_to_figma` | Captures a live website URL as editable Figma frames with auto-layout. Requires Playwright MCP alongside Figma MCP. |
|
|
238
|
+
|
|
239
|
+
### The Closed Loop
|
|
240
|
+
|
|
241
|
+
```
|
|
242
|
+
Developer prompts AI → AI calls get_design_system → generates TSX
|
|
243
|
+
↓
|
|
244
|
+
AI calls preview → renders at localhost:4321
|
|
245
|
+
↓
|
|
246
|
+
AI calls push_to_figma → editable frame in Figma
|
|
247
|
+
↓
|
|
248
|
+
Designer reviews → AI reads changes via Figma MCP → updates code
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
When `push_to_figma` is called, it returns a structured prompt ready to pass to the Figma MCP's `generate_figma_design` tool, including your component code and the relevant design tokens extracted from the active kit.
|
|
252
|
+
|
|
253
|
+
---
|
|
254
|
+
|
|
255
|
+
## Custom Design Systems
|
|
256
|
+
|
|
257
|
+
You don't need a pre-built kit. You can write your own `DESIGN.md` and the MCP server will use it.
|
|
258
|
+
|
|
259
|
+
**1. Create the `.layout/` directory:**
|
|
260
|
+
|
|
261
|
+
```bash
|
|
262
|
+
npx @layoutdesign/context init
|
|
263
|
+
```
|
|
264
|
+
|
|
265
|
+
This creates a blank template at `.layout/DESIGN.md` for you to fill in.
|
|
266
|
+
|
|
267
|
+
**2. Edit `DESIGN.md` with your design system:**
|
|
268
|
+
|
|
269
|
+
```markdown
|
|
270
|
+
# My Design System
|
|
271
|
+
|
|
272
|
+
## Colours
|
|
273
|
+
|
|
274
|
+
| Token | Value | Usage |
|
|
275
|
+
|-------|-------|-------|
|
|
276
|
+
| --color-primary | #0F172A | Primary actions |
|
|
277
|
+
| --color-background | #FFFFFF | Page background |
|
|
278
|
+
|
|
279
|
+
## Typography
|
|
280
|
+
|
|
281
|
+
- **Font family:** Inter, system-ui, sans-serif
|
|
282
|
+
- **Base size:** 16px
|
|
283
|
+
|
|
284
|
+
## Components
|
|
285
|
+
|
|
286
|
+
### Button
|
|
287
|
+
|
|
288
|
+
Primary action button. Uses --color-primary as background.
|
|
289
|
+
```
|
|
290
|
+
|
|
291
|
+
**3. Optionally add token files:**
|
|
292
|
+
|
|
293
|
+
```
|
|
294
|
+
.layout/
|
|
295
|
+
├── kit.json # Metadata (name, version, description)
|
|
296
|
+
├── DESIGN.md # Human-readable design system spec
|
|
297
|
+
├── tokens.css # CSS custom properties
|
|
298
|
+
├── tokens.json # W3C DTCG tokens (optional)
|
|
299
|
+
└── tailwind.config.js # Tailwind theme extension (optional)
|
|
300
|
+
```
|
|
301
|
+
|
|
302
|
+
**4. Start the MCP server:**
|
|
303
|
+
|
|
304
|
+
```bash
|
|
305
|
+
npx @layoutdesign/context serve
|
|
306
|
+
```
|
|
307
|
+
|
|
308
|
+
The server reads whatever is in `.layout/` — no configuration needed.
|
|
309
|
+
|
|
310
|
+
---
|
|
311
|
+
|
|
312
|
+
## On-Disk Structure
|
|
313
|
+
|
|
314
|
+
```
|
|
315
|
+
.layout/
|
|
316
|
+
├── kit.json # Kit metadata (name, version, tier, component count)
|
|
317
|
+
├── DESIGN.md # Full design system spec — this is what agents read
|
|
318
|
+
├── tokens.css # CSS custom properties for all tokens
|
|
319
|
+
├── tokens.json # W3C DTCG tokens.json (for tooling integration)
|
|
320
|
+
└── tailwind.config.js # Tailwind theme config matching the token set
|
|
321
|
+
```
|
|
322
|
+
|
|
323
|
+
All files are plain text and checked into version control. Your whole team gets the same design context.
|
|
324
|
+
|
|
325
|
+
---
|
|
326
|
+
|
|
327
|
+
## Layout Integration
|
|
328
|
+
|
|
329
|
+
[Layout](https://layout.design) extracts design systems from Figma files and live websites, then exports them as a `.zip` bundle ready for import.
|
|
330
|
+
|
|
331
|
+
Use Layout to:
|
|
332
|
+
|
|
333
|
+
- Extract tokens and components from an existing Figma file
|
|
334
|
+
- Scrape a live website's design system (colours, typography, spacing, components)
|
|
335
|
+
- Generate a structured `DESIGN.md` using Claude
|
|
336
|
+
- Export a bundle containing `DESIGN.md`, `tokens.css`, `tokens.json`, and `tailwind.config.js`
|
|
337
|
+
|
|
338
|
+
### Importing a Layout Export
|
|
339
|
+
|
|
340
|
+
```bash
|
|
341
|
+
npx @layoutdesign/context import ./my-design-export.zip
|
|
342
|
+
```
|
|
343
|
+
|
|
344
|
+
This extracts the bundle into `.layout/` and automatically merges design system rules into your project's root `CLAUDE.md` (using HTML comment markers for idempotent updates). Re-importing replaces the previous section cleanly.
|
|
345
|
+
|
|
346
|
+
After importing, run `npx @layoutdesign/context install` to connect the MCP server.
|
|
347
|
+
|
|
348
|
+
---
|
|
349
|
+
|
|
350
|
+
## Compliance Checker
|
|
351
|
+
|
|
352
|
+
The `check_compliance` tool validates a code snippet against your active kit and returns a structured result.
|
|
353
|
+
|
|
354
|
+
### Rules
|
|
355
|
+
|
|
356
|
+
| Rule | Severity | What It Catches |
|
|
357
|
+
|------|----------|-----------------|
|
|
358
|
+
| `hardcoded-colours` | Error | Hex values (`#fff`, `#6366F1`) and `rgb()`/`rgba()` that should reference tokens |
|
|
359
|
+
| `hardcoded-spacing` | Warning | `margin: 12px` and similar pixel values that should use the spacing scale |
|
|
360
|
+
| `missing-token-reference` | Warning | `var(--unknown-token)` calls not present in `tokens.css` |
|
|
361
|
+
| `unknown-component` | Info | JSX components not listed in the kit's component inventory |
|
|
362
|
+
|
|
363
|
+
### Response Shape
|
|
364
|
+
|
|
365
|
+
```json
|
|
366
|
+
{
|
|
367
|
+
"passed": false,
|
|
368
|
+
"issues": [
|
|
369
|
+
{
|
|
370
|
+
"rule": "hardcoded-colours",
|
|
371
|
+
"severity": "error",
|
|
372
|
+
"line": 4,
|
|
373
|
+
"message": "Hardcoded colour '#6366F1' — use var(--color-accent) instead"
|
|
374
|
+
},
|
|
375
|
+
{
|
|
376
|
+
"rule": "unknown-component",
|
|
377
|
+
"severity": "info",
|
|
378
|
+
"line": 12,
|
|
379
|
+
"message": "Component 'Tooltip' is not in the active kit's component list"
|
|
380
|
+
}
|
|
381
|
+
]
|
|
382
|
+
}
|
|
383
|
+
```
|
|
384
|
+
|
|
385
|
+
### Recommended Agent Workflow
|
|
386
|
+
|
|
387
|
+
```
|
|
388
|
+
1. get_design_system — read the spec before writing
|
|
389
|
+
2. get_tokens(format: "css") — get exact token values
|
|
390
|
+
3. [write component code]
|
|
391
|
+
4. check_compliance(code) — verify before finishing
|
|
392
|
+
5. preview(code) — visual check
|
|
393
|
+
```
|
|
394
|
+
|
|
395
|
+
---
|
|
396
|
+
|
|
397
|
+
## Contributing
|
|
398
|
+
|
|
399
|
+
PRs are welcome. The project is MIT licensed.
|
|
400
|
+
|
|
401
|
+
```bash
|
|
402
|
+
git clone https://github.com/uselayout/layout-context.git
|
|
403
|
+
cd context
|
|
404
|
+
npm install
|
|
405
|
+
npm run build
|
|
406
|
+
```
|
|
407
|
+
|
|
408
|
+
The source is in `src/`. Key directories:
|
|
409
|
+
|
|
410
|
+
- `src/mcp/tools/` — MCP tool handlers (one file per tool)
|
|
411
|
+
- `src/kit/` — Kit loading, parsing, and registry
|
|
412
|
+
- `src/compliance/` — Compliance rules
|
|
413
|
+
- `src/preview/` — Preview server and WebSocket handler
|
|
414
|
+
- `src/cli/` — CLI command implementations
|
|
415
|
+
|
|
416
|
+
---
|
|
417
|
+
|
|
418
|
+
## License
|
|
419
|
+
|
|
420
|
+
MIT — see [LICENSE](./LICENSE).
|
|
421
|
+
|
|
422
|
+
---
|
|
423
|
+
|
|
424
|
+
Powered by [Layout](https://layout.design).
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../../bin/cli.ts"],"names":[],"mappings":""}
|
package/dist/bin/cli.js
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { Command } from "commander";
|
|
3
|
+
import { createRequire } from "node:module";
|
|
4
|
+
import { initCommand } from "../src/cli/init.js";
|
|
5
|
+
import { serveCommand } from "../src/cli/serve.js";
|
|
6
|
+
import { importCommand } from "../src/cli/import-zip.js";
|
|
7
|
+
import { useCommand } from "../src/cli/use.js";
|
|
8
|
+
import { listCommand } from "../src/cli/list.js";
|
|
9
|
+
import { installCommand } from "../src/cli/install.js";
|
|
10
|
+
const require = createRequire(import.meta.url);
|
|
11
|
+
const pkg = require("../../package.json");
|
|
12
|
+
const program = new Command();
|
|
13
|
+
program
|
|
14
|
+
.name("layout-context")
|
|
15
|
+
.description("Design system context for AI coding agents — MCP server + CLI")
|
|
16
|
+
.version(pkg.version);
|
|
17
|
+
program
|
|
18
|
+
.command("init")
|
|
19
|
+
.description("Scaffold a .layout/ directory in the current project")
|
|
20
|
+
.option("--kit <name>", "Start with a specific design kit")
|
|
21
|
+
.action(async (options) => {
|
|
22
|
+
await initCommand(options);
|
|
23
|
+
});
|
|
24
|
+
program
|
|
25
|
+
.command("serve")
|
|
26
|
+
.description("Start the MCP server (stdio transport)")
|
|
27
|
+
.action(async () => {
|
|
28
|
+
await serveCommand();
|
|
29
|
+
});
|
|
30
|
+
program
|
|
31
|
+
.command("import <zip-path>")
|
|
32
|
+
.description("Import a Layout export ZIP into .layout/")
|
|
33
|
+
.action(async (zipPath) => {
|
|
34
|
+
await importCommand(zipPath);
|
|
35
|
+
});
|
|
36
|
+
program
|
|
37
|
+
.command("use <kit-name>")
|
|
38
|
+
.description("Install a design kit from the registry")
|
|
39
|
+
.action(async (kitName) => {
|
|
40
|
+
await useCommand(kitName);
|
|
41
|
+
});
|
|
42
|
+
program
|
|
43
|
+
.command("list")
|
|
44
|
+
.description("Show all available design kits")
|
|
45
|
+
.action(async () => {
|
|
46
|
+
await listCommand();
|
|
47
|
+
});
|
|
48
|
+
program
|
|
49
|
+
.command("install")
|
|
50
|
+
.description("Auto-configure the MCP server for Claude Code, Cursor, or Windsurf")
|
|
51
|
+
.option("--target <tool>", "Specific tool: claude, cursor, or windsurf")
|
|
52
|
+
.option("--global", "Install globally (available in all projects, Claude Code only)")
|
|
53
|
+
.action(async (options) => {
|
|
54
|
+
await installCommand(options);
|
|
55
|
+
});
|
|
56
|
+
program.parse();
|
|
57
|
+
//# sourceMappingURL=cli.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../../bin/cli.ts"],"names":[],"mappings":";AAEA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC5C,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACjD,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACnD,OAAO,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;AACzD,OAAO,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAC/C,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACjD,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAEvD,MAAM,OAAO,GAAG,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AAC/C,MAAM,GAAG,GAAG,OAAO,CAAC,oBAAoB,CAAwB,CAAC;AAEjE,MAAM,OAAO,GAAG,IAAI,OAAO,EAAE,CAAC;AAE9B,OAAO;KACJ,IAAI,CAAC,gBAAgB,CAAC;KACtB,WAAW,CACV,+DAA+D,CAChE;KACA,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;AAExB,OAAO;KACJ,OAAO,CAAC,MAAM,CAAC;KACf,WAAW,CAAC,sDAAsD,CAAC;KACnE,MAAM,CAAC,cAAc,EAAE,kCAAkC,CAAC;KAC1D,MAAM,CAAC,KAAK,EAAE,OAAyB,EAAE,EAAE;IAC1C,MAAM,WAAW,CAAC,OAAO,CAAC,CAAC;AAC7B,CAAC,CAAC,CAAC;AAEL,OAAO;KACJ,OAAO,CAAC,OAAO,CAAC;KAChB,WAAW,CAAC,wCAAwC,CAAC;KACrD,MAAM,CAAC,KAAK,IAAI,EAAE;IACjB,MAAM,YAAY,EAAE,CAAC;AACvB,CAAC,CAAC,CAAC;AAEL,OAAO;KACJ,OAAO,CAAC,mBAAmB,CAAC;KAC5B,WAAW,CAAC,0CAA0C,CAAC;KACvD,MAAM,CAAC,KAAK,EAAE,OAAe,EAAE,EAAE;IAChC,MAAM,aAAa,CAAC,OAAO,CAAC,CAAC;AAC/B,CAAC,CAAC,CAAC;AAEL,OAAO;KACJ,OAAO,CAAC,gBAAgB,CAAC;KACzB,WAAW,CAAC,wCAAwC,CAAC;KACrD,MAAM,CAAC,KAAK,EAAE,OAAe,EAAE,EAAE;IAChC,MAAM,UAAU,CAAC,OAAO,CAAC,CAAC;AAC5B,CAAC,CAAC,CAAC;AAEL,OAAO;KACJ,OAAO,CAAC,MAAM,CAAC;KACf,WAAW,CAAC,gCAAgC,CAAC;KAC7C,MAAM,CAAC,KAAK,IAAI,EAAE;IACjB,MAAM,WAAW,EAAE,CAAC;AACtB,CAAC,CAAC,CAAC;AAEL,OAAO;KACJ,OAAO,CAAC,SAAS,CAAC;KAClB,WAAW,CAAC,oEAAoE,CAAC;KACjF,MAAM,CAAC,iBAAiB,EAAE,4CAA4C,CAAC;KACvE,MAAM,CAAC,UAAU,EAAE,gEAAgE,CAAC;KACpF,MAAM,CAAC,KAAK,EAAE,OAA8C,EAAE,EAAE;IAC/D,MAAM,cAAc,CAAC,OAAO,CAAC,CAAC;AAChC,CAAC,CAAC,CAAC;AAEL,OAAO,CAAC,KAAK,EAAE,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"import-zip.d.ts","sourceRoot":"","sources":["../../../src/cli/import-zip.ts"],"names":[],"mappings":"AA6BA,wBAAsB,aAAa,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CA8HlE"}
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import os from "node:os";
|
|
4
|
+
import { execFileSync } from "node:child_process";
|
|
5
|
+
import chalk from "chalk";
|
|
6
|
+
import { LAYOUT_DIR, KIT_MANIFEST_FILE, DESIGN_MD_FILE, TOKENS_CSS_FILE, TOKENS_JSON_FILE, TAILWIND_CONFIG_FILE, } from "../kit/types.js";
|
|
7
|
+
/** Files we look for in the extracted ZIP and copy to .layout/ */
|
|
8
|
+
const KNOWN_FILES = [
|
|
9
|
+
KIT_MANIFEST_FILE,
|
|
10
|
+
DESIGN_MD_FILE,
|
|
11
|
+
TOKENS_CSS_FILE,
|
|
12
|
+
TOKENS_JSON_FILE,
|
|
13
|
+
TAILWIND_CONFIG_FILE,
|
|
14
|
+
"CLAUDE.md",
|
|
15
|
+
"agents.md",
|
|
16
|
+
];
|
|
17
|
+
/** Markers used to find/replace the design system section in root CLAUDE.md */
|
|
18
|
+
const SECTION_START = "<!-- layout:design-system:start -->";
|
|
19
|
+
const SECTION_END = "<!-- layout:design-system:end -->";
|
|
20
|
+
export async function importCommand(zipPath) {
|
|
21
|
+
const cwd = process.cwd();
|
|
22
|
+
const resolvedPath = path.resolve(cwd, zipPath);
|
|
23
|
+
if (!fs.existsSync(resolvedPath)) {
|
|
24
|
+
console.log(chalk.red("Error:"), `File not found: ${resolvedPath}`);
|
|
25
|
+
process.exit(1);
|
|
26
|
+
}
|
|
27
|
+
if (!resolvedPath.endsWith(".zip")) {
|
|
28
|
+
console.log(chalk.red("Error:"), "Expected a .zip file.");
|
|
29
|
+
process.exit(1);
|
|
30
|
+
}
|
|
31
|
+
// Check we're in a project directory, not somewhere random
|
|
32
|
+
const projectMarkers = ["package.json", ".git", "CLAUDE.md", "Cargo.toml", "pyproject.toml", "go.mod", "Gemfile"];
|
|
33
|
+
const isProjectDir = projectMarkers.some((m) => fs.existsSync(path.join(cwd, m)));
|
|
34
|
+
if (!isProjectDir) {
|
|
35
|
+
console.log(chalk.yellow("Warning:"), "This doesn't look like a project directory.");
|
|
36
|
+
console.log(chalk.dim(` Run this from your project root (e.g. where package.json lives).`));
|
|
37
|
+
console.log(chalk.dim(` Current directory: ${cwd}`));
|
|
38
|
+
console.log();
|
|
39
|
+
}
|
|
40
|
+
// Check that unzip is available
|
|
41
|
+
try {
|
|
42
|
+
execFileSync("which", ["unzip"], { stdio: "ignore" });
|
|
43
|
+
}
|
|
44
|
+
catch {
|
|
45
|
+
console.log(chalk.red("Error:"), "The 'unzip' command is required but not found.");
|
|
46
|
+
console.log(` Install it with: ${chalk.cyan("sudo apt install unzip")} (Linux) or ${chalk.cyan("brew install unzip")} (macOS)`);
|
|
47
|
+
process.exit(1);
|
|
48
|
+
}
|
|
49
|
+
// Extract to a temp directory
|
|
50
|
+
const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "layout-import-"));
|
|
51
|
+
try {
|
|
52
|
+
execFileSync("unzip", ["-o", "-q", resolvedPath, "-d", tmpDir], {
|
|
53
|
+
stdio: "ignore",
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
catch {
|
|
57
|
+
console.log(chalk.red("Error:"), "Failed to extract ZIP file.");
|
|
58
|
+
fs.rmSync(tmpDir, { recursive: true, force: true });
|
|
59
|
+
process.exit(1);
|
|
60
|
+
}
|
|
61
|
+
// Find the root of the extracted content.
|
|
62
|
+
// Layout ZIPs may have files at root or nested in a single directory.
|
|
63
|
+
const extractedRoot = findExtractedRoot(tmpDir);
|
|
64
|
+
const targetDir = path.join(process.cwd(), LAYOUT_DIR);
|
|
65
|
+
fs.mkdirSync(targetDir, { recursive: true });
|
|
66
|
+
const imported = [];
|
|
67
|
+
for (const fileName of KNOWN_FILES) {
|
|
68
|
+
const srcPath = path.join(extractedRoot, fileName);
|
|
69
|
+
if (fs.existsSync(srcPath)) {
|
|
70
|
+
fs.copyFileSync(srcPath, path.join(targetDir, fileName));
|
|
71
|
+
imported.push(fileName);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
// Copy components directory if it exists
|
|
75
|
+
const componentsSrc = path.join(extractedRoot, "components");
|
|
76
|
+
if (fs.existsSync(componentsSrc) && fs.statSync(componentsSrc).isDirectory()) {
|
|
77
|
+
const componentsDest = path.join(targetDir, "components");
|
|
78
|
+
fs.cpSync(componentsSrc, componentsDest, { recursive: true });
|
|
79
|
+
imported.push("components/");
|
|
80
|
+
}
|
|
81
|
+
// Copy cursor rules if present
|
|
82
|
+
const cursorSrc = path.join(extractedRoot, ".cursor");
|
|
83
|
+
if (fs.existsSync(cursorSrc) && fs.statSync(cursorSrc).isDirectory()) {
|
|
84
|
+
const cursorDest = path.join(targetDir, ".cursor");
|
|
85
|
+
fs.cpSync(cursorSrc, cursorDest, { recursive: true });
|
|
86
|
+
imported.push(".cursor/");
|
|
87
|
+
}
|
|
88
|
+
// Clean up temp directory
|
|
89
|
+
fs.rmSync(tmpDir, { recursive: true, force: true });
|
|
90
|
+
if (imported.length === 0) {
|
|
91
|
+
console.log(chalk.yellow("Warning:"), "No recognised design system files found in the ZIP.");
|
|
92
|
+
console.log(` Expected files: ${KNOWN_FILES.join(", ")}`);
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
console.log(chalk.green("✓"), `Imported ${imported.length} item${imported.length === 1 ? "" : "s"} into .layout/:`);
|
|
96
|
+
console.log();
|
|
97
|
+
for (const file of imported) {
|
|
98
|
+
console.log(` ${chalk.dim("•")} ${file}`);
|
|
99
|
+
}
|
|
100
|
+
// Merge design system rules into root CLAUDE.md
|
|
101
|
+
const mergedClaudeMd = mergeIntoRootClaudeMd();
|
|
102
|
+
if (mergedClaudeMd) {
|
|
103
|
+
console.log(` ${chalk.dim("•")} ${mergedClaudeMd}`);
|
|
104
|
+
}
|
|
105
|
+
console.log();
|
|
106
|
+
console.log(`Run ${chalk.cyan("npx @layoutdesign/context install")} to connect the MCP server.`);
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* Merge the .layout/CLAUDE.md content into the project's root CLAUDE.md.
|
|
110
|
+
* Uses HTML comment markers so re-imports replace the previous section.
|
|
111
|
+
* Returns a description string for the import log, or null if nothing was merged.
|
|
112
|
+
*/
|
|
113
|
+
function mergeIntoRootClaudeMd() {
|
|
114
|
+
const layoutClaudeMd = path.join(process.cwd(), LAYOUT_DIR, "CLAUDE.md");
|
|
115
|
+
if (!fs.existsSync(layoutClaudeMd))
|
|
116
|
+
return null;
|
|
117
|
+
const designSection = fs.readFileSync(layoutClaudeMd, "utf-8").trim();
|
|
118
|
+
if (!designSection)
|
|
119
|
+
return null;
|
|
120
|
+
const wrappedSection = `${SECTION_START}\n${designSection}\n${SECTION_END}`;
|
|
121
|
+
const rootClaudeMd = path.join(process.cwd(), "CLAUDE.md");
|
|
122
|
+
if (fs.existsSync(rootClaudeMd)) {
|
|
123
|
+
const existing = fs.readFileSync(rootClaudeMd, "utf-8");
|
|
124
|
+
// Replace existing section if present
|
|
125
|
+
const startIdx = existing.indexOf(SECTION_START);
|
|
126
|
+
const endIdx = existing.indexOf(SECTION_END);
|
|
127
|
+
if (startIdx !== -1 && endIdx !== -1) {
|
|
128
|
+
const before = existing.slice(0, startIdx);
|
|
129
|
+
const after = existing.slice(endIdx + SECTION_END.length);
|
|
130
|
+
fs.writeFileSync(rootClaudeMd, before + wrappedSection + after);
|
|
131
|
+
return "CLAUDE.md (updated design system section)";
|
|
132
|
+
}
|
|
133
|
+
// Append to end
|
|
134
|
+
const separator = existing.endsWith("\n") ? "\n" : "\n\n";
|
|
135
|
+
fs.writeFileSync(rootClaudeMd, existing + separator + wrappedSection + "\n");
|
|
136
|
+
return "CLAUDE.md (appended design system section)";
|
|
137
|
+
}
|
|
138
|
+
// No root CLAUDE.md — create one
|
|
139
|
+
fs.writeFileSync(rootClaudeMd, wrappedSection + "\n");
|
|
140
|
+
return "CLAUDE.md (created with design system section)";
|
|
141
|
+
}
|
|
142
|
+
/**
|
|
143
|
+
* If the ZIP contains a single top-level directory, return that.
|
|
144
|
+
* Otherwise return the tmpDir itself.
|
|
145
|
+
*/
|
|
146
|
+
function findExtractedRoot(tmpDir) {
|
|
147
|
+
const entries = fs.readdirSync(tmpDir).filter((e) => !e.startsWith("."));
|
|
148
|
+
if (entries.length === 1) {
|
|
149
|
+
const single = path.join(tmpDir, entries[0]);
|
|
150
|
+
if (fs.statSync(single).isDirectory()) {
|
|
151
|
+
return single;
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
return tmpDir;
|
|
155
|
+
}
|
|
156
|
+
//# sourceMappingURL=import-zip.js.map
|