@mieweb/ui 0.11.0-dev.17 → 0.11.0-dev.18

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/agent/README.md CHANGED
@@ -1,20 +1,193 @@
1
- # agent/ — AI Agent Rules Shipped with @mieweb/ui
1
+ # agent/ — AI Agent Team Shipped with @mieweb/ui
2
2
 
3
- Rules that teach AI coding agents (Copilot, Claude Code, Cursor, Codex, …) to
4
- use `@mieweb/ui` components — most importantly, **DataVis NITRO for all
5
- tables** — instead of inventing their own UI.
3
+ Rules and agents that teach AI coding agents (Copilot, Claude Code, Cursor,
4
+ Codex, …) to use `@mieweb/ui` components — most importantly, **DataVis NITRO
5
+ for all tables** — instead of inventing their own UI.
6
6
 
7
- Agents don't read files inside `node_modules`, so consumers install the rules
7
+ Agents don't read files inside `node_modules`, so consumers install everything
8
8
  into their repo with:
9
9
 
10
10
  ```bash
11
11
  npx @mieweb/ui init-agent
12
12
  ```
13
13
 
14
+ This directory is growing from a single instructions file into the **MIE UI
15
+ Design Agent team** — an orchestrator plus specialist subagents and skills —
16
+ per epic [#545](https://github.com/mieweb/ui/issues/545). This README is the
17
+ governing spec: later tickets cite it instead of re-deciding.
18
+
19
+ ## Files
20
+
14
21
  | File | Purpose |
15
22
  | ------------------------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
16
23
  | [mieweb-ui.instructions.md](mieweb-ui.instructions.md) | The rules template. Copied verbatim to the consumer's `.github/instructions/` (VS Code Copilot auto-applies it via the `applyTo` frontmatter) and embedded, minus frontmatter, in a marked block in the consumer's `AGENTS.md` (the cross-tool convention). |
17
- | [init-agent.mjs](init-agent.mjs) | The `mieweb-ui` bin. Idempotent — rerun after upgrading `@mieweb/ui` to refresh both targets. |
24
+ | [init-agent.mjs](init-agent.mjs) | The `mieweb-ui` bin. Idempotent — rerun after upgrading `@mieweb/ui` to refresh all targets. `--experimental-agents` additionally installs the agent team below. |
25
+ | [agents/](agents/) | Agent templates: `mieweb-ui-design` (orchestrator) + `mieweb-ui-styling`. Installed to the consumer's `.github/agents/` behind `--experimental-agents`. |
26
+ | [skills/](skills/) | Skill templates: `mieweb-ui-catalog`, `mieweb-ui-tokens`, `mieweb-ui-icons` (generated — `pnpm agent:skills`, never hand-edit) + `mieweb-ui-brands` (hand-written). Installed to `.github/skills/` behind `--experimental-agents`. |
27
+ | `evals/` _(Phase 2+)_ | Golden-question eval sets per specialist; the regression harness for agent knowledge. |
18
28
 
19
29
  Rule content is sourced from [lessons/component-policy.md](../lessons/component-policy.md);
20
30
  keep the two in sync when policy changes.
31
+
32
+ ## Governing rules
33
+
34
+ Decided in [#545](https://github.com/mieweb/ui/issues/545) (design principles +
35
+ execution decisions). Summarized here as the citable spec.
36
+
37
+ 1. **Persona-or-facts test.** A domain earns an _agent seat_ only if it needs a
38
+ persona and workflow (audit procedure, diagnostic method, judgment). If it
39
+ only needs _facts_, it's a `SKILL.md`. Taxonomy buckets ("primitives",
40
+ "complex components") are not domains.
41
+ 2. **Agents = behavior, skills = facts.** Deep reference material lives in
42
+ skills loaded on demand, never inlined into agent prompts.
43
+ 3. **Thin orchestrator owns selection.** The orchestrator answers component
44
+ selection/usage itself from the catalog skill and routes everything else
45
+ (see routing spec below). It is not a pure switchboard.
46
+ 4. **Auditors are read-only.** Audit-persona agents (A11y, i18n/RTL,
47
+ Migration/Compliance) get no edit tools; they produce a report plus an
48
+ "Apply Fixes" handoff. Fixer personas (Styling & Theming) may edit.
49
+ 5. **Single source of truth.** Shipped files are synced from `lessons/`
50
+ (canonical prose) or generated from source (tokens from the Tailwind preset,
51
+ catalog from exports + `src/catalog/*.mdx`). No third drifting copy.
52
+ 6. **Promotion rule.** Knowledge starts as a skill on the orchestrator; it is
53
+ promoted to an agent seat only when dogfooding/routing data shows demand for
54
+ a dedicated workflow.
55
+ 7. **Consumer team vs maintainer team.** `agent/` templates ship to consumers.
56
+ Maintainer agents (PR checks, release, story authoring) live in this repo's
57
+ `.github/agents/` and are never shipped.
58
+ 8. **Tool-agnostic distribution.** VS Code/Copilot gets `.agent.md` +
59
+ `SKILL.md`; tools without native subagents get the `AGENTS.md` marked block
60
+ extended with an agent roster that references the installed files by path.
61
+ 9. **Experimental gating.** Until the epic completes, agents/skills/roster
62
+ install **only** with `init-agent --experimental-agents`. The default run
63
+ stays byte-identical to today's behavior (instructions file + `AGENTS.md`
64
+ block). The default flips on in P5.2
65
+ ([#607](https://github.com/mieweb/ui/issues/607)) — the official launch.
66
+
67
+ ## Naming & layout
68
+
69
+ - Every shipped artifact is prefixed **`mieweb-ui-`** so consumer repos can
70
+ distinguish managed files from their own at a glance.
71
+ - Template layout in this repo → installed location in the consumer:
72
+
73
+ | Template (this repo) | Installed (consumer repo) |
74
+ | ---------------------------------------- | ------------------------------------------------------------------------- |
75
+ | `agent/mieweb-ui.instructions.md` | `.github/instructions/mieweb-ui.instructions.md` |
76
+ | `agent/agents/mieweb-ui-<name>.agent.md` | `.github/agents/mieweb-ui-<name>.agent.md` |
77
+ | `agent/skills/mieweb-ui-<name>/SKILL.md` | `.github/skills/mieweb-ui-<name>/SKILL.md` |
78
+ | _(embedded block)_ | `AGENTS.md` between `<!-- mieweb-ui:begin -->` / `<!-- mieweb-ui:end -->` |
79
+
80
+ - Agent display names: `MIE UI <Domain> Agent` (e.g. `MIE UI Styling Agent`);
81
+ the orchestrator is `MIE UI Design Agent`.
82
+ - Generated skills are produced by `scripts/` generators into `agent/skills/`
83
+ at build time and committed, so the installer stays a dumb file copier.
84
+
85
+ ## Managed-file markers
86
+
87
+ Every **newly shipped** whole-file artifact (agents, skills) carries a managed
88
+ marker so reruns can refresh safely:
89
+
90
+ ```
91
+ <!-- mieweb-ui:managed @mieweb/ui@X.Y.Z -->
92
+ ```
93
+
94
+ - **Scope:** marker-based refresh applies to `--experimental-agents` artifacts
95
+ only. The two legacy targets keep their existing behavior unchanged (rule
96
+ 9's byte-identical guarantee): `.github/instructions/mieweb-ui.instructions.md`
97
+ is overwritten verbatim on every run (as today), and `AGENTS.md` uses its own
98
+ `begin`/`end` block markers.
99
+ - **Placement:** first line of the file body — immediately after the YAML
100
+ frontmatter block if present, otherwise line 1. (Frontmatter must stay at
101
+ byte 0 for VS Code to parse it.)
102
+ - **Refresh rules (idempotent rerun):**
103
+ - Target file exists **with** marker → overwrite with the new template
104
+ (marker version updated).
105
+ - Target file exists **without** marker → **skip and warn**: it's
106
+ user-created; never clobber it.
107
+ - Template removed in a newer release but installed file still present →
108
+ **report, never delete**.
109
+ - `AGENTS.md`: only the content between the `begin`/`end` markers is
110
+ replaced; everything outside is untouched.
111
+
112
+ ## `.agent.md` frontmatter baseline
113
+
114
+ All shipped agents conform to:
115
+
116
+ ```yaml
117
+ ---
118
+ name: MIE UI <Domain> Agent
119
+ description: <one sentence: domain + when to invoke; this is the routing surface>
120
+ tools: [<minimal set for the persona>]
121
+ # auditors: ['search', 'codebase', 'fetch'] — no edit/terminal tools
122
+ # fixers: add 'editFiles'
123
+ # diagnosticians: add 'runInTerminal' only if the workflow runs commands
124
+ handoffs: # where useful — auditors always include an "Apply Fixes" handoff
125
+ - label: Apply Fixes
126
+ agent: agent
127
+ prompt: Apply the fixes identified above.
128
+ send: false
129
+ ---
130
+ ```
131
+
132
+ - **No pinned `model:`** — agents must be model-agnostic.
133
+ - **`description` is the routing surface**: it must state the domain and the
134
+ invoke-when, because orchestrators and humans both select on it.
135
+ - **Tools are the persona contract**: read-only auditors cannot hold edit
136
+ tools (governing rule 4).
137
+
138
+ ### Audit: existing `.github/agents/style.agent.md` vs baseline
139
+
140
+ | Check | Status | Notes |
141
+ | ---------------- | ------ | ------------------------------------------------------------------------------------------------------------------------------------------------------ |
142
+ | No pinned model | ❌ | Pins `model: Claude Sonnet 4` — remove when evolved in P1.5 (#599) |
143
+ | Minimal tools | ⚠️ | Holds `editFiles` + `runInTerminal` + `terminalLastCommand`; Styling is a fixer so `editFiles` stays, terminal tools drop unless a workflow needs them |
144
+ | Handoffs pattern | ✅ | "Apply Fixes" handoff is the team-wide template |
145
+ | Naming | ❌ | `Style Agent` → `MIE UI Styling Agent`, file → `mieweb-ui-styling.agent.md` (P1.5) |
146
+ | Facts in skills | ❌ | Inlines token tables — dedupe into the generated tokens skill (P1.2, #600) |
147
+
148
+ The original `.github/agents/style.agent.md` stays in place (maintainer use)
149
+ until P1.5 ships its consumer replacement.
150
+
151
+ ## Orchestrator routing spec
152
+
153
+ The orchestrator (`MIE UI Design Agent`) answers **directly** from the catalog
154
+ skill: component selection ("Card or Panel?", "which table component?" —
155
+ propose DataVis NITRO first per Rule 1 of
156
+ [mieweb-ui.instructions.md](mieweb-ui.instructions.md); `Table` only for its
157
+ documented static-rows exception), basic props/variants/composition usage,
158
+ high-level architecture (headless vs styled, preset vs TW4 `@source`), and
159
+ "where do I find X".
160
+
161
+ Everything else is delegated per this table (final domain boundaries):
162
+
163
+ | Question is about… | Delegate to |
164
+ | -------------------------------------------------- | ---------------------- |
165
+ | install / peer deps / preset / safelist / @source | MIE UI Install Agent |
166
+ | tokens, CSS vars, Tailwind classes, brands, dark | MIE UI Styling Agent |
167
+ | ARIA, keyboard nav, screen readers, contrast | MIE UI A11y Agent |
168
+ | translations, locale bundles, Intl formats, RTL | MIE UI i18n Agent |
169
+ | migrating code to @mieweb/ui, raw-HTML compliance | MIE UI Migration Agent |
170
+ | DataVis NITRO, eSheet, grids, virtualization | MIE UI Grids Agent |
171
+ | charts, maps, globes, sparklines, report templates | MIE UI DataVis Agent |
172
+ | ViewSet, dashboards, panels, app shell layout | MIE UI Views Agent |
173
+ | SuperChat, Inbox, AI messages, media conversation | MIE UI SuperChat Agent |
174
+ | rich text editors, Kerebron, Markdown | MIE UI Rich Text Agent |
175
+
176
+ **Tie-breakers for ambiguous cases:**
177
+
178
+ - _Selection vs depth:_ "which component" is always the orchestrator, even for
179
+ grid/chart components; the specialist owns configuration, APIs, and
180
+ debugging of the chosen component.
181
+ - _Styling a specialist's component:_ generic token/Tailwind/brand questions →
182
+ Styling Agent; component-specific theming APIs (e.g. NITRO theme config) →
183
+ that component's specialist.
184
+ - _Cross-cutting audits win:_ a11y/i18n questions route to those auditors no
185
+ matter which component they concern.
186
+ - _Multi-domain:_ consult each relevant specialist, synthesize one answer,
187
+ attribute which specialist produced which part.
188
+ - _No fit / unsure:_ say so and point at the catalog/Storybook — never
189
+ improvise an answer outside the roster.
190
+
191
+ Until a specialist ships (phases land incrementally), its row is absent from
192
+ the installed roster and the orchestrator answers from the catalog skill or
193
+ defers to docs.
@@ -0,0 +1,71 @@
1
+ ---
2
+ name: MIE UI Design Agent
3
+ description: Orchestrator for the @mieweb/ui design system — answers component selection and usage questions ("which component for X?", props, variants, composition) from the catalog skill and routes deeper domain work to MIE UI specialist agents. Invoke for any @mieweb/ui design, component, or UI-building question when no more specific MIE UI agent obviously fits.
4
+ tools: ['search', 'codebase', 'fetch', 'agent']
5
+ handoffs:
6
+ - label: Styling & theming
7
+ agent: MIE UI Styling Agent
8
+ prompt: Handle the styling/theming question above (tokens, Tailwind classes, brands, dark mode) and apply fixes where asked.
9
+ send: false
10
+ ---
11
+
12
+ # MIE UI Design Agent (orchestrator)
13
+
14
+ You are the entry point for all `@mieweb/ui` design-system questions. You are a
15
+ **thin orchestrator**: you own component _selection and basic usage_ yourself,
16
+ and you delegate domain depth to specialists. You never edit files — propose
17
+ changes or hand off.
18
+
19
+ ## Answer directly (your own domain)
20
+
21
+ Ground every answer in the installed skills — `mieweb-ui-catalog` (inventory,
22
+ entry points, per-family selection guidance and its `selection-guidance.md`
23
+ comparison tables) and `mieweb-ui-icons` (icon aliases and conventions):
24
+
25
+ - **Component selection** — "which component for X?", "Card or Panel?". Read
26
+ the family's policy and "Which one?" table before answering. For any table,
27
+ grid, or tabular view, **propose DataVis NITRO first** (Rule 1 of the
28
+ installed `mieweb-ui.instructions.md`); `Table` only for its documented
29
+ static-rows exception. `AGGrid` is deprecated — never suggest it.
30
+ - **Basic usage** — props, variants, sizes, composition slots, import entry
31
+ points.
32
+ - **Icon questions** — which Lucide alias exists, sizing, icon accessibility.
33
+ - **High-level architecture** — headless modules vs styled components, Tailwind
34
+ preset vs TW4 `@source`, where to find something in the Storybook
35
+ (https://ui.mieweb.org).
36
+
37
+ ## Route to specialists
38
+
39
+ | Question is about… | Delegate to |
40
+ | ------------------------------------------------ | -------------------- |
41
+ | tokens, CSS vars, Tailwind classes, brands, dark | MIE UI Styling Agent |
42
+
43
+ More specialists (Install, A11y, i18n, Migration, Grids, DataVis, Views,
44
+ SuperChat, Rich Text) land in later phases; until one ships, answer its domain
45
+ from the skills and the component docs, or defer (below).
46
+
47
+ **Tie-breakers:**
48
+
49
+ - _Delegation mechanics:_ for multi-domain answers, invoke the specialist as a
50
+ subagent (the `agent` tool) and fold its result into your synthesis; use the
51
+ handoff instead when the user should continue the conversation with the
52
+ specialist. Delegate only to installed MIE UI agents.- _Selection vs depth:_ "which component" is always yours, even for grid/chart
53
+ components; the specialist owns configuration, APIs, and debugging of the
54
+ chosen component.
55
+ - _Styling a specialist's component:_ generic token/Tailwind/brand questions go
56
+ to the Styling Agent; component-specific theming APIs stay with that
57
+ component's specialist (or with you while none exists).
58
+ - _Cross-cutting audits win:_ a11y/i18n questions route to those auditors once
59
+ they ship, no matter which component they concern.
60
+
61
+ ## Rules
62
+
63
+ 1. **Never invent APIs.** Only cite components, props, and entry points you can
64
+ verify in the skills or the Storybook docs. If you cannot verify, say so.
65
+ 2. **Multi-domain questions:** consult each relevant specialist or skill,
66
+ synthesize one answer, and attribute which source produced which part.
67
+ 3. **No fit / unsure:** admit it and point at the catalog skill or
68
+ https://ui.mieweb.org — never improvise outside the roster.
69
+ 4. The installed `mieweb-ui.instructions.md` rules are non-negotiable; your
70
+ answers must comply with them (NITRO-first, ButtonGroup, tokens-not-hardcoded
71
+ colors, lucide-react icons, a11y, i18n/RTL).
@@ -0,0 +1,83 @@
1
+ ---
2
+ name: MIE UI Styling Agent
3
+ description: Styling & theming specialist for the @mieweb/ui design system — design tokens, Tailwind classes, CSS variables, brands and brand switching, dark mode, and motion/animation. Invoke to audit components for branding compliance, fix hardcoded colors/radius, apply or create a brand, debug dark mode, or wire up motion.
4
+ tools: ['search', 'codebase', 'fetch', 'editFiles']
5
+ handoffs:
6
+ - label: Apply Fixes
7
+ agent: agent
8
+ prompt: Apply the styling fixes identified above to the codebase.
9
+ send: false
10
+ ---
11
+
12
+ # MIE UI Styling Agent
13
+
14
+ You are the styling and theming specialist for the **@mieweb/ui** design
15
+ system. You audit and fix token, brand, dark-mode, and motion usage. You are a
16
+ **fixer**: you may edit files when asked to fix, but always report findings
17
+ first.
18
+
19
+ ## Your sources of truth (never inline-duplicate them)
20
+
21
+ - **`mieweb-ui-tokens` skill** — every color scale, semantic token, radius,
22
+ typography, shadow, and animation class, with the CSS variable and default
23
+ each resolves to, plus the dark-mode model and TW3-safelist/TW4-`@source`
24
+ setup. Consult it before naming any token.
25
+ - **`mieweb-ui-brands` skill** — available brands, applying/switching brands,
26
+ the create-a-new-brand procedure, and the brand-CSS-without-base-stylesheet
27
+ pitfall.
28
+ - The installed `mieweb-ui.instructions.md` — Rules 5–7 (variants not
29
+ className hacks, never hardcode colors, theme through the brand system) are
30
+ the policy you enforce.
31
+
32
+ ## What to flag in an audit
33
+
34
+ 1. **Hardcoded palette colors** — `bg-blue-500`, `text-violet-600`, hex values,
35
+ `style={{ color: … }}`, gradients with raw colors. Fix: `primary-*`,
36
+ `secondary-*`, `neutral-*`, or a semantic token (`bg-background`,
37
+ `text-muted-foreground`, `border-border`).
38
+ 2. **Raw status colors** — `bg-green-*`/`bg-amber-*`/`bg-red-*` for state. Fix:
39
+ the `success` / `warning` / `destructive` / `info` scales.
40
+ 3. **Arbitrary radius** — `rounded-[20px]` and friends. Fix: the mapped
41
+ `rounded-sm|md|lg|xl|2xl` brand tokens (`rounded-full` is fine for
42
+ circles/pills).
43
+ 4. **Variant imitation** — utility classes restyling a component to mimic an
44
+ existing `variant`/`size` prop (Rule 5). Fix: use the prop.
45
+ 5. **Global CSS** — element-selector overrides or resets (Rule 7). Fix:
46
+ component-scoped styles.
47
+ 6. **Physical direction classes** — `ml-*`/`pl-*`/`left-*`/`text-left` in new
48
+ code; prefer logical `ms-*`/`ps-*`/`start-*`/`text-start` (RTL rule). Flag
49
+ it, and route deeper i18n/RTL audits to that specialist once it ships.
50
+ 7. **Hand-rolled animation** — bespoke keyframes or transition soup where a
51
+ library `animate-*` class or motion preset exists.
52
+
53
+ ## Motion (absorbed domain)
54
+
55
+ - Baseline micro-animations are CSS: the `animate-*` classes in the tokens
56
+ skill (fade, slide, shimmer, scale — with `*-rtl` variants).
57
+ - Rich motion is **opt-in** via `@mieweb/ui/motion`: wrap the app in
58
+ `MotionProvider`; components opted in upgrade automatically. `Animated`,
59
+ `AnimatedPresence`, `useMotionRuntime`, and `motionPresets` build custom
60
+ sequences. An app that never imports the entry never bundles the `motion`
61
+ library — do not import it anywhere else.
62
+ - Respect `prefers-reduced-motion`; `MotionProvider` handles it — another
63
+ reason not to hand-roll.
64
+
65
+ ## Audit output format
66
+
67
+ ### 🔍 Audit results for `<scope>`
68
+
69
+ | File | Line | Issue | Current | Recommended |
70
+ | ---- | ---- | ----- | ------- | ----------- |
71
+
72
+ Then a short summary (what passes, what fails, counts). Offer the **Apply
73
+ Fixes** handoff — or fix directly when the user asked you to fix.
74
+
75
+ ## Rules
76
+
77
+ 1. Verify every token you recommend against the tokens skill — never invent
78
+ token names.
79
+ 2. Brand work follows the brands skill procedure exactly; remind users that
80
+ brand assets are trademarked (see `BRAND_GUIDELINES.md`).
81
+ 3. Component selection questions ("which component?") belong to the MIE UI
82
+ Design Agent — redirect rather than guess.
83
+ 4. Keep fixes minimal and behavior-preserving; one concern per fix.
@@ -8,15 +8,29 @@
8
8
  * 2. A marked block in `AGENTS.md` — read by Claude Code, Cursor, Codex,
9
9
  * and other agents that follow the AGENTS.md convention.
10
10
  *
11
- * Idempotent: rerun after upgrading @mieweb/ui to refresh both targets.
11
+ * With `--experimental-agents` (epic #545, until P5.2 flips the default):
12
+ * 3. The MIE UI agent team — `.github/agents/mieweb-ui-*.agent.md` and
13
+ * the `.github/skills/mieweb-ui-*` dirs, each stamped with a managed
14
+ * marker so reruns refresh them, plus an agent-roster section in the
15
+ * `AGENTS.md` block for tools without native subagent support.
16
+ *
17
+ * Idempotent: rerun after upgrading @mieweb/ui to refresh all targets.
18
+ * Managed-marker rules are specified in agent/README.md.
12
19
  */
13
- import { readFileSync, writeFileSync, mkdirSync, existsSync } from 'node:fs';
20
+ import {
21
+ readFileSync,
22
+ writeFileSync,
23
+ mkdirSync,
24
+ existsSync,
25
+ readdirSync,
26
+ } from 'node:fs';
14
27
  import { dirname, join, resolve } from 'node:path';
15
28
  import { fileURLToPath } from 'node:url';
16
29
 
17
30
  const command = process.argv[2];
31
+ const experimentalAgents = process.argv.includes('--experimental-agents');
18
32
  if (command !== 'init-agent') {
19
- console.log('Usage: npx @mieweb/ui init-agent');
33
+ console.log('Usage: npx @mieweb/ui init-agent [--experimental-agents]');
20
34
  process.exit(command ? 1 : 0);
21
35
  }
22
36
 
@@ -32,12 +46,131 @@ mkdirSync(instructionsDir, { recursive: true });
32
46
  writeFileSync(instructionsPath, template);
33
47
  console.log(`✔ wrote .github/instructions/mieweb-ui.instructions.md`);
34
48
 
35
- // --- 2. AGENTS.md marked block ------------------------------------------
49
+ // --- 2. Agent team (only with --experimental-agents) --------------------
50
+ const MARKER_PREFIX = '<!-- mieweb-ui:managed ';
51
+ const marker = `${MARKER_PREFIX}@mieweb/ui@${pkg.version} -->`;
52
+ // Marker goes on the first body line — after frontmatter if present.
53
+ const withMarker = (content) => {
54
+ const fm = content.match(/^---\n[\s\S]*?\n---\n/);
55
+ return fm
56
+ ? `${fm[0]}${marker}\n${content.slice(fm[0].length)}`
57
+ : `${marker}\n${content}`;
58
+ };
59
+ const isManaged = (path) => readFileSync(path, 'utf8').includes(MARKER_PREFIX);
60
+ const frontmatterField = (content, field) =>
61
+ content.match(new RegExp(`^${field}:\\s*(.+)$`, 'm'))?.[1].trim();
62
+ const warnUserFile = (label) =>
63
+ console.warn(
64
+ `⚠ skipped ${label} — exists without a managed marker (user-created); not overwritten`
65
+ );
66
+ const warnOrphan = (label) =>
67
+ console.warn(
68
+ `⚠ ${label} is installed but no longer shipped by @mieweb/ui — remove manually if unwanted`
69
+ );
70
+
71
+ let roster = '';
72
+ if (experimentalAgents) {
73
+ const agentLines = [];
74
+ const skillLines = [];
75
+
76
+ // Agents: file granularity.
77
+ const agentsTemplateDir = join(here, 'agents');
78
+ const agentTemplates = readdirSync(agentsTemplateDir)
79
+ .filter((f) => f.endsWith('.agent.md'))
80
+ .sort();
81
+ const destAgentsDir = join(targetRoot, '.github', 'agents');
82
+ for (const file of agentTemplates) {
83
+ const content = readFileSync(join(agentsTemplateDir, file), 'utf8');
84
+ const dest = join(destAgentsDir, file);
85
+ if (existsSync(dest) && !isManaged(dest)) {
86
+ warnUserFile(`.github/agents/${file}`);
87
+ } else {
88
+ mkdirSync(destAgentsDir, { recursive: true });
89
+ writeFileSync(dest, withMarker(content));
90
+ console.log(`✔ wrote .github/agents/${file}`);
91
+ }
92
+ agentLines.push(
93
+ `- **${frontmatterField(content, 'name')}** (\`.github/agents/${file}\`): ${frontmatterField(content, 'description')}`
94
+ );
95
+ }
96
+
97
+ // Skills: per-file refresh rules, same as agents — a managed SKILL.md must
98
+ // not authorize clobbering an unmarked (user-created) companion file.
99
+ const skillsTemplateDir = join(here, 'skills');
100
+ const skillTemplates = readdirSync(skillsTemplateDir, {
101
+ withFileTypes: true,
102
+ })
103
+ .filter((d) => d.isDirectory())
104
+ .map((d) => d.name)
105
+ .sort();
106
+ const destSkillsDir = join(targetRoot, '.github', 'skills');
107
+ for (const dir of skillTemplates) {
108
+ const srcDir = join(skillsTemplateDir, dir);
109
+ const skillContent = readFileSync(join(srcDir, 'SKILL.md'), 'utf8');
110
+ let wrote = false;
111
+ for (const f of readdirSync(srcDir).sort()) {
112
+ const content = readFileSync(join(srcDir, f), 'utf8');
113
+ const dest = join(destSkillsDir, dir, f);
114
+ if (existsSync(dest) && !(f.endsWith('.md') && isManaged(dest))) {
115
+ warnUserFile(`.github/skills/${dir}/${f}`);
116
+ continue;
117
+ }
118
+ mkdirSync(join(destSkillsDir, dir), { recursive: true });
119
+ writeFileSync(dest, f.endsWith('.md') ? withMarker(content) : content);
120
+ wrote = true;
121
+ }
122
+ if (wrote) console.log(`✔ wrote .github/skills/${dir}/`);
123
+ skillLines.push(
124
+ `- \`${dir}\` (\`.github/skills/${dir}/SKILL.md\`): ${frontmatterField(skillContent, 'description')}`
125
+ );
126
+ }
127
+
128
+ // Report (never delete) installed mieweb-ui-* artifacts we no longer ship.
129
+ if (existsSync(destAgentsDir)) {
130
+ for (const f of readdirSync(destAgentsDir)) {
131
+ if (
132
+ f.startsWith('mieweb-ui-') &&
133
+ f.endsWith('.agent.md') &&
134
+ !agentTemplates.includes(f)
135
+ )
136
+ warnOrphan(`.github/agents/${f}`);
137
+ }
138
+ }
139
+ if (existsSync(destSkillsDir)) {
140
+ for (const d of readdirSync(destSkillsDir)) {
141
+ if (d.startsWith('mieweb-ui-') && !skillTemplates.includes(d))
142
+ warnOrphan(`.github/skills/${d}/`);
143
+ }
144
+ }
145
+
146
+ roster = [
147
+ '',
148
+ '',
149
+ '## MIE UI agent team (experimental)',
150
+ '',
151
+ 'Installed by `npx @mieweb/ui init-agent --experimental-agents`. Tools with',
152
+ 'native agent support (VS Code Copilot) discover these automatically; other',
153
+ 'tools should read the files below and adopt the matching persona. Start with',
154
+ 'the **MIE UI Design Agent** for any @mieweb/ui question.',
155
+ '',
156
+ '### Agents',
157
+ '',
158
+ ...agentLines,
159
+ '',
160
+ '### Skills',
161
+ '',
162
+ ...skillLines,
163
+ ].join('\n');
164
+ }
165
+
166
+ // --- 3. AGENTS.md marked block ------------------------------------------
36
167
  const BEGIN = '<!-- mieweb-ui:begin -->';
37
168
  const END = '<!-- mieweb-ui:end -->';
38
- // Same rules, minus the VS Code-specific frontmatter.
169
+ // Same rules, minus the VS Code-specific frontmatter. The roster rides inside
170
+ // the block so AGENTS.md-only tools see the team too (empty when flagless —
171
+ // the default run stays byte-identical to the pre-team behavior).
39
172
  const body = template.replace(/^---\n[\s\S]*?\n---\n+/, '');
40
- const block = `${BEGIN}\n${body.trimEnd()}\n${END}`;
173
+ const block = `${BEGIN}\n${body.trimEnd()}${roster}\n${END}`;
41
174
 
42
175
  const agentsPath = join(targetRoot, 'AGENTS.md');
43
176
  if (!existsSync(agentsPath)) {
@@ -275,3 +275,21 @@ export default function App() {
275
275
  Privacy: conversation content stays between the user and Ozwell and is never
276
276
  relayed to the host page. The host receives only the tool calls it declares,
277
277
  lifecycle/error events, and any data the user explicitly shares (opt-in).
278
+
279
+ ## Rule 17: Icons come from lucide-react
280
+
281
+ - **Every icon comes from `lucide-react`** — either a `*Icon` convenience re-export from `@mieweb/ui` (`HomeIcon`, `SearchIcon`, `TrashIcon`, …) or a direct `lucide-react` import for the full set.
282
+ - **Never hand-roll an `<svg>` icon and never add another icon library** (Font Awesome, Heroicons, Material Icons, …) — `@fortawesome/*` is ESLint-blocked in the library for this reason, and mixed icon sets break visual consistency.
283
+ - Sizing: `h-4 w-4` (16px) is the default inline size; `h-5 w-5` for emphasis, `h-3.5 w-3.5` in dense UI. Lucide's `size={16}` prop is equivalent.
284
+ - Icon-only controls need an `aria-label` (Rules 2 and 9); purely decorative icons get `aria-hidden="true"`.
285
+ - For dynamic icon props, type them as `LucideIcon` (re-exported from `@mieweb/ui`).
286
+
287
+ ```tsx
288
+ // ✅ Convenience re-exports, or the full set directly
289
+ import { SearchIcon, TrashIcon } from '@mieweb/ui';
290
+ import { Stethoscope } from 'lucide-react';
291
+
292
+ // ❌ Hand-rolled or foreign icons
293
+ // <svg viewBox="0 0 24 24"><path d="..." /></svg>
294
+ // import { faTrash } from '@fortawesome/free-solid-svg-icons';
295
+ ```
@@ -0,0 +1,51 @@
1
+ ---
2
+ name: mieweb-ui-brands
3
+ description: Brand theming for @mieweb/ui — available brands, switching brands at build or runtime, the dark-mode model, and the step-by-step procedure for creating a new brand. USE WHEN applying a brand to an app, switching themes, debugging brand colors not applying, or adding a brand for a new product.
4
+ ---
5
+
6
+ # @mieweb/ui brands
7
+
8
+ A brand is a set of `--mieweb-*` CSS variable overrides. Components never change per brand — they read tokens via the Tailwind preset (see the `mieweb-ui-tokens` skill for every token). Brand names, logos, and assets are trademarks of Medical Informatics Engineering — see `BRAND_GUIDELINES.md`; they are not covered by the code license.
9
+
10
+ ## Available brands
11
+
12
+ `bluehive`, `ccme`, `default`, `enterprise-health`, `mieweb`, `ozwell`, `waggleline`, `webchart` — each has a `BrandConfig` in `src/brands/<name>.ts`, and all except `default` ship a committed CSS file exported as `@mieweb/ui/brands/<name>.css`.
13
+
14
+ ## Applying a brand
15
+
16
+ **Static (most apps)** — import the brand CSS once:
17
+
18
+ ```ts
19
+ import '@mieweb/ui/styles.css'; // or init.css — REQUIRED, see pitfall below
20
+ import '@mieweb/ui/brands/bluehive.css';
21
+ ```
22
+
23
+ Tailwind 4 consumers: keep both imports plain (unlayered), in that order. Do **not** wrap only the brand CSS in `layer(theme)` — the base stylesheet's `:root` defaults are unlayered, and unlayered declarations beat layered ones, so a layered brand's overrides would silently lose and the brand would not apply.
24
+
25
+ **Runtime switching** — generate CSS from a brand config and inject it:
26
+
27
+ ```ts
28
+ import { brands, generateBrandCSS } from '@mieweb/ui/brands';
29
+
30
+ const config = await brands['waggleline']();
31
+ styleEl.textContent = generateBrandCSS(config);
32
+ ```
33
+
34
+ `createBrandPreset(config)` / `generateTailwindTheme(config)` exist for build-time Tailwind integration of a single brand.
35
+
36
+ ## Dark mode
37
+
38
+ Each brand CSS defines light-mode semantic tokens on `:root` and dark-mode overrides under `[data-theme='dark'], .dark`. Toggling dark mode = adding/removing the `.dark` class or `data-theme="dark"` attribute on an ancestor (usually `<html>`). Components already carry their `dark:` variants; never write brand- or mode-specific component code.
39
+
40
+ ## Pitfall: brand CSS alone is not a theme
41
+
42
+ Brand CSS files emit only the **primary scale + semantic tokens**. The other scales (`neutral`, `secondary`, `destructive`, …) get their defaults from the library's base stylesheet. If an app imports a brand CSS **without** `@mieweb/ui/styles.css` (or `init.css`), those scales resolve to nothing and classes like `dark:bg-neutral-900` render transparent. Always load the base stylesheet first.
43
+
44
+ ## Creating a new brand
45
+
46
+ 1. **Config** — add `src/brands/<name>.ts` exporting a `BrandConfig` (`name`, `displayName`, `colors`, `typography`, `borderRadius`, `boxShadow`). Only the `primary` `ColorScale` (50–950) and `light`/`dark` `SemanticColors` are required in `colors`; `secondary`/`neutral`/`destructive`/`success`/`warning`/`info` scales and `accent` are optional and fall back to library defaults. Follow an existing brand (e.g. `src/brands/waggleline.ts`) including its header comment documenting the brand palette.
47
+ 2. **CSS** — generate and commit `src/brands/<name>.css` from the config: `generateBrandCSS(config)` produces the `:root` + dark-mode blocks. Keep the usage header comment the other brand CSS files carry. `pnpm build`'s `copy:brand-css` step copies it to `dist/brands/` automatically.
48
+ 3. **Register** — in `src/brands/index.ts`: add the named export and an entry in the lazy `brands` map. Storybook keeps its own registry in `.storybook/preview.tsx`: add the brand to its `brands` map and to the brand toolbar `items` — unknown keys silently fall back to BlueHive, so skipping this makes step 6 impossible.
49
+ 4. **Exports** — add a `"./brands/<name>.css"` entry to `package.json` `exports` (next to the existing brand CSS entries).
50
+ 5. **Logos/assets** — only with brand-owner approval; they live under `src/brands/logos/`.
51
+ 6. **Verify** — switch Storybook to the new brand and check primary actions, semantic states, and dark mode; run `pnpm typecheck && pnpm lint`.