@grampro/kit 2.7.2 → 2.7.3

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.
Files changed (75) hide show
  1. package/.gbs/skills/gbs-components/SKILL.md +233 -209
  2. package/.gbs/skills/gbs-components/references/data-grid.md +9 -0
  3. package/.gbs/skills/gbs-components/references/forms.md +12 -0
  4. package/.gbs/skills/gbs-components/references/install.md +14 -3
  5. package/CHANGELOG.md +963 -861
  6. package/README.md +11 -3
  7. package/index.cjs +108 -123
  8. package/package.json +1 -1
  9. package/source/components/accordion/passport.json +1 -1
  10. package/source/components/alert/passport.json +1 -1
  11. package/source/components/avatar/passport.json +1 -1
  12. package/source/components/badge/passport.json +1 -1
  13. package/source/components/breadcrumb/passport.json +1 -1
  14. package/source/components/button/passport.json +1 -1
  15. package/source/components/card/passport.json +1 -1
  16. package/source/components/checkbox/passport.json +2 -2
  17. package/source/components/checkbox/passport.manual.json +1 -1
  18. package/source/components/checkbox/react/Checkbox.tsx +14 -0
  19. package/source/components/combobox/passport.json +3 -2
  20. package/source/components/combobox/passport.manual.json +1 -1
  21. package/source/components/combobox/react/MultiSelect.tsx +13 -0
  22. package/source/components/combobox/react/Select.tsx +13 -0
  23. package/source/components/combobox/react/surface.ts +70 -0
  24. package/source/components/data-grid/passport.json +6 -6
  25. package/source/components/data-grid/react/DataGrid.tsx +353 -351
  26. package/source/components/date-picker/passport.json +3 -2
  27. package/source/components/date-picker/passport.manual.json +1 -1
  28. package/source/components/date-picker/react/DatePicker.tsx +16 -2
  29. package/source/components/date-picker/react/DateRangePicker.tsx +16 -2
  30. package/source/components/date-picker/react/surface.ts +68 -0
  31. package/source/components/dialog/passport.json +1 -1
  32. package/source/components/file-uploader/passport.json +1 -1
  33. package/source/components/input/passport.json +2 -2
  34. package/source/components/input/passport.manual.json +1 -1
  35. package/source/components/input/react/Input.tsx +21 -0
  36. package/source/components/menu/passport.json +1 -1
  37. package/source/components/modal/passport.json +1 -1
  38. package/source/components/number-input/passport.json +2 -2
  39. package/source/components/number-input/passport.manual.json +1 -1
  40. package/source/components/number-input/react/NumberInput.tsx +30 -0
  41. package/source/components/popover/passport.json +1 -1
  42. package/source/components/progress/passport.json +1 -1
  43. package/source/components/radio-group/passport.json +2 -2
  44. package/source/components/radio-group/passport.manual.json +1 -1
  45. package/source/components/radio-group/react/RadioGroup.tsx +33 -0
  46. package/source/components/shared/core/agent/field-executors.ts +173 -137
  47. package/source/components/shared/core/agent/index.ts +21 -0
  48. package/source/components/shared/core/agent/surface.ts +437 -0
  49. package/source/components/shared/core/agent/webmcp.ts +332 -328
  50. package/source/components/shared/core/agent/write.ts +62 -58
  51. package/source/components/shared/index.ts +7 -0
  52. package/source/components/shared/react/AgentSurface.tsx +145 -0
  53. package/source/components/shared/react/agentSurfaceContext.ts +92 -0
  54. package/source/components/shared/react/useFieldAgent.ts +62 -88
  55. package/source/components/shared/version.json +4 -4
  56. package/source/components/shared/version.ts +6 -6
  57. package/source/components/skeleton/passport.json +1 -1
  58. package/source/components/spinner/passport.json +1 -1
  59. package/source/components/switch/passport.json +2 -2
  60. package/source/components/switch/passport.manual.json +1 -1
  61. package/source/components/switch/react/Switch.tsx +16 -0
  62. package/source/components/tabs/passport.json +1 -1
  63. package/source/components/textarea/passport.json +2 -2
  64. package/source/components/textarea/passport.manual.json +1 -1
  65. package/source/components/textarea/react/Textarea.tsx +16 -0
  66. package/source/components/toaster/passport.json +1 -1
  67. package/source/components/tooltip/passport.json +1 -1
  68. package/tools/doctor/baseline.cjs +5 -4
  69. package/tools/mcp/server.cjs +13 -1
  70. package/tools/passport/extract.cjs +20 -65
  71. package/tools/passport/hash.cjs +18 -1
  72. package/tools/passport/index.cjs +4 -2
  73. package/tools/passport/typescript.cjs +220 -0
  74. package/tools/prompt.cjs +205 -0
  75. package/tools/ui.cjs +67 -0
@@ -1,209 +1,233 @@
1
- ---
2
- name: gbs-components
3
- description: Builds UI with the GramproKit component library (@grampro/kit). Use when adding or changing UI in apps that have a component-lib/ folder.
4
- autoAttach: ["src/**/*.tsx", "src/**/*.jsx", "app/**/*.tsx", "components/**/*.tsx"]
5
- ---
6
-
7
- # GBS components (2.x)
8
-
9
- ## If the MCP server is connected
10
-
11
- This file is a summary. The `grampro` MCP server answers from passports
12
- generated from the component source, so where the two differ it is right
13
- and this is stale. When its tools are available:
14
-
15
- - **`get_component <Name>`** before writing a component — every prop with
16
- its type, the values a union accepts, which props are required, handler
17
- signatures. Do not write a prop you have not read there.
18
- - **`validate_usage`** on the JSX before reporting back. It catches props
19
- invented from memory, which is the one failure a passport cannot prevent.
20
- - **`figma_frame`** to read a design, from a `designFile` path or a Figma
21
- URL, when building from one.
22
-
23
- Neither needs the other installed. Without the server this file is what
24
- you have, and everything below still applies.
25
- ## Install before you import
26
-
27
- Not an npm dependency — the CLI **copies source into the repo**:
28
-
29
- ```bash
30
- npx @grampro/kit add Button,Input,Modal
31
- ```
32
-
33
- Writes `component-lib/<folder>/` plus `component-lib/shared/`. No flag is
34
- needed: the 2.x components are the default. A component the project has
35
- edited is never overwritten — if `add` says it kept one, tell the user and
36
- suggest `npx @grampro/kit doctor <Component>`; **never retry with `--force`**,
37
- which discards their work. Import the barrel in your component:
38
-
39
- ```ts
40
- import { Button } from "component-lib/button";
41
- ```
42
-
43
- Styles are **one line** in the global CSS (Vite: `src/index.css`), never in a
44
- component file. The CLI generates `component-lib/gbs.css` listing every
45
- stylesheet you installed, and rewrites it on each install:
46
-
47
- ```css
48
- @import "./component-lib/gbs.css";
49
- ```
50
-
51
- With **Tailwind v4 only**, import it after the framework and into Tailwind's
52
- components layer, or utilities stop overriding component rules:
53
-
54
- ```css
55
- @import "tailwindcss";
56
- @import "./component-lib/gbs.css" layer(components);
57
- ```
58
-
59
- The installer prints whichever line applies. Component CSS is unlayered, so
60
- Tailwind v3, Bootstrap and no-framework projects need nothing extra.
61
-
62
- Folder = lowercased name, except `data-grid`, `date-picker`, `file-uploader`,
63
- `number-input`, `radio-group`.
64
-
65
- ## Rules most often missed
66
-
67
- 1. **Change handlers are named per component.** `onValueChange`: Input,
68
- OtpInput, Textarea, NumberInput, CheckboxGroup, RadioGroup, Tabs, Accordion,
69
- MenuRadioGroup. `onCheckedChange`: Checkbox, Switch, MenuCheckboxItem.
70
- `onChange`: Select, MultiSelect, DatePicker, DateRangePicker, FileUploader.
71
- `onOpenChange`: Modal, Popover, Menu.
72
- 2. **Never hand-roll `<label>`, hint or error markup.** Fields take `label`,
73
- `description` and `error`; `error` also marks the control invalid and wires
74
- `aria-describedby`. A `<label>` wrapper breaks it.
75
- 3. **Style via `classNames` slots** (`classNames={{ root, label }}`);
76
- `className` hits the root only. Theme globally with `--gbs-*` on `:root`.
77
- 4. **Mount `<Toaster />` and `<DialogHost />` once at the app root**, or
78
- `toast()` and `dialog.confirm()` do nothing.
79
- 5. **All are client components** (`"use client"`); a Next.js Server Component
80
- can render them but not pass function props.
81
- 6. **Compound families throw outside their parent**: Tab/TabList/TabPanel need
82
- Tabs, AccordionItem needs Accordion, MenuItem needs Menu.
83
-
84
- ## Inventory (required props in parens)
85
-
86
- **Forms** — Input, Textarea, NumberInput (locale-aware), OtpInput; Checkbox +
87
- CheckboxGroup; Radio (`value`) + RadioGroup; Switch (applies immediately);
88
- Select and MultiSelect (`options`; searchable, client or server); DatePicker and
89
- DateRangePicker; FileUploader (chunked).
90
-
91
- **Actions** — Button: variants, sizes, icons, auto-loading from a returned
92
- promise; `render` draws a router link.
93
-
94
- **Overlays** — Modal (native `<dialog>`, also drawers); `dialog` + DialogHost
95
- (`await dialog.confirm/alert/prompt`); Popover (`trigger`); Menu (`trigger`) with
96
- MenuItem, MenuCheckboxItem, MenuRadioGroup, MenuRadioItem, MenuGroup,
97
- MenuSeparator, MenuSub; Tooltip (`content`); `toast` + Toaster.
98
-
99
- **Data** — DataGrid (`data`, `columns`) + createColumnHelper: virtualized;
100
- sort, filter, edit, CSV/Excel/PDF export. Optional agent surface: `ai` renders a
101
- natural-language box, and `registerGridTool` exposes the grid to a browser agent
102
- over WebMCP (see "Agent surface").
103
-
104
- **Display** — Tabs/TabList/Tab (`value`)/TabPanel (`value`); Accordion +
105
- AccordionItem (`value`); Card/CardHeader/CardBody/CardFooter/Stat; Alert; Badge
106
- and Tag; Avatar and AvatarGroup; Progress and CircularProgress (`value={null}`
107
- is indeterminate); Skeleton and Empty; Spinner; Breadcrumb (`items`,
108
- `renderLink`).
109
-
110
- ## Example
111
-
112
- ```tsx
113
- "use client";
114
- import { useState } from "react";
115
- import { Button } from "component-lib/button";
116
- import { Input } from "component-lib/input";
117
- import { Select } from "component-lib/combobox";
118
- import { Modal } from "component-lib/modal";
119
- import { toast } from "component-lib/toaster";
120
-
121
- const ROLES = [{ value: "admin", label: "Admin" }, { value: "dev", label: "Dev" }];
122
-
123
- export function InviteButton() {
124
- const [open, setOpen] = useState(false);
125
- const [email, setEmail] = useState("");
126
- const [role, setRole] = useState<string | null>(null);
127
- const invalid = !!email && !email.includes("@");
128
-
129
- return (
130
- <>
131
- <Button onClick={() => setOpen(true)}>Invite</Button>
132
- <Modal open={open} onOpenChange={setOpen} title="Invite a teammate"
133
- footer={({ close }) => (
134
- <Button disabled={!email || invalid} onClick={async () => {
135
- await invite({ email, role });
136
- toast.success("Invitation sent");
137
- close();
138
- }}>Send</Button>
139
- )}>
140
- <Input label="Email" type="email" value={email} onValueChange={setEmail}
141
- required error={invalid ? "Enter a valid email" : undefined} />
142
- <Select label="Role" options={ROLES} value={role} onChange={setRole} />
143
- </Modal>
144
- </>
145
- );
146
- }
147
- ```
148
-
149
- ## Agent surface (DataGrid, optional)
150
-
151
- Entirely opt-in. Without it, nothing changes and nothing is downloaded.
152
-
153
- **A person typing.** `ai` renders a box above the grid. It needs a provider
154
- supplying an adapter; with no provider it renders nothing at all.
155
-
156
- ```tsx
157
- import { GramproAIProvider } from "component-lib/shared";
158
- import { DataGrid } from "component-lib/data-grid";
159
-
160
- <GramproAIProvider adapter={myAdapter}>
161
- <DataGrid data={rows} columns={cols} ai semantics={SEMANTICS} />
162
- </GramproAIProvider>
163
- ```
164
-
165
- `adapter` is one function — `(utterance, contract, responseSchema) => envelope`.
166
- No model is bundled, downloaded or named; the app brings its own, and the key
167
- belongs on the app's server, never in client JavaScript.
168
-
169
- **A browser agent.** `registerGridTool(agent)` registers one WebMCP tool,
170
- `operate_grid`, whose schema is generated from the live grid. Requires no model
171
- at all.
172
-
173
- **Both go through the same validator**, which refuses an unknown column, an
174
- operator the type lacks, a policy-denied column, or an irreversible operation
175
- with no confirmation — and returns the reason. A producer proposes; it never
176
- acts. Do not add repair, retries or a second execution path.
177
-
178
- **Data leaves the page with a remote adapter.** The contract carries real
179
- values from low-cardinality columns, so the model can map "Kerala" to `region`.
180
- Mark personal columns `semantics: { email: { pii: true } }` — a PII column is
181
- denied *and* never summarised — or pass `stats: false` to summarise nothing.
182
- `policy.denyFilter` does **not** stop a column being described.
183
-
184
- **`semantics` is where most quality comes from**: per column, give `synonyms`,
185
- `description`, `unit`, `higherIsBetter`, `pii`. Reach for it before reaching for
186
- a bigger model.
187
-
188
- ## Don't
189
-
190
- - Raw `<button>`, `<input>`, `<select>`, `<table>`, `<dialog>`, or a hand-built
191
- modal/menu/tooltip, when a component exists.
192
- - Deep imports (`.../input/react/Input`); import the barrel. Only `<name>/core`
193
- is also public.
194
- - Editing `component-lib/shared/`; the CLI replaces it on update.
195
- - Invented props: no `asChild`, no `variant` on Input, no `onChange` on Switch.
196
- - `!important` or internal class selectors; set `--gbs-*` or pass `classNames`.
197
- - A Tooltip as a name; icon buttons need `aria-label`.
198
-
199
- ## Details (in `references/`)
200
-
201
- - `install.md` — CLI flags, folders, 1.x vs 2.x.
202
- - `forms.md` — fields, controlled state, form posting.
203
- - `pickers.md` — Select, MultiSelect, DatePicker, Uploader.
204
- - `overlays.md` — Modal, dialog, Popover, Menu, Tooltip.
205
- - `toaster.md` — `toast()` and `<Toaster />`.
206
- - `data-grid.md` — columns, API, export.
207
- - `data-display.md` — Card, Tabs, Accordion, Badge, Avatar.
208
- - `styling.md` — tokens, layers, slots, `data-*`, dark.
209
- - `accessibility.md` — what is supplied, what you add.
1
+ ---
2
+ name: gbs-components
3
+ description: Builds UI with the GramproKit component library (@grampro/kit). Use when adding or changing UI in apps that have a component-lib/ folder.
4
+ autoAttach: ["src/**/*.tsx", "src/**/*.jsx", "app/**/*.tsx", "components/**/*.tsx"]
5
+ ---
6
+
7
+ # GBS components (2.x)
8
+
9
+ ## If the MCP server is connected
10
+
11
+ This file is a summary. The `grampro` MCP server answers from passports
12
+ generated from the component source, so where the two differ it is right
13
+ and this is stale. When its tools are available:
14
+
15
+ - **`get_component <Name>`** before writing a component — every prop with
16
+ its type, the values a union accepts, which props are required, handler
17
+ signatures. Do not write a prop you have not read there.
18
+ - **`validate_usage`** on the JSX before reporting back. It catches props
19
+ invented from memory, which is the one failure a passport cannot prevent.
20
+ - **`figma_frame`** to read a design, from a `designFile` path or a Figma
21
+ URL, when building from one.
22
+
23
+ Neither needs the other installed. Without the server this file is what
24
+ you have, and everything below still applies.
25
+ ## Install before you import
26
+
27
+ Not an npm dependency — the CLI **copies source into the repo**:
28
+
29
+ ```bash
30
+ npx @grampro/kit add Button,Input,Modal
31
+ ```
32
+
33
+ Writes `component-lib/<folder>/` plus `component-lib/shared/`. No flag is
34
+ needed: the 2.x components are the default. A component the project has
35
+ edited is never overwritten — if `add` says it kept one, tell the user and
36
+ suggest `npx @grampro/kit doctor <Component>`; **never retry with `--force`**,
37
+ which discards their work. Import the barrel in your component:
38
+
39
+ ```ts
40
+ import { Button } from "component-lib/button";
41
+ ```
42
+
43
+ Styles are **one line** in the global CSS (Vite: `src/index.css`), never in a
44
+ component file. The CLI generates `component-lib/gbs.css` listing every
45
+ stylesheet you installed, and rewrites it on each install:
46
+
47
+ ```css
48
+ @import "./component-lib/gbs.css";
49
+ ```
50
+
51
+ With **Tailwind v4 only**, import it after the framework and into Tailwind's
52
+ components layer, or utilities stop overriding component rules:
53
+
54
+ ```css
55
+ @import "tailwindcss";
56
+ @import "./component-lib/gbs.css" layer(components);
57
+ ```
58
+
59
+ The installer prints whichever line applies. Component CSS is unlayered, so
60
+ Tailwind v3, Bootstrap and no-framework projects need nothing extra.
61
+
62
+ Folder = lowercased name, except `data-grid`, `date-picker`, `file-uploader`,
63
+ `number-input`, `radio-group`.
64
+
65
+ ## Rules most often missed
66
+
67
+ 1. **Change handlers are named per component.** `onValueChange`: Input,
68
+ OtpInput, Textarea, NumberInput, CheckboxGroup, RadioGroup, Tabs, Accordion,
69
+ MenuRadioGroup. `onCheckedChange`: Checkbox, Switch, MenuCheckboxItem.
70
+ `onChange`: Select, MultiSelect, DatePicker, DateRangePicker, FileUploader.
71
+ `onOpenChange`: Modal, Popover, Menu.
72
+ 2. **Never hand-roll `<label>`, hint or error markup.** Fields take `label`,
73
+ `description` and `error`; `error` also marks the control invalid and wires
74
+ `aria-describedby`. A `<label>` wrapper breaks it.
75
+ 3. **Style via `classNames` slots** (`classNames={{ root, label }}`);
76
+ `className` hits the root only. Theme globally with `--gbs-*` on `:root`.
77
+ 4. **Mount `<Toaster />` and `<DialogHost />` once at the app root**, or
78
+ `toast()` and `dialog.confirm()` do nothing.
79
+ 5. **All are client components** (`"use client"`); a Next.js Server Component
80
+ can render them but not pass function props.
81
+ 6. **Compound families throw outside their parent**: Tab/TabList/TabPanel need
82
+ Tabs, AccordionItem needs Accordion, MenuItem needs Menu.
83
+
84
+ ## Inventory (required props in parens)
85
+
86
+ **Forms** — Input, Textarea, NumberInput (locale-aware), OtpInput; Checkbox +
87
+ CheckboxGroup; Radio (`value`) + RadioGroup; Switch (applies immediately);
88
+ Select and MultiSelect (`options`; searchable, client or server); DatePicker and
89
+ DateRangePicker; FileUploader (chunked).
90
+
91
+ **Actions** — Button: variants, sizes, icons, auto-loading from a returned
92
+ promise; `render` draws a router link.
93
+
94
+ **Overlays** — Modal (native `<dialog>`, also drawers); `dialog` + DialogHost
95
+ (`await dialog.confirm/alert/prompt`); Popover (`trigger`); Menu (`trigger`) with
96
+ MenuItem, MenuCheckboxItem, MenuRadioGroup, MenuRadioItem, MenuGroup,
97
+ MenuSeparator, MenuSub; Tooltip (`content`); `toast` + Toaster.
98
+
99
+ **Data** — DataGrid (`data`, `columns`) + createColumnHelper: virtualized;
100
+ sort, filter, edit, CSV/Excel/PDF export. Optional agent surface: `ai` renders a
101
+ natural-language box, and `registerGridTool` exposes the grid to a browser agent
102
+ over WebMCP (see "Agent surface"). Inside an `<AgentSurface>` the grid joins the
103
+ page's one tool instead (see "Page surface").
104
+
105
+ **Display** — Tabs/TabList/Tab (`value`)/TabPanel (`value`); Accordion +
106
+ AccordionItem (`value`); Card/CardHeader/CardBody/CardFooter/Stat; Alert; Badge
107
+ and Tag; Avatar and AvatarGroup; Progress and CircularProgress (`value={null}`
108
+ is indeterminate); Skeleton and Empty; Spinner; Breadcrumb (`items`,
109
+ `renderLink`).
110
+
111
+ ## Example
112
+
113
+ ```tsx
114
+ "use client";
115
+ import { useState } from "react";
116
+ import { Button } from "component-lib/button";
117
+ import { Input } from "component-lib/input";
118
+ import { Select } from "component-lib/combobox";
119
+ import { Modal } from "component-lib/modal";
120
+ import { toast } from "component-lib/toaster";
121
+
122
+ const ROLES = [{ value: "admin", label: "Admin" }, { value: "dev", label: "Dev" }];
123
+
124
+ export function InviteButton() {
125
+ const [open, setOpen] = useState(false);
126
+ const [email, setEmail] = useState("");
127
+ const [role, setRole] = useState<string | null>(null);
128
+ const invalid = !!email && !email.includes("@");
129
+
130
+ return (
131
+ <>
132
+ <Button onClick={() => setOpen(true)}>Invite</Button>
133
+ <Modal open={open} onOpenChange={setOpen} title="Invite a teammate"
134
+ footer={({ close }) => (
135
+ <Button disabled={!email || invalid} onClick={async () => {
136
+ await invite({ email, role });
137
+ toast.success("Invitation sent");
138
+ close();
139
+ }}>Send</Button>
140
+ )}>
141
+ <Input label="Email" type="email" value={email} onValueChange={setEmail}
142
+ required error={invalid ? "Enter a valid email" : undefined} />
143
+ <Select label="Role" options={ROLES} value={role} onChange={setRole} />
144
+ </Modal>
145
+ </>
146
+ );
147
+ }
148
+ ```
149
+
150
+ ## Page surface (optional): `<AgentSurface>`
151
+
152
+ To make a page operable by a browser agent, wrap it — do not write WebMCP tools
153
+ by hand:
154
+
155
+ ```tsx
156
+ import { AgentSurface } from "component-lib/shared";
157
+
158
+ <AgentSurface>
159
+ <Input label="Name" maxLength={80} />
160
+ <Select label="Team" options={teams} />
161
+ <DataGrid aria-label="Customers" data={rows} columns={cols} />
162
+ </AgentSurface>
163
+ ```
164
+
165
+ Input, Textarea, NumberInput, Checkbox, Switch, RadioGroup, Select,
166
+ MultiSelect, DatePicker, DateRangePicker and DataGrid register themselves, with
167
+ what they accept, and one tool `operate_page` (`describe`, then `operate` by
168
+ id) is registered when WebMCP exists. Ids come from the label, so give every
169
+ field a text `label` (a grid an `aria-label`). Every action goes through the
170
+ component's own validator; password, hidden and file inputs are never offered.
171
+ `useAgentSurface()` returns the same surface for an app's own agent loop.
172
+
173
+ ## Agent surface (DataGrid, optional)
174
+
175
+ Entirely opt-in. Without it, nothing changes and nothing is downloaded.
176
+
177
+ **A person typing.** `ai` renders a box above the grid. It needs a provider
178
+ supplying an adapter; with no provider it renders nothing at all.
179
+
180
+ ```tsx
181
+ import { GramproAIProvider } from "component-lib/shared";
182
+ import { DataGrid } from "component-lib/data-grid";
183
+
184
+ <GramproAIProvider adapter={myAdapter}>
185
+ <DataGrid data={rows} columns={cols} ai semantics={SEMANTICS} />
186
+ </GramproAIProvider>
187
+ ```
188
+
189
+ `adapter` is one function — `(utterance, contract, responseSchema) => envelope`.
190
+ No model is bundled, downloaded or named; the app brings its own, and the key
191
+ belongs on the app's server, never in client JavaScript.
192
+
193
+ **A browser agent.** `registerGridTool(agent)` registers one WebMCP tool,
194
+ `operate_grid`, whose schema is generated from the live grid. Requires no model
195
+ at all.
196
+
197
+ **Both go through the same validator**, which refuses an unknown column, an
198
+ operator the type lacks, a policy-denied column, or an irreversible operation
199
+ with no confirmation — and returns the reason. A producer proposes; it never
200
+ acts. Do not add repair, retries or a second execution path.
201
+
202
+ **Data leaves the page with a remote adapter.** The contract carries real
203
+ values from low-cardinality columns, so the model can map "Kerala" to `region`.
204
+ Mark personal columns `semantics: { email: { pii: true } }` — a PII column is
205
+ denied *and* never summarised — or pass `stats: false` to summarise nothing.
206
+ `policy.denyFilter` does **not** stop a column being described.
207
+
208
+ **`semantics` is where most quality comes from**: per column, give `synonyms`,
209
+ `description`, `unit`, `higherIsBetter`, `pii`. Reach for it before reaching for
210
+ a bigger model.
211
+
212
+ ## Don't
213
+
214
+ - Raw `<button>`, `<input>`, `<select>`, `<table>`, `<dialog>`, or a hand-built
215
+ modal/menu/tooltip, when a component exists.
216
+ - Deep imports (`.../input/react/Input`); import the barrel. Only `<name>/core`
217
+ is also public.
218
+ - Editing `component-lib/shared/`; the CLI replaces it on update.
219
+ - Invented props: no `asChild`, no `variant` on Input, no `onChange` on Switch.
220
+ - `!important` or internal class selectors; set `--gbs-*` or pass `classNames`.
221
+ - A Tooltip as a name; icon buttons need `aria-label`.
222
+
223
+ ## Details (in `references/`)
224
+
225
+ - `install.md` — CLI flags, folders, 1.x vs 2.x.
226
+ - `forms.md` — fields, controlled state, form posting.
227
+ - `pickers.md` — Select, MultiSelect, DatePicker, Uploader.
228
+ - `overlays.md` — Modal, dialog, Popover, Menu, Tooltip.
229
+ - `toaster.md` — `toast()` and `<Toaster />`.
230
+ - `data-grid.md` — columns, API, export.
231
+ - `data-display.md` — Card, Tabs, Accordion, Badge, Avatar.
232
+ - `styling.md` — tokens, layers, slots, `data-*`, dark.
233
+ - `accessibility.md` — what is supplied, what you add.
@@ -114,3 +114,12 @@ use `print()` for those.
114
114
 
115
115
  Set `mode="server"`, pass the current page as `data` and the total as
116
116
  `rowCount`, and fetch in `onQueryChange`.
117
+
118
+ ## Inside an AgentSurface
119
+
120
+ A DataGrid inside `<AgentSurface>` builds its agent and joins the page's one
121
+ WebMCP tool (`operate_page`) by itself, with its columns and operations; there
122
+ is no need to call `createGridAgent` or `registerGridTool`. Give it an
123
+ `aria-label` — it becomes the grid's id there. `semantics` and `agentPolicy`
124
+ apply exactly as with `ai`. Outside a surface and without `ai`, no agent is
125
+ built.
@@ -200,3 +200,15 @@ There is no form library here. Compute the message yourself and pass it as
200
200
  `error`; the component handles `aria-invalid`, `aria-describedby` and the
201
201
  styling. With React Hook Form or similar, wire `value` and the component's own
202
202
  handler name, and pass `error={errors.field?.message}`.
203
+
204
+ ## Operable by a browser agent
205
+
206
+ Wrap the form in `<AgentSurface>` (from `shared`) and every field in it joins
207
+ one WebMCP tool, `operate_page`, describing what it accepts from its own props:
208
+ `maxLength`, `min`/`max`, options and which are disabled. Nothing else to
209
+ write. Give each field a text `label`: the label becomes its id
210
+ (`"Due date"` → `due-date`), and a field labelled only by a ReactNode falls
211
+ back to `aria-label`, then `name`, then `placeholder`. Password, hidden and file inputs are never
212
+ offered. Writes fire the same handlers a person's input does, so controlled
213
+ state updates as usual. Without WebMCP, `useAgentSurface()` returns the same
214
+ surface for the app's own agent loop.
@@ -18,7 +18,7 @@ other runtime dependencies.
18
18
  ```bash
19
19
  npx @grampro/kit add Button # one component
20
20
  npx @grampro/kit add Button,Input,Modal # several
21
- npx @grampro/kit -i # interactive picker
21
+ npx @grampro/kit add # interactive picker (a person at a terminal)
22
22
  npx @grampro/kit -l # list available
23
23
  npx @grampro/kit doctor # what changed locally and upstream
24
24
  npx @grampro/kit doctor Button --json # one component, for reading programmatically
@@ -29,7 +29,7 @@ npx @grampro/kit skill --for claude # only the Claude Code copy
29
29
  | Flag | Alias | Meaning |
30
30
  | --- | --- | --- |
31
31
  | `--add` | `-a` | Component, or comma-separated list. Case-insensitive. |
32
- | `--interactive` | `-i` | Pick from a menu. |
32
+ | `--interactive` | `-i` | Pick from a menu; same as `add` with no names. Needs a terminal. |
33
33
  | `--list` | `-l` | Print available components. |
34
34
  | `--legacy` | — | Install from the 1.x set instead. Unmaintained; see below. |
35
35
  | `--skill` | — | Install this skill at the project root. `-skill` and `-a skill` also work. |
@@ -37,6 +37,11 @@ npx @grampro/kit skill --for claude # only the Claude Code copy
37
37
  | `--force` | — | Replace a component, `shared/` or `.gbs/` even though the project has edited it. Never pass it on the user's behalf. |
38
38
  | `--json` | — | With `doctor`: print the report as JSON. |
39
39
 
40
+ **An agent always names the components.** The picker needs a real terminal:
41
+ run without one, `add` with no names only prints a hint and `-i` exits with an
42
+ error, so neither installs anything. Output is in colour only on a terminal,
43
+ and never with `NO_COLOR` set — read it piped and it is plain text.
44
+
40
45
  ## Components the project has changed
41
46
 
42
47
  Copied components are meant to be edited, so `add` treats an edited one as
@@ -168,6 +173,10 @@ Rules the installer enforces:
168
173
 
169
174
  - `shared/` only moves forward. If the project has a newer version than the CLI
170
175
  carries, the install aborts rather than downgrading it.
176
+ - The same version is updated anyway when its files differ from the CLI's, so a
177
+ release that changed `shared/` without a version bump still reaches the
178
+ project. (2.7.0 did; projects that had shared 1.3.0 kept the old files until
179
+ 2.7.3, which is shared 1.4.0.)
171
180
  - A file you edited since the CLI wrote it is never replaced without `--force`.
172
181
  It tracks this with sha256 hashes in `component-lib/shared/.install-manifest.json`.
173
182
 
@@ -176,7 +185,9 @@ is replaced on update. Put your changes in your own module instead.
176
185
 
177
186
  `shared` exports `cx`, `countCharacters`, `describeField`,
178
187
  `useControllableState`, `AnchoredPopover`, `placePopover`, `icons` and
179
- `sharedVersion`.
188
+ `sharedVersion`; the agent runtime (`createFieldAgent`, `useFieldAgent`,
189
+ `GramproAIProvider`); and `AgentSurface` with `useAgentSurface`, which make a
190
+ page operable by a browser agent (see SKILL.md, "Page surface").
180
191
 
181
192
  ## Importing
182
193