ds-context

Модель плохо следует дизайн-системе ещё и потому, что ей отдают плохой контекст: простыню доков, половину Storybook и ни одного антипаттерна. ds-context собирает из локальных файлов короткий пакет и показывает, сколько токенов стоит каждая стратегия упаковки.

Зачем

Агент путает систему не только из-за слабой модели. Ему часто отдают либо ничего, либо весь Storybook сразу. В первом случае он рисует свой Button. Во втором тонет в токенах и примерах, которые не относятся к задаче.

ds-context стоит перед моделью. Он собирает контекст, который потом можно отдать Claude или Codex и проверить в ds-eval.

Вход

MVP читает папку. Он не открывает Storybook и не ходит в Figma API. Файл из Figma попадает сюда уже как markdown, например после figma-to-design-md. Фикстура Acme — пять файлов:

fixtures/acme/
  tokens.css
  components.json
  patterns.md
  anti-patterns.md
  examples.md
  dist/
    design-system.md
    components.md
    patterns.md
    anti-patterns.md
    examples.md
    manifest.json

tokens.css — шкала, с которой потом сверяется починка.

:root {
  --color-accent: #8B5CF6;
  --color-text: #212223;
  --color-surface: #ffffff;
  --space-2: 8px;
  --space-3: 12px;
  --space-4: 16px;
  --radius-2: 8px;
  --font-size-body: 14px;
}

components.json — имя, пропсы, пример и чего избегать. Пример попадает в пакет только в стратегии full.

[
  {
    "name": "Button",
    "props": ["variant", "size", "children"],
    "example": "<Button variant=\"primary\">Save</Button>",
    "avoid": "A raw <button> with an inline background."
  },
  {
    "name": "Input",
    "props": ["label", "hint", "error"],
    "example": "<Input label=\"Email\" />",
    "avoid": "An unlabeled <input>."
  },
  {
    "name": "Dialog",
    "props": ["open", "title", "onClose"],
    "example": "<Dialog open title=\"Delete account\" />",
    "avoid": "A <div className=\"modal\"> with a hand-rolled overlay."
  },
  {
    "name": "Select",
    "props": ["label", "options", "value"],
    "example": "<Select label=\"Country\" options={countries} />",
    "avoid": "A native <select> when the system Select exists."
  }
]

Три markdown-файла задают паттерны, запреты и короткие примеры экранов.

# Patterns

## Destructive confirm
Ask for confirmation before a delete. Use Dialog, name the object, and make the confirm button the danger variant.

## Empty state
Show one next action. Do not invent a second illustration style.

## Settings
Group fields by task. Use Input with a label. Actions sit in the page footer, not inside the form card.
# Anti-patterns

- Do not hardcode #8B5CF6. Use var(--color-accent).
- Do not build a modal from a positioned div.
- Do not add a button with no accessible name.
- Do not pick 13px of padding. Use the space scale.
# Examples

A settings page uses Input for the name and email, Select for the country, and Button for save.
A delete flow opens Dialog. The confirm action is variant="danger". The close control has an accessible name.
Empty lists use the empty-state pattern: one sentence and one Button.

Запуск

python3 ds-context/ds_context.py build ds-context/fixtures/acme

Команда пишет пакет в ds-context/fixtures/acme/dist. Это офлайн-сборка: без ключей и без вызова модели. Печать команды на этой фикстуре:

source  ds-context/fixtures/acme
out     ds-context/fixtures/acme/dist
pack    files
        design-system.md   ~ 292 tokens
        components.md      ~ 160 tokens
        patterns.md        ~  87 tokens
        anti-patterns.md   ~  54 tokens
        examples.md        ~  72 tokens
strategies (estimate, ~4 chars/token, manifest excluded)
        full         ~ 665
        compact      ~ 375
        components   ~ 180

Выход

design-system.md — то, что можно положить в контекст целиком. В стратегии full сюда же встроены паттерны, антипаттерны и примеры. Отдельные файлы в dist/ повторяют исходные markdown.

# Design system

Components: Button, Input, Dialog, Select.

## Tokens

| Token | Value |
| --- | --- |
| `--color-accent` | #8B5CF6 |
| `--color-text` | #212223 |
| `--color-surface` | #ffffff |
| `--space-2` | 8px |
| `--space-3` | 12px |
| `--space-4` | 16px |
| `--radius-2` | 8px |
| `--font-size-body` | 14px |

# Patterns

## Destructive confirm
Ask for confirmation before a delete. Use Dialog, name the object, and make the confirm button the danger variant.

## Empty state
Show one next action. Do not invent a second illustration style.

## Settings
Group fields by task. Use Input with a label. Actions sit in the page footer, not inside the form card.

# Anti-patterns

- Do not hardcode #8B5CF6. Use var(--color-accent).
- Do not build a modal from a positioned div.
- Do not add a button with no accessible name.
- Do not pick 13px of padding. Use the space scale.

# Examples

A settings page uses Input for the name and email, Select for the country, and Button for save.
A delete flow opens Dialog. The confirm action is variant="danger". The close control has an accessible name.
Empty lists use the empty-state pattern: one sentence and one Button.

components.md — API. Пример и строка Avoid берутся из JSON.

# Component API

### Button

Props: `variant`, `size`, `children`

Example:

```jsx
<Button variant="primary">Save</Button>
```

Avoid: A raw <button> with an inline background.

### Input

Props: `label`, `hint`, `error`

Example:

```jsx
<Input label="Email" />
```

Avoid: An unlabeled <input>.

### Dialog

Props: `open`, `title`, `onClose`

Example:

```jsx
<Dialog open title="Delete account" />
```

Avoid: A <div className="modal"> with a hand-rolled overlay.

### Select

Props: `label`, `options`, `value`

Example:

```jsx
<Select label="Country" options={countries} />
```

Avoid: A native <select> when the system Select exists.

Стратегии

Оценка размера — около 4 символов на токен. Это не токенайзер Claude. Манифест в сумму не входит. На фикстуре Acme:

СтратегияОценкаЧто внутри
full~665Токены, API с примерами, паттерны, антипаттерны и примеры экранов
compact~375Токены, API без примеров и антипаттерны. Отдельный examples.md не пишется
components~180Только обзор с токенами и API. Паттерны и примеры не пишутся

Полный пакет по файлам:

ФайлОценка
design-system.md~292
components.md~160
patterns.md~87
anti-patterns.md~54
examples.md~72

Манифест

Манифест нужен следующему шагу контура: выбрать стратегию под бюджет токенов и отдать модели только эти файлы. Имена компонентов и токенов лежат списком, чтобы ui-repair и ds-eval сверялись с тем же источником.

{
  "strategy": "full",
  "source": "ds-context/fixtures/acme",
  "components": [
    "Button",
    "Input",
    "Dialog",
    "Select"
  ],
  "tokens": [
    "--color-accent",
    "--color-text",
    "--color-surface",
    "--space-2",
    "--space-3",
    "--space-4",
    "--radius-2",
    "--font-size-body"
  ],
  "files": {
    "design-system.md": 292,
    "components.md": 160,
    "patterns.md": 87,
    "anti-patterns.md": 54,
    "examples.md": 72
  },
  "tokens_total": 665
}

Куда встаёт

Figma / локальная спека
→ ds-context
→ модель
→ ui-repair
→ ds-eval
→ prompt-regress

ds-context готовит контекст. ds-eval измеряет, помог ли он. prompt-regress показывает, не сломала ли новая версия промпта то, что уже проходило.

Предел

Инструмент не открывает Storybook и не ходит в Figma. Он не выбирает кусок пакета под один экран: стратегии глобальные. Оценка токенов годится, чтобы сравнить пакеты между собой, и не годится как счёт из API.