@hirobius/design-system 0.20.0 → 0.21.0
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/AGENTS.md +123 -0
- package/CONSUMING.md +4 -2
- package/DESIGN.md +28 -17
- package/NOTICE.md +1 -1
- package/README.md +37 -5
- package/dist/chunks/empty-state-DNoThgfV.js +1227 -0
- package/dist/chunks/form-By82qZcC.js +66 -0
- package/dist/fonts/geist-mono-400.woff2 +0 -0
- package/dist/fonts/satoshi-400.woff2 +0 -0
- package/dist/fonts/satoshi-500.woff2 +0 -0
- package/dist/fonts/satoshi-700.woff2 +0 -0
- package/dist/fonts.css +53 -0
- package/dist/form.js +1 -1
- package/dist/hirobius-ui.js +876 -877
- package/dist/manifest.js +1 -1
- package/dist/patterns.js +189 -170
- package/dist/styles.css +2 -2
- package/dist/tokens.css +1 -1
- package/dist/tokens.js +136 -57
- package/dist/types/src/app/components/alert-dialog.d.ts +5 -0
- package/dist/types/src/app/components/asset-img.d.ts +1 -0
- package/dist/types/src/app/components/avatar-group.d.ts +1 -0
- package/dist/types/src/app/components/badge.d.ts +1 -1
- package/dist/types/src/app/components/blockquote.d.ts +1 -0
- package/dist/types/src/app/components/button.d.ts +2 -0
- package/dist/types/src/app/components/card.d.ts +3 -2
- package/dist/types/src/app/components/data-table-section.d.ts +1 -0
- package/dist/types/src/app/components/destructive-section.d.ts +1 -0
- package/dist/types/src/app/components/dialog.d.ts +1 -0
- package/dist/types/src/app/components/error-pattern.d.ts +6 -1
- package/dist/types/src/app/components/form-actions.d.ts +1 -0
- package/dist/types/src/app/components/form-control.d.ts +10 -0
- package/dist/types/src/app/components/grid.d.ts +5 -0
- package/dist/types/src/app/components/icon.d.ts +1 -0
- package/dist/types/src/app/components/kbd.d.ts +1 -0
- package/dist/types/src/app/components/metadata-list.d.ts +1 -0
- package/dist/types/src/app/components/metric-tiles.d.ts +1 -0
- package/dist/types/src/app/components/progress.d.ts +1 -1
- package/dist/types/src/app/components/skeleton.d.ts +1 -1
- package/dist/types/src/app/components/stat.d.ts +4 -3
- package/dist/types/src/app/components/status-tile.d.ts +3 -2
- package/dist/types/src/app/components/table.d.ts +5 -0
- package/dist/types/src/app/components/text.d.ts +53 -6
- package/dist/types/src/app/components/timestamp.d.ts +1 -0
- package/dist/types/src/app/data/manifest-types.d.ts +4 -0
- package/dist/types/src/app/design-system/generated-token-refs.d.ts +37 -28
- package/dist/types/src/app/design-system/generated-token-values.d.ts +3 -0
- package/dist/types/src/app/design-system/tokens.d.ts +117 -15
- package/dist/types/src/index.d.ts +43 -40
- package/dist/variables.css +41 -33
- package/docs/CONSUMING.md +63 -39
- package/hirobius.tokens.json +77 -73
- package/llms.txt +158 -50
- package/mcp/catalog.mjs +366 -0
- package/mcp/guide.mjs +315 -0
- package/mcp/hds-mcp.mjs +49 -0
- package/mcp/server.mjs +94 -0
- package/package.json +35 -9
- package/public/hds-manifest.json +348 -254
- package/public/llms/components.txt +4 -3
- package/public/llms/layout.txt +1 -1
- package/public/llms-full.txt +190 -70
- package/public/llms.txt +158 -50
- package/scripts/eslint-plugin-hds/index.d.mts +29 -0
- package/scripts/eslint-plugin-hds/index.mjs +55 -0
- package/scripts/eslint-plugin-hds/package.json +30 -0
- package/scripts/eslint-plugin-hds/rules/no-raw-controls.mjs +47 -0
- package/scripts/eslint-plugin-hds/rules/no-raw-hex.mjs +70 -0
- package/scripts/eslint-plugin-hds/rules/no-raw-px-spacing.mjs +125 -0
- package/scripts/eslint-plugin-hds/rules/prefer-hds-layout-primitive.mjs +54 -0
- package/scripts/eslint-plugin-hds/rules/sx-token-first.mjs +111 -0
- package/scripts/eslint-plugin-hds/rules/utils.mjs +66 -0
- package/src/app/data/component-api.json +114 -53
- package/dist/chunks/empty-state-BBqhCx0S.js +0 -1217
- package/dist/chunks/form-COP3vK9_.js +0 -66
- package/dist/types/src/app/components/status-dot.d.ts +0 -39
package/mcp/guide.mjs
ADDED
|
@@ -0,0 +1,315 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The consumer guide: which HDS component answers which screen need.
|
|
3
|
+
*
|
|
4
|
+
* One source for both agent surfaces: the `hds` MCP server (mcp/catalog.mjs)
|
|
5
|
+
* serves it, and scripts/generate-agents-md.mjs projects it into the packaged
|
|
6
|
+
* AGENTS.md. Component facts (props, descriptions, import paths) are NOT here;
|
|
7
|
+
* they come from the shipped manifest and component-api.json. This file holds
|
|
8
|
+
* only the choices the data leaves open, each one a place where agents built
|
|
9
|
+
* the same screen differently in the 2026-10-05 consistency baseline
|
|
10
|
+
* (eval/consistency/DIAGNOSIS-2026-10-05.md).
|
|
11
|
+
*
|
|
12
|
+
* Every name in `use` must exist in component-api.json or be a listed hook;
|
|
13
|
+
* scripts/__tests__/generate-agents-md.test.mjs enforces it.
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
/** Hooks are not components, so component-api.json has no entry for them. */
|
|
17
|
+
export const HOOKS = {
|
|
18
|
+
useToast: {
|
|
19
|
+
import: '@hirobius/design-system',
|
|
20
|
+
note: 'Inside a ToastProvider: `const { toast } = useToast(); toast({ title, description?, tone? })`.',
|
|
21
|
+
},
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* One row per screen need. `use` is the answer (in order of composition),
|
|
26
|
+
* `avoid` the alternatives agents reached for instead, `how` one line of usage.
|
|
27
|
+
* `keywords` feed search_components ranking. A component named in `avoid` is
|
|
28
|
+
* not wrong in general; it is the wrong answer for that one need, and the
|
|
29
|
+
* server tells an agent so (`insteadFor`). Every need has one answer: the four
|
|
30
|
+
* number components are the case that split the baseline, so a row of numbers
|
|
31
|
+
* is always MetricTiles.
|
|
32
|
+
*/
|
|
33
|
+
export const INTENTS = [
|
|
34
|
+
{
|
|
35
|
+
id: 'page-shell',
|
|
36
|
+
need: 'Outermost wrapper of a screen',
|
|
37
|
+
keywords: ['page', 'screen', 'shell', 'layout', 'wrapper', 'root'],
|
|
38
|
+
use: ['Page', 'Stack'],
|
|
39
|
+
avoid: ['Container', 'Box', 'a bare Stack as the root'],
|
|
40
|
+
how: '`<Page><Stack gap="spacious">…one child per section…</Stack></Page>`. `Container` is for a full-bleed surface that is not a page.',
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
id: 'page-header',
|
|
44
|
+
need: 'Screen title with breadcrumb, status and actions',
|
|
45
|
+
keywords: ['title', 'header', 'heading', 'breadcrumb', 'status', 'name'],
|
|
46
|
+
use: ['PageHeader', 'Breadcrumb', 'Badge'],
|
|
47
|
+
avoid: ['Text as the page title', 'a Stack row of Text and Badge'],
|
|
48
|
+
how: 'Exactly one per screen: `<PageHeader breadcrumb={<Breadcrumb items={[{ label, href }, { label }]} />} title="…" status={<Badge tone="success">Active</Badge>} />`.',
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
id: 'metrics',
|
|
52
|
+
need: 'Row of headline numbers (stats, KPIs, counts, totals)',
|
|
53
|
+
keywords: [
|
|
54
|
+
'metric',
|
|
55
|
+
'metrics',
|
|
56
|
+
'stat',
|
|
57
|
+
'stats',
|
|
58
|
+
'kpi',
|
|
59
|
+
'number',
|
|
60
|
+
'numbers',
|
|
61
|
+
'tile',
|
|
62
|
+
'tiles',
|
|
63
|
+
'headline',
|
|
64
|
+
'count',
|
|
65
|
+
'total',
|
|
66
|
+
],
|
|
67
|
+
use: ['MetricTiles', 'MetricTile'],
|
|
68
|
+
avoid: ['Stat', 'StatusTile', 'CardMetric', 'Card with Text'],
|
|
69
|
+
how: '`<MetricTiles><MetricTile label="Open projects" value="5" />…</MetricTiles>`; one MetricTile per number. `Stat` is for a number inside prose or a dense list, `Card.Metric` for one inside an existing `Card`, `StatusTile` for a state with notes (never a number).',
|
|
70
|
+
},
|
|
71
|
+
{
|
|
72
|
+
id: 'table',
|
|
73
|
+
need: 'Table of records with row actions',
|
|
74
|
+
keywords: ['table', 'rows', 'records', 'list', 'data', 'grid', 'columns'],
|
|
75
|
+
use: ['DataTableSection', 'Badge', 'Button'],
|
|
76
|
+
avoid: ['Table on its own', 'Card around a Table'],
|
|
77
|
+
how: '`<DataTableSection title="Projects" columns={[{ key, label }]} rows={[{ key, cells: [...], actions: <Stack direction="row" gap="tight">…</Stack> }]} />`; leave the actions column out of `columns`.',
|
|
78
|
+
},
|
|
79
|
+
{
|
|
80
|
+
id: 'tabs',
|
|
81
|
+
need: 'Switch between panels of related content',
|
|
82
|
+
keywords: ['tabs', 'tab', 'panel', 'panels', 'sections', 'switch'],
|
|
83
|
+
use: ['Tabs', 'TabsList', 'TabsTrigger', 'TabsContent'],
|
|
84
|
+
avoid: ['SegmentedControl', 'Button rows'],
|
|
85
|
+
how: '`<Tabs defaultValue="a"><TabsList><TabsTrigger value="a">A</TabsTrigger></TabsList><TabsContent value="a">…</TabsContent></Tabs>`.',
|
|
86
|
+
},
|
|
87
|
+
{
|
|
88
|
+
id: 'status',
|
|
89
|
+
need: 'Status label in a header or table cell',
|
|
90
|
+
keywords: ['status', 'state', 'label', 'chip', 'pill', 'badge'],
|
|
91
|
+
use: ['Badge'],
|
|
92
|
+
avoid: ['Tag', 'Text'],
|
|
93
|
+
how: '`<Badge tone="success|info|warning|danger|neutral|inProgress">…</Badge>`; tone is the only styling input.',
|
|
94
|
+
},
|
|
95
|
+
{
|
|
96
|
+
id: 'form',
|
|
97
|
+
need: 'Form with fields and a submit button',
|
|
98
|
+
keywords: ['form', 'field', 'fields', 'input', 'textarea', 'notes', 'submit', 'save'],
|
|
99
|
+
use: ['Form', 'Textarea', 'Input', 'FormActions', 'Button'],
|
|
100
|
+
avoid: ['a raw `<form>`', 'a Stack of Buttons as the footer'],
|
|
101
|
+
how: '`<Form onSubmit={…}><Textarea label="Notes" … /><FormActions primary={<Button type="submit">Save note</Button>} /></Form>`; every field takes its own `label`.',
|
|
102
|
+
},
|
|
103
|
+
{
|
|
104
|
+
id: 'confirm',
|
|
105
|
+
need: 'Confirm a destructive or irreversible action (delete, archive, remove)',
|
|
106
|
+
keywords: [
|
|
107
|
+
'confirm',
|
|
108
|
+
'confirmation',
|
|
109
|
+
'destructive',
|
|
110
|
+
'delete',
|
|
111
|
+
'archive',
|
|
112
|
+
'remove',
|
|
113
|
+
'modal',
|
|
114
|
+
'dialog',
|
|
115
|
+
],
|
|
116
|
+
use: ['AlertDialog', 'Button'],
|
|
117
|
+
avoid: ['Dialog', '`window.confirm`'],
|
|
118
|
+
how: 'Trigger, title, description, Cancel and Action parts: `<AlertDialog><AlertDialog.Trigger asChild><Button …>Archive</Button></AlertDialog.Trigger><AlertDialog.Content><AlertDialog.Header><AlertDialog.Title>…</AlertDialog.Title><AlertDialog.Description>…</AlertDialog.Description></AlertDialog.Header><AlertDialog.Footer><AlertDialog.Cancel asChild><Button variant="secondary">Cancel</Button></AlertDialog.Cancel><AlertDialog.Action asChild><Button tone="danger" onClick={…}>Archive</Button></AlertDialog.Action></AlertDialog.Footer></AlertDialog.Content></AlertDialog>`. Focus moves in and returns to the trigger for you.',
|
|
119
|
+
},
|
|
120
|
+
{
|
|
121
|
+
id: 'feedback',
|
|
122
|
+
need: 'Brief confirmation after an action (saved, archived, sent)',
|
|
123
|
+
keywords: [
|
|
124
|
+
'toast',
|
|
125
|
+
'confirmation',
|
|
126
|
+
'saved',
|
|
127
|
+
'success',
|
|
128
|
+
'notify',
|
|
129
|
+
'notification',
|
|
130
|
+
'feedback',
|
|
131
|
+
'message',
|
|
132
|
+
],
|
|
133
|
+
use: ['ToastProvider', 'useToast'],
|
|
134
|
+
avoid: ['Badge', 'Alert', 'Text'],
|
|
135
|
+
how: 'Wrap the app root once in `<ToastProvider>`, then `toast({ title: "Note saved", tone: "success" })` from `useToast()`.',
|
|
136
|
+
},
|
|
137
|
+
{
|
|
138
|
+
id: 'message',
|
|
139
|
+
need: 'Message that stays next to its content (error, warning, info)',
|
|
140
|
+
keywords: ['alert', 'error', 'warning', 'info', 'banner', 'callout'],
|
|
141
|
+
use: ['Alert'],
|
|
142
|
+
avoid: ['Text in a coloured Surface'],
|
|
143
|
+
how: '`<Alert tone="warning">…</Alert>`.',
|
|
144
|
+
},
|
|
145
|
+
{
|
|
146
|
+
id: 'action',
|
|
147
|
+
need: 'Button or row action',
|
|
148
|
+
keywords: ['button', 'action', 'click', 'view', 'edit', 'cta'],
|
|
149
|
+
use: ['Button'],
|
|
150
|
+
avoid: ['a raw `<button>`', 'InlineLink for an action'],
|
|
151
|
+
how: 'Row actions: `<Button variant="tertiary" size="sm" aria-label="View Brand refresh">View</Button>`; the destructive one adds `tone="danger"`.',
|
|
152
|
+
},
|
|
153
|
+
{
|
|
154
|
+
id: 'spacing',
|
|
155
|
+
need: 'Space between sections or items',
|
|
156
|
+
keywords: ['spacing', 'gap', 'stack', 'row', 'column', 'layout', 'columns'],
|
|
157
|
+
use: ['Stack', 'Grid'],
|
|
158
|
+
avoid: ['margins on children', 'raw px gaps such as px8'],
|
|
159
|
+
how: 'Stack `gap`: `spacious` between page sections, `normal` inside a section, `tight` between buttons. `Grid` for two-dimensional layout.',
|
|
160
|
+
},
|
|
161
|
+
{
|
|
162
|
+
id: 'surface',
|
|
163
|
+
need: 'Padded background or a titled content object',
|
|
164
|
+
keywords: ['card', 'surface', 'panel', 'box', 'background', 'container'],
|
|
165
|
+
use: ['Surface', 'Card'],
|
|
166
|
+
avoid: ['a Box with background and padding'],
|
|
167
|
+
how: '`Surface` for a plain padded background; `Card` only when the content has a title or can be pressed.',
|
|
168
|
+
},
|
|
169
|
+
{
|
|
170
|
+
id: 'progress',
|
|
171
|
+
need: 'Progress bar or ring',
|
|
172
|
+
keywords: ['progress', 'bar', 'percent', 'loading', 'completion'],
|
|
173
|
+
use: ['Progress'],
|
|
174
|
+
avoid: ['a hand-built bar'],
|
|
175
|
+
how: '`<Progress value={60} label="Upload" />`; inside an existing `Card`, `CardProgress`.',
|
|
176
|
+
},
|
|
177
|
+
{
|
|
178
|
+
id: 'text',
|
|
179
|
+
need: 'Body copy',
|
|
180
|
+
keywords: ['text', 'copy', 'paragraph', 'body', 'typography'],
|
|
181
|
+
use: ['Text'],
|
|
182
|
+
avoid: ['Text as a section heading'],
|
|
183
|
+
how: 'Section headings come from the pattern that owns the section (`PageHeader` `title`, `DataTableSection` `title`), not from extra Text.',
|
|
184
|
+
},
|
|
185
|
+
{
|
|
186
|
+
id: 'empty',
|
|
187
|
+
need: 'Empty state for a list or section',
|
|
188
|
+
keywords: ['empty', 'none', 'nothing', 'placeholder', 'zero'],
|
|
189
|
+
use: ['EmptyState'],
|
|
190
|
+
avoid: ['Text saying "No items"'],
|
|
191
|
+
how: '`DataTableSection` shows its own through `emptyTitle`; use `EmptyState` elsewhere.',
|
|
192
|
+
},
|
|
193
|
+
{
|
|
194
|
+
id: 'danger-zone',
|
|
195
|
+
need: 'The one irreversible action of a screen (delete the client, close the account)',
|
|
196
|
+
keywords: ['danger', 'irreversible', 'delete', 'account', 'zone', 'destructive'],
|
|
197
|
+
use: ['DestructiveSection'],
|
|
198
|
+
avoid: ['a danger Button that acts without a confirm'],
|
|
199
|
+
how: 'Last on the screen: `<DestructiveSection title="Delete client" description="…" actionLabel="Delete" onConfirm={…} />`; it opens its own AlertDialog. A per-row destructive action uses AlertDialog directly.',
|
|
200
|
+
},
|
|
201
|
+
{
|
|
202
|
+
id: 'metadata',
|
|
203
|
+
need: 'Read-only details as label and value pairs',
|
|
204
|
+
keywords: ['metadata', 'details', 'properties', 'key', 'value', 'pairs', 'record'],
|
|
205
|
+
use: ['MetadataList', 'Field'],
|
|
206
|
+
avoid: ['a Table with two columns', 'Text pairs in a Stack'],
|
|
207
|
+
how: '`<MetadataList items={[{ term: "Owner", description: "Ada" }]} />` for a set; `Field` for one pair.',
|
|
208
|
+
},
|
|
209
|
+
{
|
|
210
|
+
id: 'people',
|
|
211
|
+
need: 'A person, or a group of people',
|
|
212
|
+
keywords: ['avatar', 'avatars', 'person', 'people', 'user', 'users', 'team', 'members'],
|
|
213
|
+
use: ['Avatar', 'AvatarGroup'],
|
|
214
|
+
avoid: ['an image in a rounded Box'],
|
|
215
|
+
how: '`<Avatar src alt initials />`; several in a row: `<AvatarGroup max={3}>…Avatars…</AvatarGroup>`.',
|
|
216
|
+
},
|
|
217
|
+
{
|
|
218
|
+
id: 'date',
|
|
219
|
+
need: 'A date or time',
|
|
220
|
+
keywords: ['date', 'time', 'timestamp', 'when', 'ago', 'due'],
|
|
221
|
+
use: ['Timestamp'],
|
|
222
|
+
avoid: ['a formatted string in Text'],
|
|
223
|
+
how: '`<Timestamp date="2026-10-14" />` renders a `<time dateTime>`; a date in a table cell may stay plain text.',
|
|
224
|
+
},
|
|
225
|
+
{
|
|
226
|
+
id: 'shortcut',
|
|
227
|
+
need: 'A keyboard key or shortcut hint',
|
|
228
|
+
keywords: ['keyboard', 'shortcut', 'key', 'kbd', 'hotkey'],
|
|
229
|
+
use: ['Kbd'],
|
|
230
|
+
avoid: ['InlineCode', 'Text in a Box'],
|
|
231
|
+
how: '`<Kbd>⌘K</Kbd>`.',
|
|
232
|
+
},
|
|
233
|
+
{
|
|
234
|
+
id: 'quote',
|
|
235
|
+
need: 'A quotation with attribution',
|
|
236
|
+
keywords: ['quote', 'quotation', 'testimonial', 'blockquote', 'citation'],
|
|
237
|
+
use: ['Blockquote'],
|
|
238
|
+
avoid: ['Callout for a quote', 'italic Text'],
|
|
239
|
+
how: '`<Blockquote attribution="Ada Lovelace">…</Blockquote>`.',
|
|
240
|
+
},
|
|
241
|
+
{
|
|
242
|
+
id: 'rail',
|
|
243
|
+
need: 'A fixed-width side rail beside fluid content, or a row that stacks when narrow',
|
|
244
|
+
keywords: ['sidebar', 'rail', 'aside', 'responsive', 'switcher', 'stack', 'narrow', 'columns'],
|
|
245
|
+
use: ['Sidebar', 'Switcher'],
|
|
246
|
+
avoid: ['a Grid with media queries'],
|
|
247
|
+
how: '`Sidebar` (`side`, `sideWidth`) for rail + content; `Switcher` (`threshold`) for a row that flips to a column. Neither needs a media query.',
|
|
248
|
+
},
|
|
249
|
+
{
|
|
250
|
+
id: 'sticky',
|
|
251
|
+
need: 'Keep an element stuck while its region scrolls',
|
|
252
|
+
keywords: ['sticky', 'pin', 'fixed', 'scroll'],
|
|
253
|
+
use: ['Pin'],
|
|
254
|
+
avoid: ['position: sticky in style'],
|
|
255
|
+
how: '`<Pin top="…">…</Pin>` inside the scrolling region.',
|
|
256
|
+
},
|
|
257
|
+
];
|
|
258
|
+
|
|
259
|
+
/**
|
|
260
|
+
* The ratified core set (scripts/lib/core-components.mjs, hds#254). Copied, not
|
|
261
|
+
* imported, because scripts/ does not ship; scripts/__tests__/generate-agents-md.test.mjs
|
|
262
|
+
* pins the two together.
|
|
263
|
+
*/
|
|
264
|
+
export const RATIFIED_CORE = [
|
|
265
|
+
'Alert',
|
|
266
|
+
'Avatar',
|
|
267
|
+
'Badge',
|
|
268
|
+
'Box',
|
|
269
|
+
'Breadcrumb',
|
|
270
|
+
'Button',
|
|
271
|
+
'Card',
|
|
272
|
+
'Checkbox',
|
|
273
|
+
'Combobox',
|
|
274
|
+
'Container',
|
|
275
|
+
'Dialog',
|
|
276
|
+
'Disclosure',
|
|
277
|
+
'Divider',
|
|
278
|
+
'EmptyState',
|
|
279
|
+
'Field',
|
|
280
|
+
'Grid',
|
|
281
|
+
'HdsRouterProvider',
|
|
282
|
+
'HdsThemeProvider',
|
|
283
|
+
'Icon',
|
|
284
|
+
'InlineLink',
|
|
285
|
+
'Input',
|
|
286
|
+
'Kbd',
|
|
287
|
+
'Menu',
|
|
288
|
+
'Pagination',
|
|
289
|
+
'Popover',
|
|
290
|
+
'Progress',
|
|
291
|
+
'Radio',
|
|
292
|
+
'SegmentedControl',
|
|
293
|
+
'Select',
|
|
294
|
+
'Skeleton',
|
|
295
|
+
'Slider',
|
|
296
|
+
'Spinner',
|
|
297
|
+
'Stack',
|
|
298
|
+
'Surface',
|
|
299
|
+
'Table',
|
|
300
|
+
'Tabs',
|
|
301
|
+
'Tag',
|
|
302
|
+
'Text',
|
|
303
|
+
'Textarea',
|
|
304
|
+
'ToastProvider',
|
|
305
|
+
'Toggle',
|
|
306
|
+
'Tooltip',
|
|
307
|
+
'VisuallyHidden',
|
|
308
|
+
];
|
|
309
|
+
|
|
310
|
+
/** The core set agents should prefer: the ratified core plus every `use` above, hooks excluded. */
|
|
311
|
+
export function coreComponents() {
|
|
312
|
+
const names = new Set(RATIFIED_CORE);
|
|
313
|
+
for (const intent of INTENTS) for (const name of intent.use) if (!HOOKS[name]) names.add(name);
|
|
314
|
+
return [...names].sort();
|
|
315
|
+
}
|
package/mcp/hds-mcp.mjs
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* hds-mcp — the `hds` MCP server over stdio, shipped in @hirobius/design-system.
|
|
4
|
+
*
|
|
5
|
+
* Run from an app that has the package installed: npx hds-mcp
|
|
6
|
+
* Without an install: npx -y -p @hirobius/design-system hds-mcp
|
|
7
|
+
*
|
|
8
|
+
* Reads the data the package already ships (public/hds-manifest.json,
|
|
9
|
+
* src/app/data/component-api.json) from its own install directory; it never
|
|
10
|
+
* touches the network or the caller's files. Messages are newline-delimited
|
|
11
|
+
* JSON-RPC 2.0 (the MCP stdio transport); logs go to stderr only.
|
|
12
|
+
*/
|
|
13
|
+
import { readFileSync } from 'node:fs';
|
|
14
|
+
import { dirname, join } from 'node:path';
|
|
15
|
+
import { createInterface } from 'node:readline';
|
|
16
|
+
import { fileURLToPath } from 'node:url';
|
|
17
|
+
import { loadCatalog } from './catalog.mjs';
|
|
18
|
+
import { createServer } from './server.mjs';
|
|
19
|
+
|
|
20
|
+
const PKG_ROOT = join(dirname(fileURLToPath(import.meta.url)), '..');
|
|
21
|
+
const { version } = JSON.parse(readFileSync(join(PKG_ROOT, 'package.json'), 'utf8'));
|
|
22
|
+
|
|
23
|
+
let catalog;
|
|
24
|
+
try {
|
|
25
|
+
catalog = loadCatalog(PKG_ROOT);
|
|
26
|
+
} catch (err) {
|
|
27
|
+
console.error(
|
|
28
|
+
`hds-mcp: could not read the design-system data under ${PKG_ROOT} (${err.message}).\n` +
|
|
29
|
+
' Reinstall @hirobius/design-system; in the hds repo run `node scripts/generate-component-api.mjs` first.',
|
|
30
|
+
);
|
|
31
|
+
process.exit(1);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const handle = createServer({ version, instructions: catalog.instructions, tools: catalog.tools });
|
|
35
|
+
|
|
36
|
+
const send = (msg) => process.stdout.write(`${JSON.stringify(msg)}\n`);
|
|
37
|
+
|
|
38
|
+
createInterface({ input: process.stdin }).on('line', (line) => {
|
|
39
|
+
if (!line.trim()) return;
|
|
40
|
+
let message;
|
|
41
|
+
try {
|
|
42
|
+
message = JSON.parse(line);
|
|
43
|
+
} catch {
|
|
44
|
+
send({ jsonrpc: '2.0', id: null, error: { code: -32700, message: 'Parse error' } });
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
const response = handle(message);
|
|
48
|
+
if (response) send(response);
|
|
49
|
+
});
|
package/mcp/server.mjs
ADDED
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The `hds` MCP server's protocol layer: JSON-RPC 2.0 messages in, responses out.
|
|
3
|
+
*
|
|
4
|
+
* Interface: `createServer({ version, instructions, tools })` returns
|
|
5
|
+
* `handle(message)`, which takes one parsed JSON value and returns the response
|
|
6
|
+
* object, or `null` for a notification. It never throws: a message that is not a
|
|
7
|
+
* request object gets -32600, and anything unexpected inside gets -32603, so one
|
|
8
|
+
* bad line cannot stop the process. Transport (stdio framing) lives in hds-mcp.mjs, the
|
|
9
|
+
* tool behaviour in catalog.mjs; this module only speaks MCP.
|
|
10
|
+
*
|
|
11
|
+
* No dependency on @modelcontextprotocol/sdk: the server needs four methods
|
|
12
|
+
* (initialize, ping, tools/list, tools/call), and a zero-dependency module keeps
|
|
13
|
+
* the published package's install graph unchanged.
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
export const SERVER_NAME = 'hds';
|
|
17
|
+
|
|
18
|
+
/** Protocol versions this server can speak; it echoes the client's when it is one of these. */
|
|
19
|
+
const SUPPORTED_VERSIONS = ['2025-06-18', '2025-03-26', '2024-11-05'];
|
|
20
|
+
|
|
21
|
+
const rpcError = (id, code, message) => ({ jsonrpc: '2.0', id, error: { code, message } });
|
|
22
|
+
const rpcResult = (id, result) => ({ jsonrpc: '2.0', id, result });
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* @param {{ version: string, instructions: string, tools: { name: string, description: string, inputSchema: object, run: (args: object) => unknown }[] }} options
|
|
26
|
+
*/
|
|
27
|
+
export function createServer({ version, instructions, tools }) {
|
|
28
|
+
const byName = new Map(tools.map((t) => [t.name, t]));
|
|
29
|
+
|
|
30
|
+
function dispatch(message) {
|
|
31
|
+
// MCP has no batches (removed in 2025-06-18), so an array is invalid like any non-object.
|
|
32
|
+
if (!message || typeof message !== 'object' || Array.isArray(message)) {
|
|
33
|
+
return rpcError(null, -32600, 'Invalid Request: expected one JSON-RPC request object');
|
|
34
|
+
}
|
|
35
|
+
const hasId = Object.prototype.hasOwnProperty.call(message, 'id');
|
|
36
|
+
const id = hasId ? message.id : null;
|
|
37
|
+
if (typeof message.method !== 'string') {
|
|
38
|
+
return hasId ? rpcError(id, -32600, 'Invalid Request: method must be a string') : null;
|
|
39
|
+
}
|
|
40
|
+
// A message without an `id` member is a notification; `id: null` is still a request.
|
|
41
|
+
if (!hasId) return null;
|
|
42
|
+
const { method } = message;
|
|
43
|
+
const params = message.params && typeof message.params === 'object' ? message.params : {};
|
|
44
|
+
|
|
45
|
+
switch (method) {
|
|
46
|
+
case 'initialize': {
|
|
47
|
+
const asked = params.protocolVersion;
|
|
48
|
+
return rpcResult(id, {
|
|
49
|
+
protocolVersion: SUPPORTED_VERSIONS.includes(asked) ? asked : SUPPORTED_VERSIONS[0],
|
|
50
|
+
capabilities: { tools: { listChanged: false } },
|
|
51
|
+
serverInfo: { name: SERVER_NAME, version },
|
|
52
|
+
instructions,
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
case 'ping':
|
|
56
|
+
return rpcResult(id, {});
|
|
57
|
+
case 'tools/list':
|
|
58
|
+
return rpcResult(id, {
|
|
59
|
+
tools: tools.map(({ name, description, inputSchema }) => ({
|
|
60
|
+
name,
|
|
61
|
+
description,
|
|
62
|
+
inputSchema,
|
|
63
|
+
})),
|
|
64
|
+
});
|
|
65
|
+
case 'tools/call': {
|
|
66
|
+
const tool = typeof params.name === 'string' ? byName.get(params.name) : undefined;
|
|
67
|
+
if (!tool) return rpcError(id, -32602, `Unknown tool: ${params.name}`);
|
|
68
|
+
try {
|
|
69
|
+
const args =
|
|
70
|
+
params.arguments && typeof params.arguments === 'object' ? params.arguments : {};
|
|
71
|
+
const data = tool.run(args);
|
|
72
|
+
return rpcResult(id, { content: [{ type: 'text', text: JSON.stringify(data) }] });
|
|
73
|
+
} catch (err) {
|
|
74
|
+
// A tool-level failure is a result with isError, so the agent can read and recover.
|
|
75
|
+
return rpcResult(id, {
|
|
76
|
+
content: [{ type: 'text', text: JSON.stringify({ error: String(err.message) }) }],
|
|
77
|
+
isError: true,
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
default:
|
|
82
|
+
return rpcError(id, -32601, `Method not found: ${method}`);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
return function handle(message) {
|
|
87
|
+
try {
|
|
88
|
+
return dispatch(message);
|
|
89
|
+
} catch (err) {
|
|
90
|
+
const id = message && typeof message === 'object' && 'id' in message ? message.id : null;
|
|
91
|
+
return rpcError(id ?? null, -32603, `Internal error: ${err?.message ?? err}`);
|
|
92
|
+
}
|
|
93
|
+
};
|
|
94
|
+
}
|
package/package.json
CHANGED
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
"author": "Adrian Milsap",
|
|
21
21
|
"license": "SEE LICENSE IN NOTICE.md",
|
|
22
22
|
"private": false,
|
|
23
|
-
"version": "0.
|
|
23
|
+
"version": "0.21.0",
|
|
24
24
|
"engines": {
|
|
25
25
|
"node": ">=20",
|
|
26
26
|
"pnpm": ">=8"
|
|
@@ -39,6 +39,7 @@
|
|
|
39
39
|
},
|
|
40
40
|
"./tokens.css": "./dist/tokens.css",
|
|
41
41
|
"./styles.css": "./dist/styles.css",
|
|
42
|
+
"./fonts.css": "./dist/fonts.css",
|
|
42
43
|
"./variables.css": "./dist/variables.css",
|
|
43
44
|
"./static.css": "./dist/static.css",
|
|
44
45
|
"./tokens": {
|
|
@@ -91,6 +92,10 @@
|
|
|
91
92
|
"import": "./dist/icons.js",
|
|
92
93
|
"default": "./dist/icons.js"
|
|
93
94
|
},
|
|
95
|
+
"./eslint-plugin": {
|
|
96
|
+
"types": "./scripts/eslint-plugin-hds/index.d.mts",
|
|
97
|
+
"default": "./scripts/eslint-plugin-hds/index.mjs"
|
|
98
|
+
},
|
|
94
99
|
"./package.json": "./package.json"
|
|
95
100
|
},
|
|
96
101
|
"files": [
|
|
@@ -113,7 +118,13 @@
|
|
|
113
118
|
"codemods/unrewritable.mjs",
|
|
114
119
|
"codemods/jsx-fold.mjs",
|
|
115
120
|
"codemods/not-found-pattern.mjs",
|
|
116
|
-
"codemods/tile-grid.mjs"
|
|
121
|
+
"codemods/tile-grid.mjs",
|
|
122
|
+
"AGENTS.md",
|
|
123
|
+
"mcp/",
|
|
124
|
+
"scripts/eslint-plugin-hds/index.mjs",
|
|
125
|
+
"scripts/eslint-plugin-hds/index.d.mts",
|
|
126
|
+
"scripts/eslint-plugin-hds/package.json",
|
|
127
|
+
"scripts/eslint-plugin-hds/rules/"
|
|
117
128
|
],
|
|
118
129
|
"publishConfig": {
|
|
119
130
|
"registry": "https://registry.npmjs.org",
|
|
@@ -242,7 +253,8 @@
|
|
|
242
253
|
"hds-patterns-subpath": "codemods/patterns-subpath.mjs",
|
|
243
254
|
"hds-prefix": "codemods/hds-prefix.mjs",
|
|
244
255
|
"hds-not-found-pattern": "codemods/not-found-pattern.mjs",
|
|
245
|
-
"hds-tile-grid": "codemods/tile-grid.mjs"
|
|
256
|
+
"hds-tile-grid": "codemods/tile-grid.mjs",
|
|
257
|
+
"hds-mcp": "mcp/hds-mcp.mjs"
|
|
246
258
|
},
|
|
247
259
|
"scripts": {
|
|
248
260
|
"_comment:a11y-high-value": "The following check:* validators are high-value accessibility checks that catch real violations (check:focus found 1 missing focus class). Kept dormant/manual-only to avoid pre-commit performance regression. Candidates to wire into pretest after CI stability review.",
|
|
@@ -258,7 +270,7 @@
|
|
|
258
270
|
"audit:sidecar": "node scripts/generate-security-posture.mjs",
|
|
259
271
|
"audit:style-dictionary": "bash scripts/poc/style-dictionary-poc/run.sh",
|
|
260
272
|
"build-storybook": "storybook build",
|
|
261
|
-
"build:lib": "vite build -c vite.config.lib.ts && pnpm build:types && node scripts/build-vars-css.mjs && node scripts/
|
|
273
|
+
"build:lib": "vite build -c vite.config.lib.ts && pnpm build:types && node scripts/build-vars-css.mjs && node scripts/build-fonts-css.mjs && node scripts/build-styles-css.mjs && node scripts/build-static-css.mjs",
|
|
262
274
|
"build:types": "tsc -p tsconfig.dts.json && node scripts/strip-dts-side-effects.mjs && node scripts/add-dts-extensions.mjs",
|
|
263
275
|
"catalog": "node scripts/generate-visual-catalog.mjs",
|
|
264
276
|
"changelog:generate": "node scripts/generate-changelog.mjs",
|
|
@@ -279,11 +291,13 @@
|
|
|
279
291
|
"check:dimensions": "node scripts/check-dimensions.mjs",
|
|
280
292
|
"check:doc-refs": "node scripts/check-link-integrity.mjs --doc-refs-only",
|
|
281
293
|
"check:docs": "node scripts/audit-component-integrity.mjs --docs && node scripts/check-token-descriptions.mjs --no-missing --quality",
|
|
294
|
+
"check:docs-guardrails": "node scripts/check-docs.mjs",
|
|
282
295
|
"check:exemptions": "node scripts/check-exemptions.mjs",
|
|
283
296
|
"check:fast": "node scripts/validate-manifest.mjs && node scripts/check-manifest-drift.mjs && node scripts/build-tokens.mjs && node scripts/verify-tokens.mjs && node scripts/audit-tokens.mjs --full && pnpm typecheck && node scripts/check-css-integrity.mjs && node scripts/audit-component-integrity.mjs --tokens && pnpm check:docs && node scripts/check-ref-forwarding.mjs && node scripts/check-hardcoded-spacing.mjs && node scripts/check-dimensions.mjs && node scripts/check-hardcoded-breakpoints.mjs && node scripts/check-typography-discipline.mjs && pnpm check:mojibake && node scripts/check-style-discipline.mjs --style-props-only && node scripts/check-tier-bypass.mjs && node scripts/check-link-integrity.mjs --doc-refs-only && node scripts/check-brand.mjs && node scripts/check-registry.mjs && node scripts/check-template-source-of-truth.mjs",
|
|
284
297
|
"check:figma-coverage": "node scripts/check-figma-coverage.mjs",
|
|
285
298
|
"check:figma-drift": "node scripts/check-figma-drift.mjs",
|
|
286
299
|
"check:figma-mapping": "node scripts/check-figma-mapping.mjs",
|
|
300
|
+
"check:figma-staging-urls": "node scripts/check-figma-staging-urls.mjs",
|
|
287
301
|
"check:focus": "node scripts/check-focus-states.mjs",
|
|
288
302
|
"check:font-files": "node scripts/check-typography-discipline.mjs --font-files-only",
|
|
289
303
|
"check:fonts": "node scripts/check-typography-discipline.mjs --fonts-only",
|
|
@@ -317,6 +331,8 @@
|
|
|
317
331
|
"check:spacing": "node scripts/check-hardcoded-spacing.mjs",
|
|
318
332
|
"check:spacing-vocabulary": "node scripts/check-spacing-vocabulary.mjs",
|
|
319
333
|
"check:story-coverage": "node scripts/check-story-coverage.mjs",
|
|
334
|
+
"check:layout-contract": "node scripts/check-layout-contract.mjs",
|
|
335
|
+
"check:spacing-scale": "node scripts/check-tailwind-spacing-scale.mjs",
|
|
320
336
|
"check:storybook-axe": "node scripts/check-storybook-axe.mjs",
|
|
321
337
|
"check:style-props": "node scripts/check-style-discipline.mjs --style-props-only",
|
|
322
338
|
"check:sync-map": "node scripts/check-sync-map.mjs",
|
|
@@ -338,8 +354,12 @@
|
|
|
338
354
|
"closure:plan:check": "node scripts/generate-closure-plan.mjs --check",
|
|
339
355
|
"docs:api": "node scripts/generate-component-api.mjs",
|
|
340
356
|
"docs:changelogs": "node scripts/generate-component-changelogs.mjs",
|
|
357
|
+
"docs:build": "pnpm docs:prepare && pnpm --dir docs-site build",
|
|
358
|
+
"docs:components": "node scripts/generate-docs-components.mjs",
|
|
341
359
|
"docs:component": "node scripts/generate-component-page.mjs",
|
|
360
|
+
"docs:dev": "pnpm docs:prepare && pnpm --dir docs-site dev",
|
|
342
361
|
"docs:llms": "node scripts/generate-llms-txt.mjs",
|
|
362
|
+
"docs:prepare": "pnpm build:lib && pnpm docs:api && pnpm docs:components && pnpm --dir docs-site install --frozen-lockfile",
|
|
343
363
|
"docs:site": "node scripts/generate-component-page.mjs --all --clean",
|
|
344
364
|
"docs:site:inline": "node scripts/generate-component-page.mjs --all --clean --inline",
|
|
345
365
|
"docs:site:shots": "node scripts/generate-component-page.mjs --all --clean --shots",
|
|
@@ -363,8 +383,8 @@
|
|
|
363
383
|
"health-commit": "pnpm check:semantic-report && node scripts/print-health-commit.mjs",
|
|
364
384
|
"icons:sync": "node scripts/sync-icons.mjs",
|
|
365
385
|
"knip": "knip",
|
|
366
|
-
"lint": "eslint src scripts validators tests --max-warnings=0",
|
|
367
|
-
"lint:fix": "eslint src scripts validators tests --fix",
|
|
386
|
+
"lint": "eslint src scripts validators tests mcp --max-warnings=0",
|
|
387
|
+
"lint:fix": "eslint src scripts validators tests mcp --fix",
|
|
368
388
|
"llms:generate": "node scripts/generate-llms-txt.mjs",
|
|
369
389
|
"skill:generate": "node scripts/generate-consumer-skill.mjs",
|
|
370
390
|
"check:consumer-skill": "node scripts/generate-consumer-skill.mjs --check",
|
|
@@ -373,7 +393,7 @@
|
|
|
373
393
|
"prebuild-storybook": "node scripts/generate-component-api.mjs",
|
|
374
394
|
"prebuild:lib": "node scripts/generate-component-api.mjs",
|
|
375
395
|
"prestorybook": "node scripts/generate-component-api.mjs",
|
|
376
|
-
"pretest": "node scripts/validate-manifest.mjs && node scripts/check-manifest-drift.mjs && node scripts/check-manifest-schema-semver.mjs && node scripts/audit-component-integrity.mjs --completeness && node scripts/audit-component-integrity.mjs --api && node scripts/check-source-canon.mjs && node scripts/check-binding-drift.mjs && node scripts/check-registry.mjs && node scripts/check-tenant-tokens.mjs && node scripts/check-props-exports.mjs && knip --no-exit-code && node scripts/check-dark-alias-flip.mjs && node scripts/check-focus-states.mjs && node scripts/generate-staging-inventory.mjs --check && node scripts/generate-consumer-skill.mjs --check && node scripts/check-deprecations.mjs",
|
|
396
|
+
"pretest": "node scripts/validate-manifest.mjs && node scripts/check-manifest-drift.mjs && node scripts/check-manifest-schema-semver.mjs && node scripts/audit-component-integrity.mjs --completeness && node scripts/audit-component-integrity.mjs --api && node scripts/check-source-canon.mjs && node scripts/check-binding-drift.mjs && node scripts/check-registry.mjs && node scripts/check-tenant-tokens.mjs && node scripts/check-props-exports.mjs && knip --no-exit-code && node scripts/check-dark-alias-flip.mjs && node scripts/check-focus-states.mjs && node scripts/generate-staging-inventory.mjs --check && node scripts/generate-consumer-skill.mjs --check && node scripts/generate-agents-md.mjs --check && node scripts/check-deprecations.mjs && node scripts/upgrade/schema.mjs --check && node scripts/check-docs.mjs && node --test scripts/check-docs.selftest.mjs",
|
|
377
397
|
"pretypecheck": "node scripts/audit-tokens.mjs --report-only",
|
|
378
398
|
"preview:build": "vite build --config preview/vite.config.mjs",
|
|
379
399
|
"promote": "node scripts/promote.mjs",
|
|
@@ -382,6 +402,9 @@
|
|
|
382
402
|
"scaffold:component": "node scripts/scaffold-component.mjs",
|
|
383
403
|
"scaffold:tenant": "node scripts/scaffold-tenant.mjs",
|
|
384
404
|
"size-limit": "size-limit --config .size-limit.cjs",
|
|
405
|
+
"layout-contract:generate": "node scripts/generate-layout-contract.mjs",
|
|
406
|
+
"status:touch": "node scripts/status-touch.mjs",
|
|
407
|
+
"status:fold": "node scripts/status-fold.mjs",
|
|
385
408
|
"smoke:consumer": "node scripts/smoke-consumer.mjs && node scripts/check-published-types.mjs && node scripts/check-rsc-directives.mjs && node scripts/check-pack-contents.mjs",
|
|
386
409
|
"storybook": "storybook dev -p 6006",
|
|
387
410
|
"storybook:deploy": "node scripts/deploy-storybook.mjs",
|
|
@@ -394,7 +417,7 @@
|
|
|
394
417
|
"test:figma": "npx tsx scripts/figma-sync.ts",
|
|
395
418
|
"test:phase1": "node scripts/test-phase1.mjs",
|
|
396
419
|
"test:watch": "vitest",
|
|
397
|
-
"tokens": "node scripts/generate-manifest.mjs && node scripts/generate-component-api.mjs && node scripts/enrich-manifest.mjs && node scripts/build-tokens.mjs && node scripts/audit-tokens.mjs --forbidden && node scripts/check-typography-discipline.mjs --overrides-only && node scripts/build-handoff.mjs && node scripts/build-design-md.mjs && node scripts/generate-llms-txt.mjs && node scripts/generate-consumer-skill.mjs && node scripts/build-readme-counts.mjs",
|
|
420
|
+
"tokens": "node scripts/generate-manifest.mjs && node scripts/generate-component-api.mjs && node scripts/enrich-manifest.mjs && node scripts/build-tokens.mjs && node scripts/audit-tokens.mjs --forbidden && node scripts/check-typography-discipline.mjs --overrides-only && node scripts/build-handoff.mjs && node scripts/build-design-md.mjs && node scripts/generate-llms-txt.mjs && node scripts/generate-consumer-skill.mjs && node scripts/generate-agents-md.mjs && node scripts/build-readme-counts.mjs",
|
|
398
421
|
"tokens:audit": "node scripts/audit-component-integrity.mjs --tokens",
|
|
399
422
|
"tokens:build": "node scripts/build-hds-tokens.mjs",
|
|
400
423
|
"tokens:index": "node scripts/build-token-index.mjs",
|
|
@@ -406,6 +429,9 @@
|
|
|
406
429
|
"watch:guardrails": "nodemon --watch CLAUDE.md --watch .cursorrules --watch eslint.config.js --watch .github/workflows -e md,json,js,yml,yaml --exec \"node scripts/audit-guardrails.js\"",
|
|
407
430
|
"watch:metrics": "nodemon --watch src/app/components --watch src/app/pages --watch src/app/design-system --watch src/app/styles -e tsx,ts,css --exec \"node scripts/audit-tokens.mjs --full && node scripts/generate-manifest.mjs\"",
|
|
408
431
|
"consumer:usage": "node scripts/count-consumer-usage.mjs",
|
|
409
|
-
"
|
|
432
|
+
"upgrade:consumers": "node scripts/upgrade/consumers.mjs",
|
|
433
|
+
"codemod:names": "node scripts/build-codemod-pattern-names.mjs",
|
|
434
|
+
"agents:generate": "node scripts/generate-agents-md.mjs",
|
|
435
|
+
"check:agents-md": "node scripts/generate-agents-md.mjs --check"
|
|
410
436
|
}
|
|
411
437
|
}
|