{"owner":"home-assistant","repo":"frontend","hasSkills":true,"totalSkillsCount":12,"totalTokensCount":12650,"categories":["anthropic-skill","root-instruction"],"hasMcp":false,"mcpConfig":null,"found":[".agents/skills/ha-frontend-types/SKILL.md",".agents/skills/ha-frontend-lit/SKILL.md",".agents/skills/ha-frontend-styling/SKILL.md",".agents/skills/ha-frontend-user-facing-text/SKILL.md",".agents/skills/ha-frontend-testing/SKILL.md",".agents/skills/ha-frontend-events/SKILL.md",".agents/skills/ha-frontend-demo/SKILL.md",".agents/skills/ha-frontend-contexts/SKILL.md",".agents/skills/ha-frontend-components/SKILL.md",".agents/skills/ha-frontend-gallery/SKILL.md",".agents/skills/ha-frontend-review/SKILL.md","AGENTS.md"],"skills":{".agents/skills/ha-frontend-types/SKILL.md":"---\nname: ha-frontend-types\ndescription: Home Assistant frontend TypeScript conventions. Use when defining or reviewing backend data contracts, optional schemas, shared types, assertions, or Lit lifecycle types.\n---\n\n# HA Frontend Types\n\nUse this skill for Home Assistant-specific TypeScript contracts and type choices.\n\n## Home Assistant Data Contracts\n\nVerify data contracts against the source that owns them, such as Home Assistant Core, Supervisor, a WebSocket handler, or an exported component type. Do not shape a type around assumptions made by its current frontend consumers.\n\n- Match required, optional, nullable, and defaulted fields to the producer and runtime contract. Preserve optional configuration fields when omission is supported and has defined behavior.\n- Use distinct request and response types when their wire shapes differ.\n- When changing a shared contract, check affected consumers, tests, fixtures, and mocks.\n\n## Reuse Home Assistant Contracts\n\n- Reuse the canonical Home Assistant type when one exists. Define shared contract types in the data or API module that owns them.\n- Reuse types exported by components and helpers rather than reconstructing their payloads. For event types, follow `ha-frontend-events`.\n- When one domain contract is used across modules, define and export it from the module that owns that contract.\n\nPrefer an existing owning contract. Introduce a frontend-specific type when the frontend shape or boundary genuinely differs.\n\n## Assertions\n\nPrefer accurate types and runtime narrowing. Use assertions or TypeScript suppressions at boundaries where the runtime invariant is understood but cannot be expressed cleanly; keep them narrow and explain non-obvious invariants.\n\n## Lit Lifecycle Types\n\nFor Lit lifecycle methods that receive changed properties, use `PropertyValues<this>` when the method only needs public reactive properties:\n\n```ts\nprotected willUpdate(changedProperties: PropertyValues<this>) {\n  // ...\n}\n```\n\nUse unparameterized `PropertyValues` when the method inspects private or protected reactive properties, which are not keys of `this`. Do not add assertions solely to retain `PropertyValues<this>`.\n\n## Enforced Baseline\n\nUse `import type` for type-only imports. This is enforced by the repository ESLint configuration.\n",".agents/skills/ha-frontend-lit/SKILL.md":"---\nname: ha-frontend-lit\ndescription: Home Assistant frontend Lit conventions. Use when working with reactive properties, internal state, DOM queries, lifecycle methods, or render-derived state.\n---\n\n# HA Frontend Lit\n\nUse this skill when implementing or reviewing Lit component state, DOM access, lifecycle methods, or rendering behavior. Cross-load `ha-frontend-types` for Home Assistant data contracts, assertions, and lifecycle parameter types.\n\n## Reactive Fields\n\nThis project currently uses Lit's TypeScript experimental decorators with `useDefineForClassFields: false`. Match existing declarations and do not introduce standard-decorator `accessor` syntax unless the project changes decorator mode.\n\n- Use `@property()` for public reactive API and `@state()` for private reactive state.\n- Prefer inferred types for initialized reactive fields when inference preserves the intended type; annotate when widening or an external contract requires it.\n\n## DOM Queries\n\nPrefer Lit's `@query()` or `@queryAll()` decorators for fixed selectors in the component's render root.\n\n- Type the decorated field with the narrowest useful DOM or component interface.\n- Keep the field optional when it may be absent at the point of access, including conditional rendering or pre-render lifecycle access.\n- Use a definite assignment assertion only when every call site runs after the node is guaranteed to exist.\n- The optional second argument to `@query()`, as in `@query(\"#target\", true)`, caches the first query result. Use it only when later renders cannot replace the queried node.\n- Use a direct query when the selector is dynamic or the target is outside the component's render root. Before querying a child, consider whether the required value belongs in parent state or data flow.\n\n## Render-Derived State\n\n- Prefer render-local values for inexpensive structures used only by that render.\n- Assign a render-local value once when repeated evaluation is non-trivial or a local name improves clarity.\n- Keep purely presentational derivations in `render()`. Use stored state or `willUpdate()` when the value must participate in lifecycle work, reflection, CSS, or non-render consumers.\n- Use `memoizeOne` for pure, argument-derived transforms when stable input identity avoids meaningful repeated work. Keep inputs explicit and limited, and do not add caching without a credible benefit over computing the value directly.\n\n## References\n\n- [Reactive properties](https://lit.dev/docs/components/properties/)\n- [Decorators](https://lit.dev/docs/components/decorators/)\n- [Shadow DOM queries](https://lit.dev/docs/components/shadow-dom/#query)\n- [Reactive update cycle](https://lit.dev/docs/components/lifecycle/)\n",".agents/skills/ha-frontend-styling/SKILL.md":"---\nname: ha-frontend-styling\ndescription: Home Assistant frontend styling, theming, spacing, responsive layout, RTL, and View Transitions guidance. Use when editing CSS, layout, motion, or visual component structure.\n---\n\n# HA Frontend Styling\n\nUse this skill when editing CSS, layout, visual hierarchy, theme integration, responsive behavior, RTL support, or view transitions.\n\n## Theme And Layout Basics\n\n- Use Home Assistant CSS custom properties instead of hardcoded colors.\n- Use `--ha-space-*` spacing tokens instead of hardcoded spacing where possible.\n- Keep components mobile-first and enhance for desktop.\n- Keep layouts RTL-safe. Prefer logical properties when they fit.\n- Prefer `ha-*` components and current Web Awesome wrappers.\n- Avoid adding new legacy Material Web Components (`mwc-*`).\n- Scope styles to the component. Do not rely on global styles for component internals.\n\nSpacing tokens are defined in `src/resources/theme/core.globals.ts`. The scale runs from `--ha-space-1` at 4px through `--ha-space-20` at 80px in 4px increments. Common values are `--ha-space-2` at 8px, `--ha-space-4` at 16px, and `--ha-space-8` at 32px.\n\n```ts\nstatic get styles() {\n  return css`\n    :host {\n      padding: var(--ha-space-4);\n      color: var(--primary-text-color);\n      background-color: var(--card-background-color);\n    }\n\n    .content {\n      gap: var(--ha-space-2);\n    }\n\n    @media (max-width: 600px) {\n      :host {\n        padding: var(--ha-space-2);\n      }\n    }\n  `;\n}\n```\n\n## Interaction States\n\n- Make touch targets large enough for mobile.\n- Provide clear hover, active, focus, disabled, loading, error, and unavailable states.\n- Preserve keyboard navigation and visible focus indicators.\n- Maintain WCAG AA contrast for text and essential UI affordances.\n\n## View Transitions\n\nUse the View Transitions API only for meaningful continuity between DOM states.\n\nCore resources:\n\n- Utility wrapper: `src/common/util/view-transition.ts`, `withViewTransition()`.\n- Launch-screen fade example: `src/util/launch-screen.ts`.\n- Animation keyframes: `src/resources/theme/animations.globals.ts`.\n- Animation duration tokens: `src/resources/theme/core.globals.ts`.\n\nImplementation rules:\n\n- Use `withViewTransition()` for fallback behavior.\n- Keep transitions simple. Subtle fades and crossfades usually work best.\n- Use `--ha-animation-duration-fast`, `--ha-animation-duration-normal`, or `--ha-animation-duration-slow` for timing.\n- Ensure each `view-transition-name` is unique at any given time.\n- Remember only one view transition can run at a time.\n- View transitions operate at document level and do not work inside Shadow DOM style isolation. For web components, set `view-transition-name` on `:host` or use document-level transitions.\n- The root gets `view-transition-name: root` by default. Target `::view-transition-group(root)` to customize the default page transition.\n\n## Review Checklist\n\n- Styling uses theme variables and spacing tokens where practical.\n- Layout is mobile-first and RTL-safe.\n- Component styles are scoped.\n- Interactive states are clear and accessible.\n- Motion is subtle, tokenized, and respects reduced-motion behavior through existing globals.\n",".agents/skills/ha-frontend-user-facing-text/SKILL.md":"---\nname: ha-frontend-user-facing-text\ndescription: Home Assistant frontend copy, localization, terminology, and user-facing text guidance. Use when adding or reviewing labels, buttons, dialogs, errors, translations, or UI strings.\n---\n\n# HA Frontend User-Facing Text\n\nUse this skill for all user-facing text, translations, labels, buttons, dialog copy, errors, helper text, and review comments about wording.\n\n## Localization\n\n- All user-facing text must be translatable.\n- Add translation keys to `src/translations/en.json` when introducing new strings.\n- Use the localization system instead of inline user-visible strings.\n- Prefer complete localized strings with placeholders over concatenating translated fragments.\n- Give translators enough context through key naming and placeholders.\n\n```ts\nthis._localize(\"ui.panel.config.updates.updates_refreshed\", {\n  count: 5,\n});\n```\n\n## Voice And Style\n\n- Use American English.\n- Use a friendly, informational tone.\n- Address users directly with \"you\" and \"your\" when appropriate.\n- Be inclusive, objective, and non-discriminatory.\n- Be concise and clear.\n- Use active voice.\n- Avoid jargon where a familiar home automation term works.\n- Always write \"Home Assistant\" in full. Do not use \"HA\" or \"HASS\" in user-facing copy.\n- Spell out terms when possible.\n- Use sentence case for titles, headings, buttons, labels, and UI elements.\n- Use the Oxford comma in lists.\n- Prefer \"like\" over \"e.g.\" and \"for example\" over \"i.e.\".\n- Avoid all caps for emphasis. Use wording, bold, or italics instead.\n- Write for both technical and non-technical users.\n\nSentence case examples:\n\n- Use: \"Create new automation\"\n- Avoid: \"Create New Automation\"\n- Use: \"Device settings\"\n- Avoid: \"Device Settings\"\n\n## Terminology\n\nUse \"integration\" instead of \"component\" for user-facing product language unless referring to a frontend component in developer context.\n\nTechnical product terms are lowercase in prose: automation, entity, device, service.\n\n## Delete, Remove, Create, Add\n\nUse \"Remove\" for actions that can be restored or reapplied:\n\n- Removing a user's permission.\n- Removing a user from a group.\n- Removing links between items.\n- Removing a widget from a dashboard.\n- Removing an item from a cart.\n\nUse \"Delete\" for permanent, non-recoverable actions:\n\n- Deleting a field.\n- Deleting a value in a field.\n- Deleting a task.\n- Deleting a group.\n- Deleting a permission.\n- Deleting a calendar event.\n\nUse \"Add\" for already-existing items:\n\n- Adding a permission to a user.\n- Adding a user to a group.\n- Adding links between items.\n- Adding a widget to a dashboard.\n- Adding an item to a cart.\n\nUse \"Create\" for something made from scratch:\n\n- Creating a new field.\n- Creating a new task.\n- Creating a new group.\n- Creating a new permission.\n- Creating a new calendar event.\n\nCreate pairs with Delete. Add pairs with Remove.\n\n## Review Checklist\n\n- Text is localized.\n- Copy uses sentence case.\n- \"Home Assistant\" is written in full.\n- Delete/Remove and Create/Add match recoverability and object lifecycle.\n- Placeholders are used instead of string concatenation.\n- The wording is concise and understandable without implementation knowledge.\n",".agents/skills/ha-frontend-testing/SKILL.md":"---\nname: ha-frontend-testing\ndescription: Home Assistant frontend testing and validation workflow. Use when adding or updating tests, running lint, TypeScript checks, Vitest, Playwright e2e suites, dev servers, or chart-data benchmarks.\n---\n\n# HA Frontend Testing\n\nUse this skill when choosing or running validation for frontend changes.\n\n## Test Helpers\n\n- Before adding or changing tests, inspect the relevant suite's existing helpers and fixtures. Reuse them instead of duplicating setup, test data, navigation, interactions, waits, or assertions.\n- When the same test flow appears more than once, move it into the closest suite-local helper with a focused interface.\n- Keep one-off test behaviour in the test unless a helper makes the intent materially clearer. Do not hide the behaviour under test behind broad, configurable abstractions.\n\n## Core Commands\n\n```bash\npnpm lint          # ESLint + Prettier + TypeScript + Lit\npnpm format        # Auto-fix ESLint + Prettier\npnpm lint:types    # TypeScript compiler, run without file arguments\npnpm test          # Vitest\npnpm build         # Full production build\npnpm dev           # App dev server\npnpm dev:serve     # Local serving dev server\n```\n\nNever run `tsc` or `pnpm lint:types` with file arguments. File arguments make `tsc` ignore `tsconfig.json` and can emit `.js` files into `src/`.\n\nFor focused type feedback on one file, use editor diagnostics instead of a file-scoped `tsc` command.\n\n## Production Builds\n\nProduction builds support foreground and managed background execution:\n\n```bash\npnpm build                         # Full foreground build\npnpm build --background            # Full managed background build\npnpm build --modern                # Modern frontend_latest bundle only\npnpm build --modern --background   # Modern managed background build\npnpm build --status\npnpm build --logs [--follow]\npnpm build --stop\n```\n\nUse `pnpm build --modern --background` for production bundle-size or browser performance comparisons that only need modern browser output. It runs the normal metadata and static preparation, minifies and compresses the modern `frontend_latest` bundle and shared static assets, and generates modern-only entry pages and service workers. It deliberately skips the legacy bundle and its service worker.\n\nDo not pass `--help`, `--background`, or `--modern` to `script/build_frontend`; that raw script does not parse arguments and always starts the full foreground build. Use `pnpm build` for managed builds. App builds and development servers keep exclusive ownership of `hass_frontend/` for their lifetime.\n\nManaged app, demo, gallery, and E2E app workflows share one lifetime lock, so only one build or development server can run at a time.\n\n## When To Add Tests\n\n- Write tests for code that computes something: data processing, utilities, config validation, and strategies.\n- Do not write rendering tests. This includes views, panels, and components whose text, styles, slots, or option defaults are checked, or that only put context and helper data into a template.\n- Do not try to cover every scenario, especially for behaviour that changes often.\n- If you are not sure a test is useful, describe it and what it would catch, and let the user decide.\n- Tests never talk to a real Home Assistant. Replace `callWS`, `callApi`, and the connection with fakes.\n\n## Dev Servers\n\n`pnpm dev` builds and watches the app, served by a running Home Assistant core configured through `development_repo`.\n\n`pnpm dev:serve` also serves locally and supports `-c` for the core URL and `-p` for the port. The default is 8124, or 8123 in a devcontainer.\n\nDev server commands support `--background`, `--status`, `--stop`, and `--logs [--follow]`. `pnpm dev`, `pnpm dev:serve`, `pnpm dev:demo`, and `pnpm dev:gallery` also support `--fetch-translations`; this runs translation fetching, including first-time GitHub device authentication, under the workflow lock before starting the watcher. It works in foreground and background modes. Prefer managed background mode while iterating so the watcher stays available across test runs without occupying the terminal. `pnpm dev` and `pnpm dev:serve` share one managed process slot because both write the app output.\n\n## Playwright E2E\n\nEach suite has its own dev server port. Playwright reuses an existing server locally when its configured URL responds; otherwise it performs a slow full build. When a development watcher is being reused, rspack recompiles on save and reruns should not need a restart.\n\nStart the relevant suite server, then run that suite:\n\n| Suite   | Background server                            | Test command            |\n| ------- | -------------------------------------------- | ----------------------- |\n| App     | `pnpm test:e2e:app:dev --background` on 8095 | `pnpm test:e2e:app`     |\n| Demo    | `pnpm dev:demo --background` on 8090         | `pnpm test:e2e:demo`    |\n| Gallery | `pnpm dev:gallery --background` on 8100      | `pnpm test:e2e:gallery` |\n\nThe custom development wrappers use `/__ha_dev_status` to identify and manage their own suites. Playwright server reuse checks the configured URL instead. Wrapper start and stop operations are idempotent for a matching suite and reject an unrelated process occupying the port.\n\nLocal runs against a watched development server do not always match CI's clean build artifacts, environment, sharding, or worker configuration. Use background servers for the fast iteration loop, but confirm the relevant CI jobs complete successfully before considering E2E changes verified.\n\nUse `-g \"<title>\" --project=chromium` to narrow a run. `pnpm test:e2e` runs suites sequentially when managed servers are unavailable to prevent cold builds racing over shared generated assets. Run suites directly; piping through output truncation hides progress and failures.\n\nThe app suite uses a stripped-down harness for e2e. Demo and gallery use their normal dev servers.\n\n## Benchmarks\n\nFor chart data transforms such as history, statistics, energy, and downsampling, read and follow the complete workflow in `test/benchmarks/README.md` before making benchmark or optimization changes.\n\nThat workflow owns the baseline, noise analysis, guardrails, acceptance thresholds, and reporting requirements. In particular, optimizations must keep output bit-identical; never update snapshots or modify fixtures to make an optimization pass.\n\n## Verification Selection\n\n- Documentation-only change: no code test required unless examples or commands changed.\n- Type-only or utility change: run focused Vitest if available, then `pnpm lint:types` if practical.\n- Lit component change: run relevant tests plus lint or typecheck depending on scope.\n- E2E-sensitive flow: start the relevant e2e dev server and run the narrow Playwright suite.\n- Broad refactor: run `pnpm lint` and relevant test suites when practical.\n",".agents/skills/ha-frontend-events/SKILL.md":"---\nname: ha-frontend-events\ndescription: Home Assistant frontend event patterns. Use when typing event handlers, using HASSDomEvent types, dispatching with fireEvent, or declaring HASSDomEvents and event maps.\n---\n\n# HA Frontend Events\n\nUse this skill when implementing or reviewing event listeners and custom event contracts. Cross-load `ha-frontend-components` when the work also involves dialogs, forms, alerts, shortcuts, tooltips, panels, Lovelace cards, or buttons.\n\n## Event Handling\n\nUse the event types from `src/common/dom/fire_event.ts` instead of plain `Event`, generic `CustomEvent`, or element casts when they express the handler contract:\n\n- Use `HASSDomCurrentTargetEvent<T>` to read the element on which the listener was registered through `ev.currentTarget`.\n- Use `HASSDomTargetEvent<T>` only to read the element that originated the event through `ev.target`.\n- Use `HASSDomEvent<T>` to read a custom event payload through `ev.detail`.\n- Use `ValueChangedEvent<T>` from `src/types.ts` for the standard `value-changed` event.\n- Prefer an event type exported by the component being listened to, such as `HaSelectSelectEvent<T, Clearable>` or `HaDropdownSelectEvent<TValue, TData>`, over reconstructing its detail type.\n- Use `ActionHandlerEvent` from `src/data/lovelace/action_handler.ts` for Lovelace tap, hold, and double-tap handlers.\n\nImport event and element types with `import type`:\n\n```ts\nimport type {\n  HASSDomCurrentTargetEvent,\n  HASSDomEvent,\n  HASSDomTargetEvent,\n} from \"../common/dom/fire_event\";\nimport type { HaCheckbox } from \"../components/ha-checkbox\";\nimport type { HaEntityPicker } from \"../components/entity/ha-entity-picker\";\nimport type { HaRadioGroup } from \"../components/radio/ha-radio-group\";\nimport type { ValueChangedEvent } from \"../types\";\n```\n\nType the handler so the selected property can be read directly. Do not cast `ev.currentTarget` or assign it to a single-use variable:\n\n```ts\nprivate _scopeChanged(ev: HASSDomCurrentTargetEvent<HaRadioGroup>): void {\n  this._scope = ev.currentTarget.value;\n}\n\nprivate _checkedChanged(ev: HASSDomTargetEvent<HaCheckbox>): void {\n  this._checked = ev.target.checked;\n}\n\nprivate _valueChanged(ev: ValueChangedEvent<string>): void {\n  this._value = ev.detail.value;\n}\n\nprivate _itemSelected(ev: HASSDomEvent<{ id: string }>): void {\n  this._selectedId = ev.detail.id;\n}\n```\n\nUse intersections when a handler needs more than one facet of an event. Keep the native event type when the handler reads native fields such as `key`, modifier keys, `dataTransfer`, or focus relationships:\n\n```ts\nprivate _entityChanged(\n  ev: ValueChangedEvent<string> & HASSDomCurrentTargetEvent<HaEntityPicker>\n): void {\n  ev.currentTarget.value = ev.detail.value;\n}\n\nprivate _keyDown(\n  ev: KeyboardEvent & HASSDomCurrentTargetEvent<HTMLInputElement>\n): void {\n  if (ev.key === \"Enter\") {\n    this._submit(ev.currentTarget.value);\n  }\n}\n```\n\nDispatch Home Assistant component events with `fireEvent()` instead of constructing `Event` or `CustomEvent` directly. Register the event name and detail type by augmenting `HASSDomEvents`; use `undefined` when an event has no detail. `fireEvent()` constrains event names and supplied detail, and events bubble and cross shadow boundaries by default:\n\n```ts\nfireEvent(this, \"item-selected\", { id: item.id });\nfireEvent(this, \"refresh-requested\");\n```\n\nWhen an event is already registered, derive handler and listener types from its registration rather than repeating the payload shape:\n\n```ts\nprivate _itemSelected(\n  ev: HASSDomEvent<HASSDomEvents[\"item-selected\"]>\n): void {\n  this._selectedId = ev.detail.id;\n}\n```\n\n`HASSDomEvents` types `fireEvent()` calls. Augment `HTMLElementEventMap` for typed listeners on HTML elements, or `GlobalEventHandlersEventMap` when the event is handled on global event targets.\n\nIn component files, prefer placing global event declarations after the class at the bottom of the file. Preserve the existing placement when editing established files; foundational type, helper, and mixin files commonly keep declarations near the top before their consumers.\n\n```ts\ndeclare global {\n  interface HASSDomEvents {\n    \"item-selected\": { id: string };\n    \"refresh-requested\": undefined;\n  }\n\n  interface HTMLElementEventMap {\n    \"item-selected\": HASSDomEvent<HASSDomEvents[\"item-selected\"]>;\n  }\n}\n```\n",".agents/skills/ha-frontend-demo/SKILL.md":"---\nname: ha-frontend-demo\ndescription: Home Assistant standalone demo structure, configurations, URL navigation, mocked APIs, shared demo stubs used by gallery, and verification. Use when changing files under demo/, changing gallery consumers of demo/src/stubs/, or running and validating the demo.\n---\n\n# HA Frontend Demo\n\nUse this skill for standalone demo work and changes to shared demo stubs consumed by the gallery. Follow the persistent repository guidance in `AGENTS.md` and load matching specialist skills alongside this skill, especially `ha-frontend-testing` when running or validating the demo.\n\n## Purpose\n\nThe demo is the full Home Assistant frontend running against a mocked backend and is published at <https://demo.home-assistant.io>. It needs no Home Assistant server, which makes it the easiest way to load the real UI in a browser, for example to take screenshots.\n\n## Running The Demo\n\nRun commands from the repository root:\n\n```bash\npnpm dev:demo               # Development server on http://localhost:8090\npnpm dev:demo --background  # Detached; also supports --status/--stop/--logs\n```\n\nUse the E2E workflows documented in `ha-frontend-testing` when validating the demo.\n\n## Opening A Specific Demo\n\nThe demo contains multiple demo configurations. Select one directly with the `demo` query parameter, for example `http://localhost:8090/?demo=<slug>`. The valid slugs are defined in `demoConfigs` in `demo/src/configs/demo-configs.ts`, so \"the second demo\" means the slug of the second entry in that list. An unknown slug falls back to the default, which is the first entry.\n\n## Opening A Specific Page\n\nThe demo build uses hash-based routing: the frontend path goes in the URL hash, and the `demo` query parameter goes before the `#`. The URL format is:\n\n```text\nhttp://localhost:8090/?demo=<slug>#/<path>\n```\n\nUseful paths:\n\n- `/lovelace/0`: The selected demo's dashboard, also the default when no hash is given.\n- `/energy`: The energy dashboard. Its tabs are views: `/energy/overview` (Summary), `/energy/electricity`, `/energy/gas`, `/energy/water`, and `/energy/now`.\n- `/map`, `/history`, `/todo`, `/config`: Other sidebar panels.\n\nFor example, the water tab of the energy dashboard of the second demo is:\n\n```text\nhttp://localhost:8090/?demo=<second slug>#/energy/water\n```\n\n## Structure\n\n- `demo/src/ha-demo.ts`: Root element that sets up all backend mocks.\n- `demo/src/configs/<slug>/`: One directory per demo configuration, containing entities, dashboard, and theme data.\n- `demo/src/configs/demo-configs.ts`: Registry of demo configurations and URL slug handling.\n- `demo/src/stubs/`: Mocked WebSocket and REST APIs.\n- `demo/script/develop_demo`, `demo/script/build_demo`: Development server and static build wrappers.\n\n## Core Data\n\nThe service descriptions in `src/fake_data/demo_services.ts` and the trigger and condition descriptions and icons in `demo/src/stubs/automation_platforms_*.ts` are generated from Home Assistant Core. Do not edit them by hand. Run `script/gen_demo_core_data` to regenerate them from the core `dev` branch, and add a domain to the lists in `build-scripts/gulp/gen-demo-core-data.js` when the demo needs its services, triggers, or conditions. Their translations come from the backend translations merged into the demo build.\n\n## Shared Gallery Stubs\n\n`demo/src/stubs/` is shared, not demo-private: gallery pages import from it directly. Before changing or removing anything a stub does, search for its callers across `demo/src/` and `gallery/src/`, then check the affected gallery pages as well as the demo.\n\nThe two consumers use a stub differently, so demo behavior does not predict gallery behavior. A gallery page calls stubs against the `hass` from `provideHass`, where `hass.config` is the shared `demoConfig` object. A stub that mutates `hass.config` in place is therefore visible to the page. `demo/src/ha-demo.ts` copies `components` into a new array before the stubs run, so the same mutation never reaches the demo. A change can look correct in the demo while quietly breaking a gallery page.\n\n## Verification\n\nLoad `ha-frontend-testing` and use its demo and gallery workflows. Validate both consumers when changing shared stubs. Documentation-only changes do not require code tests unless examples or commands change.\n",".agents/skills/ha-frontend-contexts/SKILL.md":"---\nname: ha-frontend-contexts\ndescription: Home Assistant frontend Lit context and hass migration guidance. Use when adding or changing component state access, replacing hass reads, consuming entity or registry contexts, or reviewing rerender behavior.\n---\n\n# HA Frontend Contexts\n\nUse this skill when a component reads Home Assistant state, registries, localization, services, config, UI data, connection state, or API helpers.\n\n## Goal\n\nMove leaf components away from the broad `hass: HomeAssistant` object. Broad `hass` access rerenders components for unrelated changes, hides the data a component depends on, and makes tests harder to mock.\n\nContainer components may keep `hass` when they own it and feed providers. Leaf components should consume the narrowest context that covers their reads.\n\n## Core Files\n\n- Context definitions: `src/data/context/index.ts`\n- `consume` decorator, `ContextSubscriptionController`, and `consumeContext` / `ContextController` for reactive controllers: `src/common/decorators/consume.ts` (use instead of `@lit/context`'s `consume` and `ContextConsumer`, enforced by ESLint)\n- Entity-scoped consume helpers: `src/common/decorators/consume-context-entry.ts`\n- Transform decorator: `src/common/decorators/transform.ts`\n- Canonical migration example: `src/panels/lovelace/cards/hui-button-card.ts`\n- Providers are wired by `contextMixin` on `HassBaseEl`; consumers do not wire providers manually.\n\n## Context Selection\n\n| Context                                                              | Replaces                                                                                                  |\n| -------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------- |\n| `statesContext`                                                      | `hass.states`                                                                                             |\n| `entitiesContext`, `devicesContext`, `areasContext`, `floorsContext` | `hass.entities`, `hass.devices`, `hass.areas`, `hass.floors`                                              |\n| `registriesContext`                                                  | all four registries together                                                                              |\n| `servicesContext`                                                    | `hass.services`                                                                                           |\n| `internationalizationContext`                                        | `hass.localize`, `hass.locale`, `hass.language`                                                           |\n| `formattersContext`                                                  | entity and attribute formatters                                                                           |\n| `configContext`                                                      | `hass.config`, `hass.user`, `hass.auth`, `hass.userData`                                                  |\n| `connectionContext`                                                  | `hass.connection`, `hass.connected`, `hass.debugConnection`, `hass.hassUrl`                               |\n| `apiContext`                                                         | `hass.callService`, `hass.callApi`, `hass.callApiRaw`, `hass.callWS`, `hass.sendWS`, `hass.fetchWithAuth` |\n| `uiContext`                                                          | themes, selected theme, panels, sidebar, and UI state                                                     |\n| `narrowViewportContext`                                              | narrow-layout boolean                                                                                     |\n| `reducedMotionContext`                                               | `prefers-reduced-motion` media query                                                                      |\n\nLazy contexts subscribe on first consumer and tear down after the last consumer: `labelsContext`, `fullEntitiesContext`, `configEntriesContext`, `manifestsContext`, `triggerDescriptionsContext`, and `conditionDescriptionsContext`.\n\nThe single-field contexts such as `localizeContext`, `themesContext`, and `userContext` are deprecated. Use grouped contexts instead.\n\n## Consumption Patterns\n\nImport `consume` from `src/common/decorators/consume`, not from `@lit/context`. The `@lit/context` version forces a host update on every context change, so every consumed field rerenders the component, even without `@state()`, and `@transform` cannot skip anything. Our `consume` leaves update scheduling to the field:\n\n- `@state()` plus `@consume`: rerenders when the context value changes.\n- `@state()` plus `@consume` plus `@transform`: rerenders only when the transformed value changes.\n- `@consume` alone: the field stays current but never triggers a render. Use this for values only read in event handlers or callbacks, such as `apiContext`, or for data read on demand, such as picker item callbacks.\n\nIn reactive controllers, extend `ContextController` and decorate fields with `consumeContext({ context, subscribe, transform })` from the same file. `transform` stores one part of the context value, and `contextUpdated()` runs only when that part changes. `@state()` and `@transform` do not work on controller fields; call `host.requestUpdate()` when the host needs to rerender. Example: `src/data/service-info-controller.ts`.\n\nFor lazily created subscriptions, use `ContextSubscriptionController` from the same file instead of `@lit/context`'s `ContextConsumer`, which also forces a host update. Its callback must store the value in a reactive field or call `host.requestUpdate()` itself when the host needs to rerender.\n\nUse entity-scoped helpers when the component watches an entity ID held on the host:\n\n```ts\n@state() @consumeEntityState({ entityIdPath: [\"_config\", \"entity\"] })\nprivate _stateObj?: HassEntity;\n\n@state() @consumeEntityRegistryEntry({ entityIdPath: [\"_config\", \"entity\"] })\nprivate _entity?: EntityRegistryDisplayEntry;\n\n@state() @consumeLocalize()\nprivate _localize!: LocalizeFunc;\n```\n\nUse `consumeEntityStates` when the host property contains one or more entity IDs. It filters missing entities and preserves the previous record when none of the selected entities changed.\n\n```ts\n@state() @consumeEntityStates({ entityIdPath: [\"_config\", \"entities\"] })\nprivate _stateObjs?: Record<string, HassEntity>;\n```\n\nFor a single field from a grouped context, pair `@consume` with `@transform`:\n\n```ts\n@state()\n@consume({ context: uiContext, subscribe: true })\n@transform<HomeAssistantUI, Themes>({ transformer: ({ themes }) => themes })\nprivate _themes!: Themes;\n```\n\nUse `@transform` with `watch` when the transformer depends on a host property, such as a computed entity ID. `consumeEntityState` and `consumeEntityStates` only watch the first path segment.\n\nTo consume a whole group untransformed, omit `@transform` and type the field as `ContextType<typeof statesContext>` or the matching context type.\n\n## Review Checklist\n\n- The component consumes the narrowest context needed for the data it reads.\n- A broad `hass` property is kept only when the component is a container or external API requires it.\n- Entity-scoped reads use the consume helpers rather than ad hoc context transforms.\n- Context fields read during render or `willUpdate` are marked `@state()` so updates trigger rendering. Fields only read outside the render cycle are left without `@state()`.\n- Tests and mocks only provide the data the component actually consumes.\n",".agents/skills/ha-frontend-components/SKILL.md":"---\nname: ha-frontend-components\ndescription: Home Assistant frontend component patterns. Use when implementing or reviewing dialogs, ha-form, ha-alert, keyboard shortcuts, tooltips, panels, Lovelace cards, or ha-button usage.\n---\n\n# HA Frontend Components\n\nUse this skill when creating or reviewing Home Assistant UI components and common interaction patterns.\n\nCross-load `ha-frontend-events` when component work includes event listener typing, custom event dispatch, or event-map declarations.\n\n## Dialogs\n\nOpen dialogs through the fire-event pattern:\n\n```ts\nfireEvent(this, \"show-dialog\", {\n  dialogTag: \"dialog-example\",\n  dialogImport: () => import(\"./dialog-example\"),\n  dialogParams: { title: \"Example\", data: someData },\n});\n```\n\nDialog implementation requirements:\n\n- Use `ha-dialog`.\n- Use `DialogMixin`, which implements `HassDialogNext<T>`, for new dialogs. See `src/dialogs/dialog-mixin.ts`.\n- Read dialog parameters from the mixin's `params` property and render the dialog open. Return `nothing` while required parameters are absent.\n- Call the mixin's `closeDialog()` to close a new dialog. The mixin handles the `closed` event, fires `dialog-closed`, and removes the host element.\n- Existing dialogs may implement the legacy `HassDialog<T>` interface from `src/dialogs/make-dialog-manager.ts`.\n- Preserve the existing `showDialog()`, open-state, and close-event lifecycle when maintaining a legacy dialog; do not copy that lifecycle into a `DialogMixin` dialog.\n- Use `header-title` and `header-subtitle` for simple header text.\n- Use slots when standard header attributes are not enough.\n- Use `ha-dialog-footer` with `primaryAction` and `secondaryAction` slots.\n- Add `autofocus` to the first focusable element, such as `<ha-form autofocus>`, and forward it internally if needed.\n\nUse standard dialog widths: `small`, `medium`, `large`, or `full`. Avoid custom dialog sizing unless there is a clear product need.\n\n## Buttons\n\n`ha-button` wraps the Web Awesome button in `src/components/ha-button.ts`.\n\nAxes:\n\n- `variant`: `brand`, `neutral`, `danger`, `warning`, `success`.\n- `appearance`: `accent`, `filled`, `outlined`, `plain`.\n- `size`: `xs`, `s`, `m`, `l`, `xl`.\n\nCommon usage:\n\n- Use `appearance=\"filled\"` for primary emphasis when needed.\n- Use `appearance=\"plain\"` for cancel and dismiss actions.\n- Use `variant=\"danger\"` for destructive actions.\n- Place primary actions in `slot=\"primaryAction\"` and secondary actions in `slot=\"secondaryAction\"`.\n\n## Forms\n\n`ha-form` is schema-driven with `HaFormSchema[]` and supports common selectors for entities, devices, areas, targets, numbers, booleans, time, actions, text, objects, selects, icons, media, and location.\n\nUse `computeLabel`, `computeError`, and `computeHelper` for translated labels, validation, and helper text.\n\n```ts\n<ha-form\n  .hass=${this.hass}\n  .data=${this._data}\n  .schema=${this._schema}\n  .error=${this._errors}\n  .computeLabel=${(schema) => this._localize(`ui.panel.${schema.name}`)}\n  @value-changed=${this._valueChanged}\n></ha-form>\n```\n\n## Alerts\n\nUse `ha-alert` for user-visible status messaging.\n\n- Alert types: `error`, `warning`, `info`, `success`.\n- Useful properties: `title`, `alert-type`, `dismissable`, `narrow`.\n- Slots: `icon` for custom leading icon, `action` for custom action content.\n- Content is announced by screen readers when dynamically displayed.\n\n```ts\nhtml`\n  <ha-alert alert-type=\"error\">${this._localize(\"ui.example.error\")}</ha-alert>\n  <ha-alert alert-type=\"warning\" .title=${this._localize(\"ui.example.warning\")}>\n    ${this._localize(\"ui.example.description\")}\n  </ha-alert>\n  <ha-alert alert-type=\"success\" dismissable>\n    ${this._localize(\"ui.example.success\")}\n  </ha-alert>\n`;\n```\n\n## Shortcuts And Tooltips\n\nUse `ShortcutManager` from `src/common/keyboard/shortcuts.ts` for keyboard shortcuts. It blocks shortcuts in input fields, can prevent shortcuts during text selection, and supports character and KeyCode shortcuts for non-latin keyboards. See `src/state/quick-bar-mixin.ts` for global shortcut examples.\n\nUse `ha-tooltip` from `src/components/ha-tooltip.ts` for contextual hover help. See `src/components/ha-label.ts` for an example.\n\n## Panels And Lovelace Cards\n\nPanels commonly extend `SubscribeMixin(LitElement)` and receive route and narrow-layout properties.\n\nLovelace cards should implement `LovelaceCard`, validate config in `setConfig()`, handle loading, error, unavailable, and missing-entity states, and add a configuration editor when needed.\n\nCards are user-story surfaces. Support different households, entity types, responsive layouts, and accessible interaction states.\n",".agents/skills/ha-frontend-gallery/SKILL.md":"---\nname: ha-frontend-gallery\ndescription: Home Assistant frontend gallery structure, pages, demos, content, and verification. Use when changing files under gallery/, including gallery markdown, TypeScript demos, sidebar entries, mock data, page generation, or gallery builds.\n---\n\n# HA Frontend Gallery\n\nUse this skill for all work under `gallery/`. Follow the persistent repository guidance in `AGENTS.md` and load the matching specialist skills alongside this gallery-specific guidance.\n\n## Quick Reference\n\nRun commands from the repository root unless noted otherwise:\n\n```bash\ngallery/script/develop_gallery  # Start the gallery development server\ngallery/script/build_gallery    # Build the static gallery\npnpm lint                       # ESLint, Prettier, TypeScript, and Lit checks\npnpm lint:types                 # TypeScript compiler, without file arguments\n```\n\nNever run `pnpm lint:types` or `tsc` with file arguments. File arguments make `tsc` ignore `tsconfig.json` and can emit `.js` files into `src/`.\n\n## Purpose\n\nThe gallery is a developer and designer reference for Home Assistant frontend UI patterns. It documents component APIs, shows realistic Lovelace and more-info states, captures brand and copy guidance, and provides reproducible demos that are safe to inspect outside a running Home Assistant instance.\n\n- Prefer demonstrating real production components from `src/` instead of creating gallery-only replacements.\n- Keep fake state, sample data, and demo-only helpers inside `gallery/`.\n- Do not move gallery stubs or demo data into production code unless a production feature explicitly needs them.\n- Do not hand-edit generated output under `gallery/build/` or `gallery/dist/`.\n\n## Structure\n\n- `gallery/sidebar.js`: Defines gallery sections, headers, and explicit page ordering.\n- `gallery/script/develop_gallery`: Wrapper for the `develop-gallery` gulp task.\n- `gallery/script/build_gallery`: Wrapper for the `build-gallery` gulp task.\n- `gallery/src/entrypoint.js`: Creates the `<ha-gallery>` shell.\n- `gallery/src/ha-gallery.ts`: Renders the drawer, page routing, markdown descriptions, demos, edit links, and RTL toggle.\n- `gallery/src/html/index.html.template`: HTML template used by the gallery build.\n- `gallery/src/pages/<category>/<page>.markdown`: Optional page description and frontmatter.\n- `gallery/src/pages/<category>/<page>.ts`: Optional live demo module for the same page ID.\n- `gallery/src/components/`: Gallery-only demo wrappers like `demo-card`, `demo-cards`, `demo-more-info`, and `page-description`.\n- `gallery/src/data/`: Fake `hass`, demo states, mock traces, and reusable sample data.\n- `gallery/public/`: Static assets copied into the gallery output.\n\n## Page Model\n\nGallery pages are generated by `gather-gallery-pages` in `build-scripts/gulp/gallery.js`.\n\n- A page ID is the path under `gallery/src/pages/` without the extension, like `components/ha-button`.\n- A `.markdown` file and a `.ts` file with the same page ID become one gallery page.\n- A page may have only markdown, only a TypeScript demo, or both.\n- Markdown can contain YAML frontmatter with `title` and optional `subtitle`.\n- Markdown that contains only frontmatter contributes metadata without rendering a description block.\n- TypeScript demo modules are dynamically imported for side effects when the page is opened.\n- A demo module must define a custom element named `demo-${category}-${page}` with slashes replaced by hyphens, like `demo-components-ha-button` for `components/ha-button`.\n- `gallery/src/ha-gallery.ts` renders that element with `dynamicElement()` based on the current page ID.\n\n## Sidebar\n\nUse `gallery/sidebar.js` when a page needs a visible section, section header, or deterministic ordering.\n\n- `category` must match the first directory name under `gallery/src/pages/`.\n- `header` is the section label shown in the drawer.\n- `pages` is optional. When present, listed pages keep that exact order.\n- Pages in a category that are not listed are appended alphabetically after the listed pages.\n- New categories without a sidebar entry are appended by the generator with their category name as the header.\n- If a listed page does not exist, the generator logs an error during `gather-gallery-pages`.\n\n### Subsections\n\nA section can group its pages under named subsections instead of one flat list. Use this for large categories where related pages should sit together.\n\n- `subsections` is an array of `{ header, pages }`. It is mutually exclusive with a flat `pages` array on the same group.\n- Each subsection `header` is a non-collapsible label rendered inside the section's expansion panel; the section stays the only collapsible level.\n- Listed pages keep their per-subsection order.\n- Any pages found in the category but not listed in a subsection are collected into a generated `Other` subsection, appended alphabetically. The `Other` subsection is omitted when there are no leftovers.\n- A listed page that does not exist still logs an error during `gather-gallery-pages`.\n- Use sentence case for subsection headers and follow the content standards below.\n\n## Markdown Pages\n\nUse markdown pages for explanations, design guidance, API notes, and copy standards.\n\n- Start with frontmatter when the page needs a title or subtitle.\n- Use sentence case for titles, headings, labels, and UI copy.\n- Put the live example before the reference API when that makes the page easier to scan.\n- Use fenced code blocks with a language tag for copyable examples.\n- Keep examples short and focused on the behavior being documented.\n- Prefer real component names and attributes over prose-only descriptions.\n- Use Home Assistant terminology from `ha-frontend-user-facing-text`.\n- For remove/delete and add/create wording, follow `gallery/src/pages/misc/remove-delete-add-create.markdown`.\n\nGallery markdown is documentation content and is not localized with `localize`. If demo code creates production UI strings, follow the localization and copy guidance in `ha-frontend-user-facing-text`.\n\n## Demo Components\n\nUse TypeScript demo pages for interactive or stateful examples.\n\n- Import production components from `src/` using the correct relative path from the demo file.\n- Import reusable gallery helpers from `gallery/src/components/` when they already model the pattern.\n- Use `demo-card` and `demo-cards` for Lovelace card examples that render YAML card configs.\n- Use `demo-more-info` and `demo-more-infos` for more-info dialog examples.\n- Use shared mock data from `gallery/src/data/` instead of repeating large fake state objects inline.\n- Show meaningful states, such as loading, unavailable, empty, error, active, inactive, and disabled when relevant.\n- Check responsive behavior and the gallery RTL toggle when layout or direction-sensitive UI changes.\n- Keep unavoidable casts or loose demo parsing local to the demo helper or demo page.\n\nThe gallery ESLint config allows `console` for gallery diagnostics. Do not copy that exception into production frontend code.\n\n## Content Standards\n\nFollow the detailed copy standards in `ha-frontend-user-facing-text`: use American English, sentence case, active voice, inclusive language, direct user-focused wording, and consistent Home Assistant terminology.\n\n- Use `Home Assistant` in full, not `HA` or `HASS`.\n- Use `integration` instead of `component` for product concepts.\n- Use `Remove` for reversible disassociation and `Delete` for permanent deletion.\n- Use `Add` for existing items and `Create` for something made from scratch.\n- Avoid Latin abbreviations like `e.g.` and `i.e.` in prose.\n- Avoid stitching sentence fragments together in production UI examples.\n\n## Verification\n\n- For markdown, sidebar, and page-generation changes, run `gallery/script/build_gallery`.\n- For TypeScript demo or gallery shell changes, run the smallest relevant check plus `pnpm lint` when practical.\n- For type checking, run `pnpm lint:types` without file arguments.\n- For visual changes, run `gallery/script/develop_gallery` and check the affected page on desktop, narrow viewport, and RTL when relevant.\n- If verification is skipped, state which command was skipped and why.\n",".agents/skills/ha-frontend-review/SKILL.md":"---\nname: ha-frontend-review\ndescription: Home Assistant frontend PR and review guidance, including implementation quality, existing review feedback, established design patterns, and the authority of UI/UX evidence. Use when reviewing frontend changes, preparing a PR, checking recurring review issues, or applying the PR template.\n---\n\n# HA Frontend Review\n\nUse this skill when reviewing Home Assistant frontend changes or preparing a pull request.\n\n## Review Preparation\n\n1. Establish the review scope from the diff and the behavior changed by it.\n2. Before identifying findings, use the [project skill catalogue](../../../AGENTS.md#project-skills) to load every matching companion's `SKILL.md`. Follow applicable companion references in those skills as well; this review skill supplies the workflow, and companions supply the domain-specific criteria.\n3. Apply all loaded guidance within that scope. Report only problems introduced or worsened by the changeset, anchored to changed lines. Read surrounding code to understand affected behavior, not to generate unrelated cleanup or migration requests.\n\n## Pull Request Body\n\nWhen creating a pull request, use `.github/PULL_REQUEST_TEMPLATE.md` as the body.\n\n- Do not omit, reorder, or rewrite template sections.\n- Check the appropriate \"Type of change\" box based on the actual change.\n- Do not check checklist items on behalf of the user.\n- If the PR includes UI changes, remind the user to add screenshots or a short video.\n- Explain what the change does for users, not only implementation details.\n- Use Markdown.\n\n## Pre-Submission Checklist\n\n- `pnpm lint` passes when practical for the scope.\n- `pnpm test` or focused relevant tests are green when practical for the scope.\n- Each test added by the change protects real logic, not the look of a component.\n- User-facing text is localized and follows `ha-frontend-user-facing-text` guidance.\n- Components handle loading, error, unavailable, and missing-entity states.\n- Entity existence is checked before property access.\n- Event listeners and subscriptions are cleaned up.\n- UI is accessible to screen readers and keyboard users.\n\n## Recurring Review Issues\n\nScope and public surface:\n\n- Keep changes independently reviewable and limited to the requested area.\n- Prefer existing Home Assistant helpers, Lit primitives, and component seams over parallel implementations.\n- Challenge new public properties and optional feature surface when transient options or existing seams meet the requirement with less lifecycle and consistency cost.\n\nStateful and asynchronous UI:\n\n- Review transitions in both directions, not only individual rendered states.\n- When controls reappear, restore valid defaults instead of retaining state that was only valid while they were hidden.\n- Establish immutable dirty-state baselines before asynchronous work, guard against stale responses, and preserve unsaved state in mounted editors.\n- Determine an action's current meaning before applying dirty-state checks, especially when an action can change between Save and Close.\n\nReadiness and invalidation:\n\n- Treat readiness as the first displayable terminal result, including stable empty and error states.\n- Register child readiness before resolving the parent, do not treat fallback work as terminal, and replay readiness correctly for cached or reused panels.\n- Ensure every value read by memoized output participates in its invalidation.\n\nRepository-owned contracts:\n\n- Consult the public [frontend contributor documentation](https://contributors.home-assistant.io/frontend/) for documented architecture, data flow, design, and development workflows.\n- For new leaf components, load `ha-frontend-contexts` and verify they consume narrow contexts instead of introducing a broad `hass` property; containers and external APIs may still require `hass`.\n- Verify backend assumptions against the owning Core, Supervisor, or WebSocket implementation, and component assumptions against the exported component contract.\n- Prefer canonical repository helpers and test setup over duplicate local implementations.\n- Promote AI-review concerns into durable guidance only when supported by code evidence, reproduced behavior, an accepted corrective commit, or human-maintainer validation.\n\nUser experience and accessibility:\n\n- Forms need proper labels, helper text, and validation feedback.\n- Form markup should not cause password managers to identify fields incorrectly.\n- Clickable areas should be large enough for touch interaction.\n- Hover, active, disabled, loading, and focus states should be clear.\n\nDialog and modal patterns:\n\n- Multi-step operations should show progress.\n- Dialog state should survive background operations correctly.\n- Cancel and close buttons should behave consistently.\n- Defaults should be helpful without blocking user override.\n\nComponent design patterns:\n\n- Terminology should be consistent. Use words like \"Join\" or \"Apply\" instead of \"Group\" when that better matches the user action.\n- Visual hierarchy should use appropriate font sizes, weights, and spacing ratios.\n- Components should align to the design grid.\n- Badges and indicators should be placed consistently.\n\nCode quality:\n\n- Null and undefined paths should be handled explicitly.\n- Potentially undefined array and object access should be guarded.\n- Event handlers, timers, observers, and subscriptions should be cleaned up.\n\nConfiguration and props:\n\n- Make configuration fields optional when sensible.\n- Provide reasonable defaults.\n- Keep APIs extensible without adding speculative abstractions.\n- Validate configuration before applying changes.\n\n## UI/UX Evidence\n\nFor user-facing changes, establish the existing design context as part of frontend review:\n\n- Treat an applicable gallery specification as the authoritative repository source for the documented component or interaction. Inspect its written guidance and demos when present.\n- Note relevant production designs, approved designs, and shared `ha-*` components so the implementation can also be compared with established frontend behaviour.\n- Require the applicable gallery documentation or demo to change when the implementation intentionally changes behaviour documented there.\n- Prefer an established input layout or appropriate shared component over a raw input or ad hoc control.\n- Treat explicit UI/UX approval, current repository guidance, and direct human or workflow instructions as stronger evidence than inference.\n- Treat linked tasks as evidence of the problem and stated requirements, not automatic UI/UX approval.\n- When the direction remains uncertain, search frontend pull requests that carry or previously carried **Needs UX**. Inspect their label history, comments, and reviews rather than relying on the current label alone.\n- Judge relevant feedback by its content and surrounding discussion. When GitHub provides `author_association`, prioritise feedback marked `MEMBER`.\n- Prefer recent feedback about similar interactions or components. Label removal can show that a workflow gate moved on, but does not prove approval without the surrounding discussion.\n- Summarise the applicable guidance instead of maintaining a reviewer list or treating one historical decision as a permanent rule.\n\n## Review Flow\n\n- Before reviewing a pull request, read its existing comments, reviews, and threads, including their status, resolver, and Copilot resolution reason when available.\n- Prioritise substantive human feedback, especially from authors marked `MEMBER`, and validate agent-generated feedback against the code and repository guidance.\n- Do not duplicate unresolved findings as new inline comments; reference any that still need action in the review summary. Treat resolved feedback as closed only when the resolution reason or surrounding discussion supports that outcome; otherwise validate it against the current code before suppressing it. Respect **Won't fix** and **Incorrect** reasons.\n- When a later commit addresses an unresolved thread, note in the review summary that it looks addressed, linking the thread and the commit, so it can be resolved. Do not report it as still open.\n- Identify behavioral regressions, bugs, accessibility issues, and missing tests that `ha-frontend-testing` calls for first.\n- Do not ask for new tests on visual components. If the visuals clearly changed and the PR has no screenshots or videos, suggest adding them instead.\n- When the change adds something repository guidance rules out, such as a rendering test, open the comment with the removal (\"Remove this test file\") and cite the guidance in one sentence. Do not critique how the unwanted code is written, as that reads as a request to fix it.\n- Record the applicable UI/UX evidence for user-facing changes, whether or not further input is needed.\n- Keep style-only comments secondary unless they affect maintainability or user experience.\n- Prefer small, direct fixes over large refactors during review follow-up.\n","AGENTS.md":"# Home Assistant Frontend Agent Guide\n\nYou are helping develop the Home Assistant frontend. This repository is a TypeScript application built from Lit-based Web Components for the Home Assistant web UI.\n\n## Essential Commands\n\n```bash\npnpm lint          # ESLint + Prettier + TypeScript + Lit\npnpm format        # Auto-fix ESLint + Prettier\npnpm lint:types    # TypeScript compiler, run without file arguments\npnpm test          # Vitest\npnpm dev           # App dev server, supports --background/--status/--stop/--logs\npnpm dev:serve     # Local serving dev server, supports -c core URL, -p port, and dev flags\n```\n\nNever run `tsc` or `pnpm lint:types` with file arguments. When `tsc` receives file arguments, it ignores `tsconfig.json` and can emit `.js` files into `src/`. Always run `pnpm lint:types` without arguments. For individual file type checking, rely on editor diagnostics.\n\n## Architecture\n\n- The frontend uses custom elements built with Lit and TypeScript strict mode.\n- Components communicate with the backend through the Home Assistant WebSocket API.\n- Use `ha-` for Home Assistant components, `hui-` for Lovelace UI components, and `dialog-` for dialogs.\n- Prefer `ha-*` components and current Web Awesome wrappers. Avoid adding new legacy `mwc-*` usage.\n- Leaf components should consume narrow Lit contexts instead of taking the broad `hass` object unless they are containers that own and provide `hass`.\n\n## Development Standards\n\n- Use strict TypeScript, proper interfaces, and `import type` for type-only imports.\n- Avoid `any`; model data with existing Home Assistant types or narrow new types.\n- Keep imports organized and remove unused imports.\n- Do not use `console`; use existing logging or user-visible error patterns.\n- Use `@state()` for internal Lit state and `@property()` for public API.\n- Do not query or manipulate DOM manually when Lit decorators, component refs, or render state are appropriate.\n- Scope styles to components, use theme custom properties, and keep layouts mobile-first and RTL-safe.\n- All user-facing text must be localized through the translation system.\n- Do not write tests just because you changed some code. Write a test when there is real logic that could break without anyone noticing, and explain what the test protects.\n\n## Project Skills\n\nDetailed guidance lives in `.agents/skills/<name>/SKILL.md`. Load every matching skill before detailed implementation or review. For reviews, load `ha-frontend-review` alongside all companions that apply to the changed code or behavior:\n\n- `ha-frontend-contexts`: Lit contexts, `hass` migration, and rerender-sensitive state access.\n- `ha-frontend-components`: dialogs, forms, alerts, shortcuts, tooltips, panels, and Lovelace cards.\n- `ha-frontend-events`: event handler typing, custom event dispatch, and event-map declarations.\n- `ha-frontend-types`: backend data contracts, optional schemas, shared types, assertions, and lifecycle types.\n- `ha-frontend-lit`: reactive fields, DOM queries, lifecycle behavior, and render-derived state.\n- `ha-frontend-styling`: theme variables, spacing tokens, responsive layout, RTL, and view transitions.\n- `ha-frontend-testing`: lint, typecheck, Vitest, Playwright e2e dev servers, and benchmarks.\n- `ha-frontend-query-params`: typed URL query params, `QueryParamsController`, data table URL sync, in-app links, and My Home Assistant redirect params.\n- `ha-frontend-user-facing-text`: localization, terminology, sentence case, and Home Assistant text style.\n- `ha-frontend-review`: PR template use, existing review feedback, review checklist, recurring issues, and UI/UX evidence including gallery specifications.\n- `ha-frontend-ux-readiness`: UI/UX routing that complements frontend review for new user experiences.\n- `ha-frontend-gallery`: gallery pages, demos, sidebar structure, content, and verification.\n- `ha-frontend-demo`: standalone demo structure, configurations, navigation, shared stubs, and verification.\n\n## Pull Requests\n\nWhen creating a pull request, use `.github/PULL_REQUEST_TEMPLATE.md` as the PR body. Preserve template sections, check only the appropriate type-of-change boxes, and do not check checklist items on behalf of the user. If the PR includes UI changes, remind the user to add screenshots or a short video.\n\n## AI policy\n\nThis project follows the [Open Home Foundation AI Policy](AI_POLICY.md).\nAutonomous contributions are not accepted: a human must review, understand,\nand be able to explain every change before it is submitted. Do not open\nissues or pull requests autonomously, and do not post comments on behalf of\na user without their review.\n"},"files":{".agents/skills/ha-frontend-types/SKILL.md":"---\nname: ha-frontend-types\ndescription: Home Assistant frontend TypeScript conventions. Use when defining or reviewing backend data contracts, optional schemas, shared types, assertions, or Lit lifecycle types.\n---\n\n# HA Frontend Types\n\nUse this skill for Home Assistant-specific TypeScript contracts and type choices.\n\n## Home Assistant Data Contracts\n\nVerify data contracts against the source that owns them, such as Home Assistant Core, Supervisor, a WebSocket handler, or an exported component type. Do not shape a type around assumptions made by its current frontend consumers.\n\n- Match required, optional, nullable, and defaulted fields to the producer and runtime contract. Preserve optional configuration fields when omission is supported and has defined behavior.\n- Use distinct request and response types when their wire shapes differ.\n- When changing a shared contract, check affected consumers, tests, fixtures, and mocks.\n\n## Reuse Home Assistant Contracts\n\n- Reuse the canonical Home Assistant type when one exists. Define shared contract types in the data or API module that owns them.\n- Reuse types exported by components and helpers rather than reconstructing their payloads. For event types, follow `ha-frontend-events`.\n- When one domain contract is used across modules, define and export it from the module that owns that contract.\n\nPrefer an existing owning contract. Introduce a frontend-specific type when the frontend shape or boundary genuinely differs.\n\n## Assertions\n\nPrefer accurate types and runtime narrowing. Use assertions or TypeScript suppressions at boundaries where the runtime invariant is understood but cannot be expressed cleanly; keep them narrow and explain non-obvious invariants.\n\n## Lit Lifecycle Types\n\nFor Lit lifecycle methods that receive changed properties, use `PropertyValues<this>` when the method only needs public reactive properties:\n\n```ts\nprotected willUpdate(changedProperties: PropertyValues<this>) {\n  // ...\n}\n```\n\nUse unparameterized `PropertyValues` when the method inspects private or protected reactive properties, which are not keys of `this`. Do not add assertions solely to retain `PropertyValues<this>`.\n\n## Enforced Baseline\n\nUse `import type` for type-only imports. This is enforced by the repository ESLint configuration.\n",".agents/skills/ha-frontend-lit/SKILL.md":"---\nname: ha-frontend-lit\ndescription: Home Assistant frontend Lit conventions. Use when working with reactive properties, internal state, DOM queries, lifecycle methods, or render-derived state.\n---\n\n# HA Frontend Lit\n\nUse this skill when implementing or reviewing Lit component state, DOM access, lifecycle methods, or rendering behavior. Cross-load `ha-frontend-types` for Home Assistant data contracts, assertions, and lifecycle parameter types.\n\n## Reactive Fields\n\nThis project currently uses Lit's TypeScript experimental decorators with `useDefineForClassFields: false`. Match existing declarations and do not introduce standard-decorator `accessor` syntax unless the project changes decorator mode.\n\n- Use `@property()` for public reactive API and `@state()` for private reactive state.\n- Prefer inferred types for initialized reactive fields when inference preserves the intended type; annotate when widening or an external contract requires it.\n\n## DOM Queries\n\nPrefer Lit's `@query()` or `@queryAll()` decorators for fixed selectors in the component's render root.\n\n- Type the decorated field with the narrowest useful DOM or component interface.\n- Keep the field optional when it may be absent at the point of access, including conditional rendering or pre-render lifecycle access.\n- Use a definite assignment assertion only when every call site runs after the node is guaranteed to exist.\n- The optional second argument to `@query()`, as in `@query(\"#target\", true)`, caches the first query result. Use it only when later renders cannot replace the queried node.\n- Use a direct query when the selector is dynamic or the target is outside the component's render root. Before querying a child, consider whether the required value belongs in parent state or data flow.\n\n## Render-Derived State\n\n- Prefer render-local values for inexpensive structures used only by that render.\n- Assign a render-local value once when repeated evaluation is non-trivial or a local name improves clarity.\n- Keep purely presentational derivations in `render()`. Use stored state or `willUpdate()` when the value must participate in lifecycle work, reflection, CSS, or non-render consumers.\n- Use `memoizeOne` for pure, argument-derived transforms when stable input identity avoids meaningful repeated work. Keep inputs explicit and limited, and do not add caching without a credible benefit over computing the value directly.\n\n## References\n\n- [Reactive properties](https://lit.dev/docs/components/properties/)\n- [Decorators](https://lit.dev/docs/components/decorators/)\n- [Shadow DOM queries](https://lit.dev/docs/components/shadow-dom/#query)\n- [Reactive update cycle](https://lit.dev/docs/components/lifecycle/)\n",".agents/skills/ha-frontend-styling/SKILL.md":"---\nname: ha-frontend-styling\ndescription: Home Assistant frontend styling, theming, spacing, responsive layout, RTL, and View Transitions guidance. Use when editing CSS, layout, motion, or visual component structure.\n---\n\n# HA Frontend Styling\n\nUse this skill when editing CSS, layout, visual hierarchy, theme integration, responsive behavior, RTL support, or view transitions.\n\n## Theme And Layout Basics\n\n- Use Home Assistant CSS custom properties instead of hardcoded colors.\n- Use `--ha-space-*` spacing tokens instead of hardcoded spacing where possible.\n- Keep components mobile-first and enhance for desktop.\n- Keep layouts RTL-safe. Prefer logical properties when they fit.\n- Prefer `ha-*` components and current Web Awesome wrappers.\n- Avoid adding new legacy Material Web Components (`mwc-*`).\n- Scope styles to the component. Do not rely on global styles for component internals.\n\nSpacing tokens are defined in `src/resources/theme/core.globals.ts`. The scale runs from `--ha-space-1` at 4px through `--ha-space-20` at 80px in 4px increments. Common values are `--ha-space-2` at 8px, `--ha-space-4` at 16px, and `--ha-space-8` at 32px.\n\n```ts\nstatic get styles() {\n  return css`\n    :host {\n      padding: var(--ha-space-4);\n      color: var(--primary-text-color);\n      background-color: var(--card-background-color);\n    }\n\n    .content {\n      gap: var(--ha-space-2);\n    }\n\n    @media (max-width: 600px) {\n      :host {\n        padding: var(--ha-space-2);\n      }\n    }\n  `;\n}\n```\n\n## Interaction States\n\n- Make touch targets large enough for mobile.\n- Provide clear hover, active, focus, disabled, loading, error, and unavailable states.\n- Preserve keyboard navigation and visible focus indicators.\n- Maintain WCAG AA contrast for text and essential UI affordances.\n\n## View Transitions\n\nUse the View Transitions API only for meaningful continuity between DOM states.\n\nCore resources:\n\n- Utility wrapper: `src/common/util/view-transition.ts`, `withViewTransition()`.\n- Launch-screen fade example: `src/util/launch-screen.ts`.\n- Animation keyframes: `src/resources/theme/animations.globals.ts`.\n- Animation duration tokens: `src/resources/theme/core.globals.ts`.\n\nImplementation rules:\n\n- Use `withViewTransition()` for fallback behavior.\n- Keep transitions simple. Subtle fades and crossfades usually work best.\n- Use `--ha-animation-duration-fast`, `--ha-animation-duration-normal`, or `--ha-animation-duration-slow` for timing.\n- Ensure each `view-transition-name` is unique at any given time.\n- Remember only one view transition can run at a time.\n- View transitions operate at document level and do not work inside Shadow DOM style isolation. For web components, set `view-transition-name` on `:host` or use document-level transitions.\n- The root gets `view-transition-name: root` by default. Target `::view-transition-group(root)` to customize the default page transition.\n\n## Review Checklist\n\n- Styling uses theme variables and spacing tokens where practical.\n- Layout is mobile-first and RTL-safe.\n- Component styles are scoped.\n- Interactive states are clear and accessible.\n- Motion is subtle, tokenized, and respects reduced-motion behavior through existing globals.\n",".agents/skills/ha-frontend-user-facing-text/SKILL.md":"---\nname: ha-frontend-user-facing-text\ndescription: Home Assistant frontend copy, localization, terminology, and user-facing text guidance. Use when adding or reviewing labels, buttons, dialogs, errors, translations, or UI strings.\n---\n\n# HA Frontend User-Facing Text\n\nUse this skill for all user-facing text, translations, labels, buttons, dialog copy, errors, helper text, and review comments about wording.\n\n## Localization\n\n- All user-facing text must be translatable.\n- Add translation keys to `src/translations/en.json` when introducing new strings.\n- Use the localization system instead of inline user-visible strings.\n- Prefer complete localized strings with placeholders over concatenating translated fragments.\n- Give translators enough context through key naming and placeholders.\n\n```ts\nthis._localize(\"ui.panel.config.updates.updates_refreshed\", {\n  count: 5,\n});\n```\n\n## Voice And Style\n\n- Use American English.\n- Use a friendly, informational tone.\n- Address users directly with \"you\" and \"your\" when appropriate.\n- Be inclusive, objective, and non-discriminatory.\n- Be concise and clear.\n- Use active voice.\n- Avoid jargon where a familiar home automation term works.\n- Always write \"Home Assistant\" in full. Do not use \"HA\" or \"HASS\" in user-facing copy.\n- Spell out terms when possible.\n- Use sentence case for titles, headings, buttons, labels, and UI elements.\n- Use the Oxford comma in lists.\n- Prefer \"like\" over \"e.g.\" and \"for example\" over \"i.e.\".\n- Avoid all caps for emphasis. Use wording, bold, or italics instead.\n- Write for both technical and non-technical users.\n\nSentence case examples:\n\n- Use: \"Create new automation\"\n- Avoid: \"Create New Automation\"\n- Use: \"Device settings\"\n- Avoid: \"Device Settings\"\n\n## Terminology\n\nUse \"integration\" instead of \"component\" for user-facing product language unless referring to a frontend component in developer context.\n\nTechnical product terms are lowercase in prose: automation, entity, device, service.\n\n## Delete, Remove, Create, Add\n\nUse \"Remove\" for actions that can be restored or reapplied:\n\n- Removing a user's permission.\n- Removing a user from a group.\n- Removing links between items.\n- Removing a widget from a dashboard.\n- Removing an item from a cart.\n\nUse \"Delete\" for permanent, non-recoverable actions:\n\n- Deleting a field.\n- Deleting a value in a field.\n- Deleting a task.\n- Deleting a group.\n- Deleting a permission.\n- Deleting a calendar event.\n\nUse \"Add\" for already-existing items:\n\n- Adding a permission to a user.\n- Adding a user to a group.\n- Adding links between items.\n- Adding a widget to a dashboard.\n- Adding an item to a cart.\n\nUse \"Create\" for something made from scratch:\n\n- Creating a new field.\n- Creating a new task.\n- Creating a new group.\n- Creating a new permission.\n- Creating a new calendar event.\n\nCreate pairs with Delete. Add pairs with Remove.\n\n## Review Checklist\n\n- Text is localized.\n- Copy uses sentence case.\n- \"Home Assistant\" is written in full.\n- Delete/Remove and Create/Add match recoverability and object lifecycle.\n- Placeholders are used instead of string concatenation.\n- The wording is concise and understandable without implementation knowledge.\n",".agents/skills/ha-frontend-testing/SKILL.md":"---\nname: ha-frontend-testing\ndescription: Home Assistant frontend testing and validation workflow. Use when adding or updating tests, running lint, TypeScript checks, Vitest, Playwright e2e suites, dev servers, or chart-data benchmarks.\n---\n\n# HA Frontend Testing\n\nUse this skill when choosing or running validation for frontend changes.\n\n## Test Helpers\n\n- Before adding or changing tests, inspect the relevant suite's existing helpers and fixtures. Reuse them instead of duplicating setup, test data, navigation, interactions, waits, or assertions.\n- When the same test flow appears more than once, move it into the closest suite-local helper with a focused interface.\n- Keep one-off test behaviour in the test unless a helper makes the intent materially clearer. Do not hide the behaviour under test behind broad, configurable abstractions.\n\n## Core Commands\n\n```bash\npnpm lint          # ESLint + Prettier + TypeScript + Lit\npnpm format        # Auto-fix ESLint + Prettier\npnpm lint:types    # TypeScript compiler, run without file arguments\npnpm test          # Vitest\npnpm build         # Full production build\npnpm dev           # App dev server\npnpm dev:serve     # Local serving dev server\n```\n\nNever run `tsc` or `pnpm lint:types` with file arguments. File arguments make `tsc` ignore `tsconfig.json` and can emit `.js` files into `src/`.\n\nFor focused type feedback on one file, use editor diagnostics instead of a file-scoped `tsc` command.\n\n## Production Builds\n\nProduction builds support foreground and managed background execution:\n\n```bash\npnpm build                         # Full foreground build\npnpm build --background            # Full managed background build\npnpm build --modern                # Modern frontend_latest bundle only\npnpm build --modern --background   # Modern managed background build\npnpm build --status\npnpm build --logs [--follow]\npnpm build --stop\n```\n\nUse `pnpm build --modern --background` for production bundle-size or browser performance comparisons that only need modern browser output. It runs the normal metadata and static preparation, minifies and compresses the modern `frontend_latest` bundle and shared static assets, and generates modern-only entry pages and service workers. It deliberately skips the legacy bundle and its service worker.\n\nDo not pass `--help`, `--background`, or `--modern` to `script/build_frontend`; that raw script does not parse arguments and always starts the full foreground build. Use `pnpm build` for managed builds. App builds and development servers keep exclusive ownership of `hass_frontend/` for their lifetime.\n\nManaged app, demo, gallery, and E2E app workflows share one lifetime lock, so only one build or development server can run at a time.\n\n## When To Add Tests\n\n- Write tests for code that computes something: data processing, utilities, config validation, and strategies.\n- Do not write rendering tests. This includes views, panels, and components whose text, styles, slots, or option defaults are checked, or that only put context and helper data into a template.\n- Do not try to cover every scenario, especially for behaviour that changes often.\n- If you are not sure a test is useful, describe it and what it would catch, and let the user decide.\n- Tests never talk to a real Home Assistant. Replace `callWS`, `callApi`, and the connection with fakes.\n\n## Dev Servers\n\n`pnpm dev` builds and watches the app, served by a running Home Assistant core configured through `development_repo`.\n\n`pnpm dev:serve` also serves locally and supports `-c` for the core URL and `-p` for the port. The default is 8124, or 8123 in a devcontainer.\n\nDev server commands support `--background`, `--status`, `--stop`, and `--logs [--follow]`. `pnpm dev`, `pnpm dev:serve`, `pnpm dev:demo`, and `pnpm dev:gallery` also support `--fetch-translations`; this runs translation fetching, including first-time GitHub device authentication, under the workflow lock before starting the watcher. It works in foreground and background modes. Prefer managed background mode while iterating so the watcher stays available across test runs without occupying the terminal. `pnpm dev` and `pnpm dev:serve` share one managed process slot because both write the app output.\n\n## Playwright E2E\n\nEach suite has its own dev server port. Playwright reuses an existing server locally when its configured URL responds; otherwise it performs a slow full build. When a development watcher is being reused, rspack recompiles on save and reruns should not need a restart.\n\nStart the relevant suite server, then run that suite:\n\n| Suite   | Background server                            | Test command            |\n| ------- | -------------------------------------------- | ----------------------- |\n| App     | `pnpm test:e2e:app:dev --background` on 8095 | `pnpm test:e2e:app`     |\n| Demo    | `pnpm dev:demo --background` on 8090         | `pnpm test:e2e:demo`    |\n| Gallery | `pnpm dev:gallery --background` on 8100      | `pnpm test:e2e:gallery` |\n\nThe custom development wrappers use `/__ha_dev_status` to identify and manage their own suites. Playwright server reuse checks the configured URL instead. Wrapper start and stop operations are idempotent for a matching suite and reject an unrelated process occupying the port.\n\nLocal runs against a watched development server do not always match CI's clean build artifacts, environment, sharding, or worker configuration. Use background servers for the fast iteration loop, but confirm the relevant CI jobs complete successfully before considering E2E changes verified.\n\nUse `-g \"<title>\" --project=chromium` to narrow a run. `pnpm test:e2e` runs suites sequentially when managed servers are unavailable to prevent cold builds racing over shared generated assets. Run suites directly; piping through output truncation hides progress and failures.\n\nThe app suite uses a stripped-down harness for e2e. Demo and gallery use their normal dev servers.\n\n## Benchmarks\n\nFor chart data transforms such as history, statistics, energy, and downsampling, read and follow the complete workflow in `test/benchmarks/README.md` before making benchmark or optimization changes.\n\nThat workflow owns the baseline, noise analysis, guardrails, acceptance thresholds, and reporting requirements. In particular, optimizations must keep output bit-identical; never update snapshots or modify fixtures to make an optimization pass.\n\n## Verification Selection\n\n- Documentation-only change: no code test required unless examples or commands changed.\n- Type-only or utility change: run focused Vitest if available, then `pnpm lint:types` if practical.\n- Lit component change: run relevant tests plus lint or typecheck depending on scope.\n- E2E-sensitive flow: start the relevant e2e dev server and run the narrow Playwright suite.\n- Broad refactor: run `pnpm lint` and relevant test suites when practical.\n",".agents/skills/ha-frontend-events/SKILL.md":"---\nname: ha-frontend-events\ndescription: Home Assistant frontend event patterns. Use when typing event handlers, using HASSDomEvent types, dispatching with fireEvent, or declaring HASSDomEvents and event maps.\n---\n\n# HA Frontend Events\n\nUse this skill when implementing or reviewing event listeners and custom event contracts. Cross-load `ha-frontend-components` when the work also involves dialogs, forms, alerts, shortcuts, tooltips, panels, Lovelace cards, or buttons.\n\n## Event Handling\n\nUse the event types from `src/common/dom/fire_event.ts` instead of plain `Event`, generic `CustomEvent`, or element casts when they express the handler contract:\n\n- Use `HASSDomCurrentTargetEvent<T>` to read the element on which the listener was registered through `ev.currentTarget`.\n- Use `HASSDomTargetEvent<T>` only to read the element that originated the event through `ev.target`.\n- Use `HASSDomEvent<T>` to read a custom event payload through `ev.detail`.\n- Use `ValueChangedEvent<T>` from `src/types.ts` for the standard `value-changed` event.\n- Prefer an event type exported by the component being listened to, such as `HaSelectSelectEvent<T, Clearable>` or `HaDropdownSelectEvent<TValue, TData>`, over reconstructing its detail type.\n- Use `ActionHandlerEvent` from `src/data/lovelace/action_handler.ts` for Lovelace tap, hold, and double-tap handlers.\n\nImport event and element types with `import type`:\n\n```ts\nimport type {\n  HASSDomCurrentTargetEvent,\n  HASSDomEvent,\n  HASSDomTargetEvent,\n} from \"../common/dom/fire_event\";\nimport type { HaCheckbox } from \"../components/ha-checkbox\";\nimport type { HaEntityPicker } from \"../components/entity/ha-entity-picker\";\nimport type { HaRadioGroup } from \"../components/radio/ha-radio-group\";\nimport type { ValueChangedEvent } from \"../types\";\n```\n\nType the handler so the selected property can be read directly. Do not cast `ev.currentTarget` or assign it to a single-use variable:\n\n```ts\nprivate _scopeChanged(ev: HASSDomCurrentTargetEvent<HaRadioGroup>): void {\n  this._scope = ev.currentTarget.value;\n}\n\nprivate _checkedChanged(ev: HASSDomTargetEvent<HaCheckbox>): void {\n  this._checked = ev.target.checked;\n}\n\nprivate _valueChanged(ev: ValueChangedEvent<string>): void {\n  this._value = ev.detail.value;\n}\n\nprivate _itemSelected(ev: HASSDomEvent<{ id: string }>): void {\n  this._selectedId = ev.detail.id;\n}\n```\n\nUse intersections when a handler needs more than one facet of an event. Keep the native event type when the handler reads native fields such as `key`, modifier keys, `dataTransfer`, or focus relationships:\n\n```ts\nprivate _entityChanged(\n  ev: ValueChangedEvent<string> & HASSDomCurrentTargetEvent<HaEntityPicker>\n): void {\n  ev.currentTarget.value = ev.detail.value;\n}\n\nprivate _keyDown(\n  ev: KeyboardEvent & HASSDomCurrentTargetEvent<HTMLInputElement>\n): void {\n  if (ev.key === \"Enter\") {\n    this._submit(ev.currentTarget.value);\n  }\n}\n```\n\nDispatch Home Assistant component events with `fireEvent()` instead of constructing `Event` or `CustomEvent` directly. Register the event name and detail type by augmenting `HASSDomEvents`; use `undefined` when an event has no detail. `fireEvent()` constrains event names and supplied detail, and events bubble and cross shadow boundaries by default:\n\n```ts\nfireEvent(this, \"item-selected\", { id: item.id });\nfireEvent(this, \"refresh-requested\");\n```\n\nWhen an event is already registered, derive handler and listener types from its registration rather than repeating the payload shape:\n\n```ts\nprivate _itemSelected(\n  ev: HASSDomEvent<HASSDomEvents[\"item-selected\"]>\n): void {\n  this._selectedId = ev.detail.id;\n}\n```\n\n`HASSDomEvents` types `fireEvent()` calls. Augment `HTMLElementEventMap` for typed listeners on HTML elements, or `GlobalEventHandlersEventMap` when the event is handled on global event targets.\n\nIn component files, prefer placing global event declarations after the class at the bottom of the file. Preserve the existing placement when editing established files; foundational type, helper, and mixin files commonly keep declarations near the top before their consumers.\n\n```ts\ndeclare global {\n  interface HASSDomEvents {\n    \"item-selected\": { id: string };\n    \"refresh-requested\": undefined;\n  }\n\n  interface HTMLElementEventMap {\n    \"item-selected\": HASSDomEvent<HASSDomEvents[\"item-selected\"]>;\n  }\n}\n```\n",".agents/skills/ha-frontend-demo/SKILL.md":"---\nname: ha-frontend-demo\ndescription: Home Assistant standalone demo structure, configurations, URL navigation, mocked APIs, shared demo stubs used by gallery, and verification. Use when changing files under demo/, changing gallery consumers of demo/src/stubs/, or running and validating the demo.\n---\n\n# HA Frontend Demo\n\nUse this skill for standalone demo work and changes to shared demo stubs consumed by the gallery. Follow the persistent repository guidance in `AGENTS.md` and load matching specialist skills alongside this skill, especially `ha-frontend-testing` when running or validating the demo.\n\n## Purpose\n\nThe demo is the full Home Assistant frontend running against a mocked backend and is published at <https://demo.home-assistant.io>. It needs no Home Assistant server, which makes it the easiest way to load the real UI in a browser, for example to take screenshots.\n\n## Running The Demo\n\nRun commands from the repository root:\n\n```bash\npnpm dev:demo               # Development server on http://localhost:8090\npnpm dev:demo --background  # Detached; also supports --status/--stop/--logs\n```\n\nUse the E2E workflows documented in `ha-frontend-testing` when validating the demo.\n\n## Opening A Specific Demo\n\nThe demo contains multiple demo configurations. Select one directly with the `demo` query parameter, for example `http://localhost:8090/?demo=<slug>`. The valid slugs are defined in `demoConfigs` in `demo/src/configs/demo-configs.ts`, so \"the second demo\" means the slug of the second entry in that list. An unknown slug falls back to the default, which is the first entry.\n\n## Opening A Specific Page\n\nThe demo build uses hash-based routing: the frontend path goes in the URL hash, and the `demo` query parameter goes before the `#`. The URL format is:\n\n```text\nhttp://localhost:8090/?demo=<slug>#/<path>\n```\n\nUseful paths:\n\n- `/lovelace/0`: The selected demo's dashboard, also the default when no hash is given.\n- `/energy`: The energy dashboard. Its tabs are views: `/energy/overview` (Summary), `/energy/electricity`, `/energy/gas`, `/energy/water`, and `/energy/now`.\n- `/map`, `/history`, `/todo`, `/config`: Other sidebar panels.\n\nFor example, the water tab of the energy dashboard of the second demo is:\n\n```text\nhttp://localhost:8090/?demo=<second slug>#/energy/water\n```\n\n## Structure\n\n- `demo/src/ha-demo.ts`: Root element that sets up all backend mocks.\n- `demo/src/configs/<slug>/`: One directory per demo configuration, containing entities, dashboard, and theme data.\n- `demo/src/configs/demo-configs.ts`: Registry of demo configurations and URL slug handling.\n- `demo/src/stubs/`: Mocked WebSocket and REST APIs.\n- `demo/script/develop_demo`, `demo/script/build_demo`: Development server and static build wrappers.\n\n## Core Data\n\nThe service descriptions in `src/fake_data/demo_services.ts` and the trigger and condition descriptions and icons in `demo/src/stubs/automation_platforms_*.ts` are generated from Home Assistant Core. Do not edit them by hand. Run `script/gen_demo_core_data` to regenerate them from the core `dev` branch, and add a domain to the lists in `build-scripts/gulp/gen-demo-core-data.js` when the demo needs its services, triggers, or conditions. Their translations come from the backend translations merged into the demo build.\n\n## Shared Gallery Stubs\n\n`demo/src/stubs/` is shared, not demo-private: gallery pages import from it directly. Before changing or removing anything a stub does, search for its callers across `demo/src/` and `gallery/src/`, then check the affected gallery pages as well as the demo.\n\nThe two consumers use a stub differently, so demo behavior does not predict gallery behavior. A gallery page calls stubs against the `hass` from `provideHass`, where `hass.config` is the shared `demoConfig` object. A stub that mutates `hass.config` in place is therefore visible to the page. `demo/src/ha-demo.ts` copies `components` into a new array before the stubs run, so the same mutation never reaches the demo. A change can look correct in the demo while quietly breaking a gallery page.\n\n## Verification\n\nLoad `ha-frontend-testing` and use its demo and gallery workflows. Validate both consumers when changing shared stubs. Documentation-only changes do not require code tests unless examples or commands change.\n",".agents/skills/ha-frontend-contexts/SKILL.md":"---\nname: ha-frontend-contexts\ndescription: Home Assistant frontend Lit context and hass migration guidance. Use when adding or changing component state access, replacing hass reads, consuming entity or registry contexts, or reviewing rerender behavior.\n---\n\n# HA Frontend Contexts\n\nUse this skill when a component reads Home Assistant state, registries, localization, services, config, UI data, connection state, or API helpers.\n\n## Goal\n\nMove leaf components away from the broad `hass: HomeAssistant` object. Broad `hass` access rerenders components for unrelated changes, hides the data a component depends on, and makes tests harder to mock.\n\nContainer components may keep `hass` when they own it and feed providers. Leaf components should consume the narrowest context that covers their reads.\n\n## Core Files\n\n- Context definitions: `src/data/context/index.ts`\n- `consume` decorator, `ContextSubscriptionController`, and `consumeContext` / `ContextController` for reactive controllers: `src/common/decorators/consume.ts` (use instead of `@lit/context`'s `consume` and `ContextConsumer`, enforced by ESLint)\n- Entity-scoped consume helpers: `src/common/decorators/consume-context-entry.ts`\n- Transform decorator: `src/common/decorators/transform.ts`\n- Canonical migration example: `src/panels/lovelace/cards/hui-button-card.ts`\n- Providers are wired by `contextMixin` on `HassBaseEl`; consumers do not wire providers manually.\n\n## Context Selection\n\n| Context                                                              | Replaces                                                                                                  |\n| -------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------- |\n| `statesContext`                                                      | `hass.states`                                                                                             |\n| `entitiesContext`, `devicesContext`, `areasContext`, `floorsContext` | `hass.entities`, `hass.devices`, `hass.areas`, `hass.floors`                                              |\n| `registriesContext`                                                  | all four registries together                                                                              |\n| `servicesContext`                                                    | `hass.services`                                                                                           |\n| `internationalizationContext`                                        | `hass.localize`, `hass.locale`, `hass.language`                                                           |\n| `formattersContext`                                                  | entity and attribute formatters                                                                           |\n| `configContext`                                                      | `hass.config`, `hass.user`, `hass.auth`, `hass.userData`                                                  |\n| `connectionContext`                                                  | `hass.connection`, `hass.connected`, `hass.debugConnection`, `hass.hassUrl`                               |\n| `apiContext`                                                         | `hass.callService`, `hass.callApi`, `hass.callApiRaw`, `hass.callWS`, `hass.sendWS`, `hass.fetchWithAuth` |\n| `uiContext`                                                          | themes, selected theme, panels, sidebar, and UI state                                                     |\n| `narrowViewportContext`                                              | narrow-layout boolean                                                                                     |\n| `reducedMotionContext`                                               | `prefers-reduced-motion` media query                                                                      |\n\nLazy contexts subscribe on first consumer and tear down after the last consumer: `labelsContext`, `fullEntitiesContext`, `configEntriesContext`, `manifestsContext`, `triggerDescriptionsContext`, and `conditionDescriptionsContext`.\n\nThe single-field contexts such as `localizeContext`, `themesContext`, and `userContext` are deprecated. Use grouped contexts instead.\n\n## Consumption Patterns\n\nImport `consume` from `src/common/decorators/consume`, not from `@lit/context`. The `@lit/context` version forces a host update on every context change, so every consumed field rerenders the component, even without `@state()`, and `@transform` cannot skip anything. Our `consume` leaves update scheduling to the field:\n\n- `@state()` plus `@consume`: rerenders when the context value changes.\n- `@state()` plus `@consume` plus `@transform`: rerenders only when the transformed value changes.\n- `@consume` alone: the field stays current but never triggers a render. Use this for values only read in event handlers or callbacks, such as `apiContext`, or for data read on demand, such as picker item callbacks.\n\nIn reactive controllers, extend `ContextController` and decorate fields with `consumeContext({ context, subscribe, transform })` from the same file. `transform` stores one part of the context value, and `contextUpdated()` runs only when that part changes. `@state()` and `@transform` do not work on controller fields; call `host.requestUpdate()` when the host needs to rerender. Example: `src/data/service-info-controller.ts`.\n\nFor lazily created subscriptions, use `ContextSubscriptionController` from the same file instead of `@lit/context`'s `ContextConsumer`, which also forces a host update. Its callback must store the value in a reactive field or call `host.requestUpdate()` itself when the host needs to rerender.\n\nUse entity-scoped helpers when the component watches an entity ID held on the host:\n\n```ts\n@state() @consumeEntityState({ entityIdPath: [\"_config\", \"entity\"] })\nprivate _stateObj?: HassEntity;\n\n@state() @consumeEntityRegistryEntry({ entityIdPath: [\"_config\", \"entity\"] })\nprivate _entity?: EntityRegistryDisplayEntry;\n\n@state() @consumeLocalize()\nprivate _localize!: LocalizeFunc;\n```\n\nUse `consumeEntityStates` when the host property contains one or more entity IDs. It filters missing entities and preserves the previous record when none of the selected entities changed.\n\n```ts\n@state() @consumeEntityStates({ entityIdPath: [\"_config\", \"entities\"] })\nprivate _stateObjs?: Record<string, HassEntity>;\n```\n\nFor a single field from a grouped context, pair `@consume` with `@transform`:\n\n```ts\n@state()\n@consume({ context: uiContext, subscribe: true })\n@transform<HomeAssistantUI, Themes>({ transformer: ({ themes }) => themes })\nprivate _themes!: Themes;\n```\n\nUse `@transform` with `watch` when the transformer depends on a host property, such as a computed entity ID. `consumeEntityState` and `consumeEntityStates` only watch the first path segment.\n\nTo consume a whole group untransformed, omit `@transform` and type the field as `ContextType<typeof statesContext>` or the matching context type.\n\n## Review Checklist\n\n- The component consumes the narrowest context needed for the data it reads.\n- A broad `hass` property is kept only when the component is a container or external API requires it.\n- Entity-scoped reads use the consume helpers rather than ad hoc context transforms.\n- Context fields read during render or `willUpdate` are marked `@state()` so updates trigger rendering. Fields only read outside the render cycle are left without `@state()`.\n- Tests and mocks only provide the data the component actually consumes.\n",".agents/skills/ha-frontend-components/SKILL.md":"---\nname: ha-frontend-components\ndescription: Home Assistant frontend component patterns. Use when implementing or reviewing dialogs, ha-form, ha-alert, keyboard shortcuts, tooltips, panels, Lovelace cards, or ha-button usage.\n---\n\n# HA Frontend Components\n\nUse this skill when creating or reviewing Home Assistant UI components and common interaction patterns.\n\nCross-load `ha-frontend-events` when component work includes event listener typing, custom event dispatch, or event-map declarations.\n\n## Dialogs\n\nOpen dialogs through the fire-event pattern:\n\n```ts\nfireEvent(this, \"show-dialog\", {\n  dialogTag: \"dialog-example\",\n  dialogImport: () => import(\"./dialog-example\"),\n  dialogParams: { title: \"Example\", data: someData },\n});\n```\n\nDialog implementation requirements:\n\n- Use `ha-dialog`.\n- Use `DialogMixin`, which implements `HassDialogNext<T>`, for new dialogs. See `src/dialogs/dialog-mixin.ts`.\n- Read dialog parameters from the mixin's `params` property and render the dialog open. Return `nothing` while required parameters are absent.\n- Call the mixin's `closeDialog()` to close a new dialog. The mixin handles the `closed` event, fires `dialog-closed`, and removes the host element.\n- Existing dialogs may implement the legacy `HassDialog<T>` interface from `src/dialogs/make-dialog-manager.ts`.\n- Preserve the existing `showDialog()`, open-state, and close-event lifecycle when maintaining a legacy dialog; do not copy that lifecycle into a `DialogMixin` dialog.\n- Use `header-title` and `header-subtitle` for simple header text.\n- Use slots when standard header attributes are not enough.\n- Use `ha-dialog-footer` with `primaryAction` and `secondaryAction` slots.\n- Add `autofocus` to the first focusable element, such as `<ha-form autofocus>`, and forward it internally if needed.\n\nUse standard dialog widths: `small`, `medium`, `large`, or `full`. Avoid custom dialog sizing unless there is a clear product need.\n\n## Buttons\n\n`ha-button` wraps the Web Awesome button in `src/components/ha-button.ts`.\n\nAxes:\n\n- `variant`: `brand`, `neutral`, `danger`, `warning`, `success`.\n- `appearance`: `accent`, `filled`, `outlined`, `plain`.\n- `size`: `xs`, `s`, `m`, `l`, `xl`.\n\nCommon usage:\n\n- Use `appearance=\"filled\"` for primary emphasis when needed.\n- Use `appearance=\"plain\"` for cancel and dismiss actions.\n- Use `variant=\"danger\"` for destructive actions.\n- Place primary actions in `slot=\"primaryAction\"` and secondary actions in `slot=\"secondaryAction\"`.\n\n## Forms\n\n`ha-form` is schema-driven with `HaFormSchema[]` and supports common selectors for entities, devices, areas, targets, numbers, booleans, time, actions, text, objects, selects, icons, media, and location.\n\nUse `computeLabel`, `computeError`, and `computeHelper` for translated labels, validation, and helper text.\n\n```ts\n<ha-form\n  .hass=${this.hass}\n  .data=${this._data}\n  .schema=${this._schema}\n  .error=${this._errors}\n  .computeLabel=${(schema) => this._localize(`ui.panel.${schema.name}`)}\n  @value-changed=${this._valueChanged}\n></ha-form>\n```\n\n## Alerts\n\nUse `ha-alert` for user-visible status messaging.\n\n- Alert types: `error`, `warning`, `info`, `success`.\n- Useful properties: `title`, `alert-type`, `dismissable`, `narrow`.\n- Slots: `icon` for custom leading icon, `action` for custom action content.\n- Content is announced by screen readers when dynamically displayed.\n\n```ts\nhtml`\n  <ha-alert alert-type=\"error\">${this._localize(\"ui.example.error\")}</ha-alert>\n  <ha-alert alert-type=\"warning\" .title=${this._localize(\"ui.example.warning\")}>\n    ${this._localize(\"ui.example.description\")}\n  </ha-alert>\n  <ha-alert alert-type=\"success\" dismissable>\n    ${this._localize(\"ui.example.success\")}\n  </ha-alert>\n`;\n```\n\n## Shortcuts And Tooltips\n\nUse `ShortcutManager` from `src/common/keyboard/shortcuts.ts` for keyboard shortcuts. It blocks shortcuts in input fields, can prevent shortcuts during text selection, and supports character and KeyCode shortcuts for non-latin keyboards. See `src/state/quick-bar-mixin.ts` for global shortcut examples.\n\nUse `ha-tooltip` from `src/components/ha-tooltip.ts` for contextual hover help. See `src/components/ha-label.ts` for an example.\n\n## Panels And Lovelace Cards\n\nPanels commonly extend `SubscribeMixin(LitElement)` and receive route and narrow-layout properties.\n\nLovelace cards should implement `LovelaceCard`, validate config in `setConfig()`, handle loading, error, unavailable, and missing-entity states, and add a configuration editor when needed.\n\nCards are user-story surfaces. Support different households, entity types, responsive layouts, and accessible interaction states.\n",".agents/skills/ha-frontend-gallery/SKILL.md":"---\nname: ha-frontend-gallery\ndescription: Home Assistant frontend gallery structure, pages, demos, content, and verification. Use when changing files under gallery/, including gallery markdown, TypeScript demos, sidebar entries, mock data, page generation, or gallery builds.\n---\n\n# HA Frontend Gallery\n\nUse this skill for all work under `gallery/`. Follow the persistent repository guidance in `AGENTS.md` and load the matching specialist skills alongside this gallery-specific guidance.\n\n## Quick Reference\n\nRun commands from the repository root unless noted otherwise:\n\n```bash\ngallery/script/develop_gallery  # Start the gallery development server\ngallery/script/build_gallery    # Build the static gallery\npnpm lint                       # ESLint, Prettier, TypeScript, and Lit checks\npnpm lint:types                 # TypeScript compiler, without file arguments\n```\n\nNever run `pnpm lint:types` or `tsc` with file arguments. File arguments make `tsc` ignore `tsconfig.json` and can emit `.js` files into `src/`.\n\n## Purpose\n\nThe gallery is a developer and designer reference for Home Assistant frontend UI patterns. It documents component APIs, shows realistic Lovelace and more-info states, captures brand and copy guidance, and provides reproducible demos that are safe to inspect outside a running Home Assistant instance.\n\n- Prefer demonstrating real production components from `src/` instead of creating gallery-only replacements.\n- Keep fake state, sample data, and demo-only helpers inside `gallery/`.\n- Do not move gallery stubs or demo data into production code unless a production feature explicitly needs them.\n- Do not hand-edit generated output under `gallery/build/` or `gallery/dist/`.\n\n## Structure\n\n- `gallery/sidebar.js`: Defines gallery sections, headers, and explicit page ordering.\n- `gallery/script/develop_gallery`: Wrapper for the `develop-gallery` gulp task.\n- `gallery/script/build_gallery`: Wrapper for the `build-gallery` gulp task.\n- `gallery/src/entrypoint.js`: Creates the `<ha-gallery>` shell.\n- `gallery/src/ha-gallery.ts`: Renders the drawer, page routing, markdown descriptions, demos, edit links, and RTL toggle.\n- `gallery/src/html/index.html.template`: HTML template used by the gallery build.\n- `gallery/src/pages/<category>/<page>.markdown`: Optional page description and frontmatter.\n- `gallery/src/pages/<category>/<page>.ts`: Optional live demo module for the same page ID.\n- `gallery/src/components/`: Gallery-only demo wrappers like `demo-card`, `demo-cards`, `demo-more-info`, and `page-description`.\n- `gallery/src/data/`: Fake `hass`, demo states, mock traces, and reusable sample data.\n- `gallery/public/`: Static assets copied into the gallery output.\n\n## Page Model\n\nGallery pages are generated by `gather-gallery-pages` in `build-scripts/gulp/gallery.js`.\n\n- A page ID is the path under `gallery/src/pages/` without the extension, like `components/ha-button`.\n- A `.markdown` file and a `.ts` file with the same page ID become one gallery page.\n- A page may have only markdown, only a TypeScript demo, or both.\n- Markdown can contain YAML frontmatter with `title` and optional `subtitle`.\n- Markdown that contains only frontmatter contributes metadata without rendering a description block.\n- TypeScript demo modules are dynamically imported for side effects when the page is opened.\n- A demo module must define a custom element named `demo-${category}-${page}` with slashes replaced by hyphens, like `demo-components-ha-button` for `components/ha-button`.\n- `gallery/src/ha-gallery.ts` renders that element with `dynamicElement()` based on the current page ID.\n\n## Sidebar\n\nUse `gallery/sidebar.js` when a page needs a visible section, section header, or deterministic ordering.\n\n- `category` must match the first directory name under `gallery/src/pages/`.\n- `header` is the section label shown in the drawer.\n- `pages` is optional. When present, listed pages keep that exact order.\n- Pages in a category that are not listed are appended alphabetically after the listed pages.\n- New categories without a sidebar entry are appended by the generator with their category name as the header.\n- If a listed page does not exist, the generator logs an error during `gather-gallery-pages`.\n\n### Subsections\n\nA section can group its pages under named subsections instead of one flat list. Use this for large categories where related pages should sit together.\n\n- `subsections` is an array of `{ header, pages }`. It is mutually exclusive with a flat `pages` array on the same group.\n- Each subsection `header` is a non-collapsible label rendered inside the section's expansion panel; the section stays the only collapsible level.\n- Listed pages keep their per-subsection order.\n- Any pages found in the category but not listed in a subsection are collected into a generated `Other` subsection, appended alphabetically. The `Other` subsection is omitted when there are no leftovers.\n- A listed page that does not exist still logs an error during `gather-gallery-pages`.\n- Use sentence case for subsection headers and follow the content standards below.\n\n## Markdown Pages\n\nUse markdown pages for explanations, design guidance, API notes, and copy standards.\n\n- Start with frontmatter when the page needs a title or subtitle.\n- Use sentence case for titles, headings, labels, and UI copy.\n- Put the live example before the reference API when that makes the page easier to scan.\n- Use fenced code blocks with a language tag for copyable examples.\n- Keep examples short and focused on the behavior being documented.\n- Prefer real component names and attributes over prose-only descriptions.\n- Use Home Assistant terminology from `ha-frontend-user-facing-text`.\n- For remove/delete and add/create wording, follow `gallery/src/pages/misc/remove-delete-add-create.markdown`.\n\nGallery markdown is documentation content and is not localized with `localize`. If demo code creates production UI strings, follow the localization and copy guidance in `ha-frontend-user-facing-text`.\n\n## Demo Components\n\nUse TypeScript demo pages for interactive or stateful examples.\n\n- Import production components from `src/` using the correct relative path from the demo file.\n- Import reusable gallery helpers from `gallery/src/components/` when they already model the pattern.\n- Use `demo-card` and `demo-cards` for Lovelace card examples that render YAML card configs.\n- Use `demo-more-info` and `demo-more-infos` for more-info dialog examples.\n- Use shared mock data from `gallery/src/data/` instead of repeating large fake state objects inline.\n- Show meaningful states, such as loading, unavailable, empty, error, active, inactive, and disabled when relevant.\n- Check responsive behavior and the gallery RTL toggle when layout or direction-sensitive UI changes.\n- Keep unavoidable casts or loose demo parsing local to the demo helper or demo page.\n\nThe gallery ESLint config allows `console` for gallery diagnostics. Do not copy that exception into production frontend code.\n\n## Content Standards\n\nFollow the detailed copy standards in `ha-frontend-user-facing-text`: use American English, sentence case, active voice, inclusive language, direct user-focused wording, and consistent Home Assistant terminology.\n\n- Use `Home Assistant` in full, not `HA` or `HASS`.\n- Use `integration` instead of `component` for product concepts.\n- Use `Remove` for reversible disassociation and `Delete` for permanent deletion.\n- Use `Add` for existing items and `Create` for something made from scratch.\n- Avoid Latin abbreviations like `e.g.` and `i.e.` in prose.\n- Avoid stitching sentence fragments together in production UI examples.\n\n## Verification\n\n- For markdown, sidebar, and page-generation changes, run `gallery/script/build_gallery`.\n- For TypeScript demo or gallery shell changes, run the smallest relevant check plus `pnpm lint` when practical.\n- For type checking, run `pnpm lint:types` without file arguments.\n- For visual changes, run `gallery/script/develop_gallery` and check the affected page on desktop, narrow viewport, and RTL when relevant.\n- If verification is skipped, state which command was skipped and why.\n",".agents/skills/ha-frontend-review/SKILL.md":"---\nname: ha-frontend-review\ndescription: Home Assistant frontend PR and review guidance, including implementation quality, existing review feedback, established design patterns, and the authority of UI/UX evidence. Use when reviewing frontend changes, preparing a PR, checking recurring review issues, or applying the PR template.\n---\n\n# HA Frontend Review\n\nUse this skill when reviewing Home Assistant frontend changes or preparing a pull request.\n\n## Review Preparation\n\n1. Establish the review scope from the diff and the behavior changed by it.\n2. Before identifying findings, use the [project skill catalogue](../../../AGENTS.md#project-skills) to load every matching companion's `SKILL.md`. Follow applicable companion references in those skills as well; this review skill supplies the workflow, and companions supply the domain-specific criteria.\n3. Apply all loaded guidance within that scope. Report only problems introduced or worsened by the changeset, anchored to changed lines. Read surrounding code to understand affected behavior, not to generate unrelated cleanup or migration requests.\n\n## Pull Request Body\n\nWhen creating a pull request, use `.github/PULL_REQUEST_TEMPLATE.md` as the body.\n\n- Do not omit, reorder, or rewrite template sections.\n- Check the appropriate \"Type of change\" box based on the actual change.\n- Do not check checklist items on behalf of the user.\n- If the PR includes UI changes, remind the user to add screenshots or a short video.\n- Explain what the change does for users, not only implementation details.\n- Use Markdown.\n\n## Pre-Submission Checklist\n\n- `pnpm lint` passes when practical for the scope.\n- `pnpm test` or focused relevant tests are green when practical for the scope.\n- Each test added by the change protects real logic, not the look of a component.\n- User-facing text is localized and follows `ha-frontend-user-facing-text` guidance.\n- Components handle loading, error, unavailable, and missing-entity states.\n- Entity existence is checked before property access.\n- Event listeners and subscriptions are cleaned up.\n- UI is accessible to screen readers and keyboard users.\n\n## Recurring Review Issues\n\nScope and public surface:\n\n- Keep changes independently reviewable and limited to the requested area.\n- Prefer existing Home Assistant helpers, Lit primitives, and component seams over parallel implementations.\n- Challenge new public properties and optional feature surface when transient options or existing seams meet the requirement with less lifecycle and consistency cost.\n\nStateful and asynchronous UI:\n\n- Review transitions in both directions, not only individual rendered states.\n- When controls reappear, restore valid defaults instead of retaining state that was only valid while they were hidden.\n- Establish immutable dirty-state baselines before asynchronous work, guard against stale responses, and preserve unsaved state in mounted editors.\n- Determine an action's current meaning before applying dirty-state checks, especially when an action can change between Save and Close.\n\nReadiness and invalidation:\n\n- Treat readiness as the first displayable terminal result, including stable empty and error states.\n- Register child readiness before resolving the parent, do not treat fallback work as terminal, and replay readiness correctly for cached or reused panels.\n- Ensure every value read by memoized output participates in its invalidation.\n\nRepository-owned contracts:\n\n- Consult the public [frontend contributor documentation](https://contributors.home-assistant.io/frontend/) for documented architecture, data flow, design, and development workflows.\n- For new leaf components, load `ha-frontend-contexts` and verify they consume narrow contexts instead of introducing a broad `hass` property; containers and external APIs may still require `hass`.\n- Verify backend assumptions against the owning Core, Supervisor, or WebSocket implementation, and component assumptions against the exported component contract.\n- Prefer canonical repository helpers and test setup over duplicate local implementations.\n- Promote AI-review concerns into durable guidance only when supported by code evidence, reproduced behavior, an accepted corrective commit, or human-maintainer validation.\n\nUser experience and accessibility:\n\n- Forms need proper labels, helper text, and validation feedback.\n- Form markup should not cause password managers to identify fields incorrectly.\n- Clickable areas should be large enough for touch interaction.\n- Hover, active, disabled, loading, and focus states should be clear.\n\nDialog and modal patterns:\n\n- Multi-step operations should show progress.\n- Dialog state should survive background operations correctly.\n- Cancel and close buttons should behave consistently.\n- Defaults should be helpful without blocking user override.\n\nComponent design patterns:\n\n- Terminology should be consistent. Use words like \"Join\" or \"Apply\" instead of \"Group\" when that better matches the user action.\n- Visual hierarchy should use appropriate font sizes, weights, and spacing ratios.\n- Components should align to the design grid.\n- Badges and indicators should be placed consistently.\n\nCode quality:\n\n- Null and undefined paths should be handled explicitly.\n- Potentially undefined array and object access should be guarded.\n- Event handlers, timers, observers, and subscriptions should be cleaned up.\n\nConfiguration and props:\n\n- Make configuration fields optional when sensible.\n- Provide reasonable defaults.\n- Keep APIs extensible without adding speculative abstractions.\n- Validate configuration before applying changes.\n\n## UI/UX Evidence\n\nFor user-facing changes, establish the existing design context as part of frontend review:\n\n- Treat an applicable gallery specification as the authoritative repository source for the documented component or interaction. Inspect its written guidance and demos when present.\n- Note relevant production designs, approved designs, and shared `ha-*` components so the implementation can also be compared with established frontend behaviour.\n- Require the applicable gallery documentation or demo to change when the implementation intentionally changes behaviour documented there.\n- Prefer an established input layout or appropriate shared component over a raw input or ad hoc control.\n- Treat explicit UI/UX approval, current repository guidance, and direct human or workflow instructions as stronger evidence than inference.\n- Treat linked tasks as evidence of the problem and stated requirements, not automatic UI/UX approval.\n- When the direction remains uncertain, search frontend pull requests that carry or previously carried **Needs UX**. Inspect their label history, comments, and reviews rather than relying on the current label alone.\n- Judge relevant feedback by its content and surrounding discussion. When GitHub provides `author_association`, prioritise feedback marked `MEMBER`.\n- Prefer recent feedback about similar interactions or components. Label removal can show that a workflow gate moved on, but does not prove approval without the surrounding discussion.\n- Summarise the applicable guidance instead of maintaining a reviewer list or treating one historical decision as a permanent rule.\n\n## Review Flow\n\n- Before reviewing a pull request, read its existing comments, reviews, and threads, including their status, resolver, and Copilot resolution reason when available.\n- Prioritise substantive human feedback, especially from authors marked `MEMBER`, and validate agent-generated feedback against the code and repository guidance.\n- Do not duplicate unresolved findings as new inline comments; reference any that still need action in the review summary. Treat resolved feedback as closed only when the resolution reason or surrounding discussion supports that outcome; otherwise validate it against the current code before suppressing it. Respect **Won't fix** and **Incorrect** reasons.\n- When a later commit addresses an unresolved thread, note in the review summary that it looks addressed, linking the thread and the commit, so it can be resolved. Do not report it as still open.\n- Identify behavioral regressions, bugs, accessibility issues, and missing tests that `ha-frontend-testing` calls for first.\n- Do not ask for new tests on visual components. If the visuals clearly changed and the PR has no screenshots or videos, suggest adding them instead.\n- When the change adds something repository guidance rules out, such as a rendering test, open the comment with the removal (\"Remove this test file\") and cite the guidance in one sentence. Do not critique how the unwanted code is written, as that reads as a request to fix it.\n- Record the applicable UI/UX evidence for user-facing changes, whether or not further input is needed.\n- Keep style-only comments secondary unless they affect maintainability or user experience.\n- Prefer small, direct fixes over large refactors during review follow-up.\n","AGENTS.md":"# Home Assistant Frontend Agent Guide\n\nYou are helping develop the Home Assistant frontend. This repository is a TypeScript application built from Lit-based Web Components for the Home Assistant web UI.\n\n## Essential Commands\n\n```bash\npnpm lint          # ESLint + Prettier + TypeScript + Lit\npnpm format        # Auto-fix ESLint + Prettier\npnpm lint:types    # TypeScript compiler, run without file arguments\npnpm test          # Vitest\npnpm dev           # App dev server, supports --background/--status/--stop/--logs\npnpm dev:serve     # Local serving dev server, supports -c core URL, -p port, and dev flags\n```\n\nNever run `tsc` or `pnpm lint:types` with file arguments. When `tsc` receives file arguments, it ignores `tsconfig.json` and can emit `.js` files into `src/`. Always run `pnpm lint:types` without arguments. For individual file type checking, rely on editor diagnostics.\n\n## Architecture\n\n- The frontend uses custom elements built with Lit and TypeScript strict mode.\n- Components communicate with the backend through the Home Assistant WebSocket API.\n- Use `ha-` for Home Assistant components, `hui-` for Lovelace UI components, and `dialog-` for dialogs.\n- Prefer `ha-*` components and current Web Awesome wrappers. Avoid adding new legacy `mwc-*` usage.\n- Leaf components should consume narrow Lit contexts instead of taking the broad `hass` object unless they are containers that own and provide `hass`.\n\n## Development Standards\n\n- Use strict TypeScript, proper interfaces, and `import type` for type-only imports.\n- Avoid `any`; model data with existing Home Assistant types or narrow new types.\n- Keep imports organized and remove unused imports.\n- Do not use `console`; use existing logging or user-visible error patterns.\n- Use `@state()` for internal Lit state and `@property()` for public API.\n- Do not query or manipulate DOM manually when Lit decorators, component refs, or render state are appropriate.\n- Scope styles to components, use theme custom properties, and keep layouts mobile-first and RTL-safe.\n- All user-facing text must be localized through the translation system.\n- Do not write tests just because you changed some code. Write a test when there is real logic that could break without anyone noticing, and explain what the test protects.\n\n## Project Skills\n\nDetailed guidance lives in `.agents/skills/<name>/SKILL.md`. Load every matching skill before detailed implementation or review. For reviews, load `ha-frontend-review` alongside all companions that apply to the changed code or behavior:\n\n- `ha-frontend-contexts`: Lit contexts, `hass` migration, and rerender-sensitive state access.\n- `ha-frontend-components`: dialogs, forms, alerts, shortcuts, tooltips, panels, and Lovelace cards.\n- `ha-frontend-events`: event handler typing, custom event dispatch, and event-map declarations.\n- `ha-frontend-types`: backend data contracts, optional schemas, shared types, assertions, and lifecycle types.\n- `ha-frontend-lit`: reactive fields, DOM queries, lifecycle behavior, and render-derived state.\n- `ha-frontend-styling`: theme variables, spacing tokens, responsive layout, RTL, and view transitions.\n- `ha-frontend-testing`: lint, typecheck, Vitest, Playwright e2e dev servers, and benchmarks.\n- `ha-frontend-query-params`: typed URL query params, `QueryParamsController`, data table URL sync, in-app links, and My Home Assistant redirect params.\n- `ha-frontend-user-facing-text`: localization, terminology, sentence case, and Home Assistant text style.\n- `ha-frontend-review`: PR template use, existing review feedback, review checklist, recurring issues, and UI/UX evidence including gallery specifications.\n- `ha-frontend-ux-readiness`: UI/UX routing that complements frontend review for new user experiences.\n- `ha-frontend-gallery`: gallery pages, demos, sidebar structure, content, and verification.\n- `ha-frontend-demo`: standalone demo structure, configurations, navigation, shared stubs, and verification.\n\n## Pull Requests\n\nWhen creating a pull request, use `.github/PULL_REQUEST_TEMPLATE.md` as the PR body. Preserve template sections, check only the appropriate type-of-change boxes, and do not check checklist items on behalf of the user. If the PR includes UI changes, remind the user to add screenshots or a short video.\n\n## AI policy\n\nThis project follows the [Open Home Foundation AI Policy](AI_POLICY.md).\nAutonomous contributions are not accepted: a human must review, understand,\nand be able to explain every change before it is submitted. Do not open\nissues or pull requests autonomously, and do not post comments on behalf of\na user without their review.\n"},"items":[{"name":"AGENTS.md","path":"AGENTS.md","rawUrl":"https://raw.githubusercontent.com/home-assistant/frontend/HEAD/AGENTS.md","title":"AI Agent Protocol & Instructions","category":"root-instruction","format":"markdown","content":"# Home Assistant Frontend Agent Guide\n\nYou are helping develop the Home Assistant frontend. This repository is a TypeScript application built from Lit-based Web Components for the Home Assistant web UI.\n\n## Essential Commands\n\n```bash\npnpm lint          # ESLint + Prettier + TypeScript + Lit\npnpm format        # Auto-fix ESLint + Prettier\npnpm lint:types    # TypeScript compiler, run without file arguments\npnpm test          # Vitest\npnpm dev           # App dev server, supports --background/--status/--stop/--logs\npnpm dev:serve     # Local serving dev server, supports -c core URL, -p port, and dev flags\n```\n\nNever run `tsc` or `pnpm lint:types` with file arguments. When `tsc` receives file arguments, it ignores `tsconfig.json` and can emit `.js` files into `src/`. Always run `pnpm lint:types` without arguments. For individual file type checking, rely on editor diagnostics.\n\n## Architecture\n\n- The frontend uses custom elements built with Lit and TypeScript strict mode.\n- Components communicate with the backend through the Home Assistant WebSocket API.\n- Use `ha-` for Home Assistant components, `hui-` for Lovelace UI components, and `dialog-` for dialogs.\n- Prefer `ha-*` components and current Web Awesome wrappers. Avoid adding new legacy `mwc-*` usage.\n- Leaf components should consume narrow Lit contexts instead of taking the broad `hass` object unless they are containers that own and provide `hass`.\n\n## Development Standards\n\n- Use strict TypeScript, proper interfaces, and `import type` for type-only imports.\n- Avoid `any`; model data with existing Home Assistant types or narrow new types.\n- Keep imports organized and remove unused imports.\n- Do not use `console`; use existing logging or user-visible error patterns.\n- Use `@state()` for internal Lit state and `@property()` for public API.\n- Do not query or manipulate DOM manually when Lit decorators, component refs, or render state are appropriate.\n- Scope styles to components, use theme custom properties, and keep layouts mobile-first and RTL-safe.\n- All user-facing text must be localized through the translation system.\n- Do not write tests just because you changed some code. Write a test when there is real logic that could break without anyone noticing, and explain what the test protects.\n\n## Project Skills\n\nDetailed guidance lives in `.agents/skills/<name>/SKILL.md`. Load every matching skill before detailed implementation or review. For reviews, load `ha-frontend-review` alongside all companions that apply to the changed code or behavior:\n\n- `ha-frontend-contexts`: Lit contexts, `hass` migration, and rerender-sensitive state access.\n- `ha-frontend-components`: dialogs, forms, alerts, shortcuts, tooltips, panels, and Lovelace cards.\n- `ha-frontend-events`: event handler typing, custom event dispatch, and event-map declarations.\n- `ha-frontend-types`: backend data contracts, optional schemas, shared types, assertions, and lifecycle types.\n- `ha-frontend-lit`: reactive fields, DOM queries, lifecycle behavior, and render-derived state.\n- `ha-frontend-styling`: theme variables, spacing tokens, responsive layout, RTL, and view transitions.\n- `ha-frontend-testing`: lint, typecheck, Vitest, Playwright e2e dev servers, and benchmarks.\n- `ha-frontend-query-params`: typed URL query params, `QueryParamsController`, data table URL sync, in-app links, and My Home Assistant redirect params.\n- `ha-frontend-user-facing-text`: localization, terminology, sentence case, and Home Assistant text style.\n- `ha-frontend-review`: PR template use, existing review feedback, review checklist, recurring issues, and UI/UX evidence including gallery specifications.\n- `ha-frontend-ux-readiness`: UI/UX routing that complements frontend review for new user experiences.\n- `ha-frontend-gallery`: gallery pages, demos, sidebar structure, content, and verification.\n- `ha-frontend-demo`: standalone demo structure, configurations, navigation, shared stubs, and verification.\n\n## Pull Requests\n\nWhen creating a pull request, use `.github/PULL_REQUEST_TEMPLATE.md` as the PR body. Preserve template sections, check only the appropriate type-of-change boxes, and do not check checklist items on behalf of the user. If the PR includes UI changes, remind the user to add screenshots or a short video.\n\n## AI policy\n\nThis project follows the [Open Home Foundation AI Policy](AI_POLICY.md).\nAutonomous contributions are not accepted: a human must review, understand,\nand be able to explain every change before it is submitted. Do not open\nissues or pull requests autonomously, and do not post comments on behalf of\na user without their review.\n","isInternal":false,"tokens":1003,"sizeBytes":4623},{"name":"SKILL.md","path":".agents/skills/ha-frontend-components/SKILL.md","rawUrl":"https://raw.githubusercontent.com/home-assistant/frontend/HEAD/.agents/skills/ha-frontend-components/SKILL.md","title":"ha-frontend-components","category":"anthropic-skill","format":"markdown","content":"---\nname: ha-frontend-components\ndescription: Home Assistant frontend component patterns. Use when implementing or reviewing dialogs, ha-form, ha-alert, keyboard shortcuts, tooltips, panels, Lovelace cards, or ha-button usage.\n---\n\n# HA Frontend Components\n\nUse this skill when creating or reviewing Home Assistant UI components and common interaction patterns.\n\nCross-load `ha-frontend-events` when component work includes event listener typing, custom event dispatch, or event-map declarations.\n\n## Dialogs\n\nOpen dialogs through the fire-event pattern:\n\n```ts\nfireEvent(this, \"show-dialog\", {\n  dialogTag: \"dialog-example\",\n  dialogImport: () => import(\"./dialog-example\"),\n  dialogParams: { title: \"Example\", data: someData },\n});\n```\n\nDialog implementation requirements:\n\n- Use `ha-dialog`.\n- Use `DialogMixin`, which implements `HassDialogNext<T>`, for new dialogs. See `src/dialogs/dialog-mixin.ts`.\n- Read dialog parameters from the mixin's `params` property and render the dialog open. Return `nothing` while required parameters are absent.\n- Call the mixin's `closeDialog()` to close a new dialog. The mixin handles the `closed` event, fires `dialog-closed`, and removes the host element.\n- Existing dialogs may implement the legacy `HassDialog<T>` interface from `src/dialogs/make-dialog-manager.ts`.\n- Preserve the existing `showDialog()`, open-state, and close-event lifecycle when maintaining a legacy dialog; do not copy that lifecycle into a `DialogMixin` dialog.\n- Use `header-title` and `header-subtitle` for simple header text.\n- Use slots when standard header attributes are not enough.\n- Use `ha-dialog-footer` with `primaryAction` and `secondaryAction` slots.\n- Add `autofocus` to the first focusable element, such as `<ha-form autofocus>`, and forward it internally if needed.\n\nUse standard dialog widths: `small`, `medium`, `large`, or `full`. Avoid custom dialog sizing unless there is a clear product need.\n\n## Buttons\n\n`ha-button` wraps the Web Awesome button in `src/components/ha-button.ts`.\n\nAxes:\n\n- `variant`: `brand`, `neutral`, `danger`, `warning`, `success`.\n- `appearance`: `accent`, `filled`, `outlined`, `plain`.\n- `size`: `xs`, `s`, `m`, `l`, `xl`.\n\nCommon usage:\n\n- Use `appearance=\"filled\"` for primary emphasis when needed.\n- Use `appearance=\"plain\"` for cancel and dismiss actions.\n- Use `variant=\"danger\"` for destructive actions.\n- Place primary actions in `slot=\"primaryAction\"` and secondary actions in `slot=\"secondaryAction\"`.\n\n## Forms\n\n`ha-form` is schema-driven with `HaFormSchema[]` and supports common selectors for entities, devices, areas, targets, numbers, booleans, time, actions, text, objects, selects, icons, media, and location.\n\nUse `computeLabel`, `computeError`, and `computeHelper` for translated labels, validation, and helper text.\n\n```ts\n<ha-form\n  .hass=${this.hass}\n  .data=${this._data}\n  .schema=${this._schema}\n  .error=${this._errors}\n  .computeLabel=${(schema) => this._localize(`ui.panel.${schema.name}`)}\n  @value-changed=${this._valueChanged}\n></ha-form>\n```\n\n## Alerts\n\nUse `ha-alert` for user-visible status messaging.\n\n- Alert types: `error`, `warning`, `info`, `success`.\n- Useful properties: `title`, `alert-type`, `dismissable`, `narrow`.\n- Slots: `icon` for custom leading icon, `action` for custom action content.\n- Content is announced by screen readers when dynamically displayed.\n\n```ts\nhtml`\n  <ha-alert alert-type=\"error\">${this._localize(\"ui.example.error\")}</ha-alert>\n  <ha-alert alert-type=\"warning\" .title=${this._localize(\"ui.example.warning\")}>\n    ${this._localize(\"ui.example.description\")}\n  </ha-alert>\n  <ha-alert alert-type=\"success\" dismissable>\n    ${this._localize(\"ui.example.success\")}\n  </ha-alert>\n`;\n```\n\n## Shortcuts And Tooltips\n\nUse `ShortcutManager` from `src/common/keyboard/shortcuts.ts` for keyboard shortcuts. It blocks shortcuts in input fields, can prevent shortcuts during text selection, and supports character and KeyCode shortcuts for non-latin keyboards. See `src/state/quick-bar-mixin.ts` for global shortcut examples.\n\nUse `ha-tooltip` from `src/components/ha-tooltip.ts` for contextual hover help. See `src/components/ha-label.ts` for an example.\n\n## Panels And Lovelace Cards\n\nPanels commonly extend `SubscribeMixin(LitElement)` and receive route and narrow-layout properties.\n\nLovelace cards should implement `LovelaceCard`, validate config in `setConfig()`, handle loading, error, unavailable, and missing-entity states, and add a configuration editor when needed.\n\nCards are user-story surfaces. Support different households, entity types, responsive layouts, and accessible interaction states.\n","frontmatter":{"name":"ha-frontend-components","description":"Home Assistant frontend component patterns. Use when implementing or reviewing dialogs, ha-form, ha-alert, keyboard shortcuts, tooltips, panels, Lovelace cards, or ha-button usage."},"isInternal":false,"tokens":1082,"sizeBytes":4629},{"name":"SKILL.md","path":".agents/skills/ha-frontend-contexts/SKILL.md","rawUrl":"https://raw.githubusercontent.com/home-assistant/frontend/HEAD/.agents/skills/ha-frontend-contexts/SKILL.md","title":"ha-frontend-contexts","category":"anthropic-skill","format":"markdown","content":"---\nname: ha-frontend-contexts\ndescription: Home Assistant frontend Lit context and hass migration guidance. Use when adding or changing component state access, replacing hass reads, consuming entity or registry contexts, or reviewing rerender behavior.\n---\n\n# HA Frontend Contexts\n\nUse this skill when a component reads Home Assistant state, registries, localization, services, config, UI data, connection state, or API helpers.\n\n## Goal\n\nMove leaf components away from the broad `hass: HomeAssistant` object. Broad `hass` access rerenders components for unrelated changes, hides the data a component depends on, and makes tests harder to mock.\n\nContainer components may keep `hass` when they own it and feed providers. Leaf components should consume the narrowest context that covers their reads.\n\n## Core Files\n\n- Context definitions: `src/data/context/index.ts`\n- `consume` decorator, `ContextSubscriptionController`, and `consumeContext` / `ContextController` for reactive controllers: `src/common/decorators/consume.ts` (use instead of `@lit/context`'s `consume` and `ContextConsumer`, enforced by ESLint)\n- Entity-scoped consume helpers: `src/common/decorators/consume-context-entry.ts`\n- Transform decorator: `src/common/decorators/transform.ts`\n- Canonical migration example: `src/panels/lovelace/cards/hui-button-card.ts`\n- Providers are wired by `contextMixin` on `HassBaseEl`; consumers do not wire providers manually.\n\n## Context Selection\n\n| Context                                                              | Replaces                                                                                                  |\n| -------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------- |\n| `statesContext`                                                      | `hass.states`                                                                                             |\n| `entitiesContext`, `devicesContext`, `areasContext`, `floorsContext` | `hass.entities`, `hass.devices`, `hass.areas`, `hass.floors`                                              |\n| `registriesContext`                                                  | all four registries together                                                                              |\n| `servicesContext`                                                    | `hass.services`                                                                                           |\n| `internationalizationContext`                                        | `hass.localize`, `hass.locale`, `hass.language`                                                           |\n| `formattersContext`                                                  | entity and attribute formatters                                                                           |\n| `configContext`                                                      | `hass.config`, `hass.user`, `hass.auth`, `hass.userData`                                                  |\n| `connectionContext`                                                  | `hass.connection`, `hass.connected`, `hass.debugConnection`, `hass.hassUrl`                               |\n| `apiContext`                                                         | `hass.callService`, `hass.callApi`, `hass.callApiRaw`, `hass.callWS`, `hass.sendWS`, `hass.fetchWithAuth` |\n| `uiContext`                                                          | themes, selected theme, panels, sidebar, and UI state                                                     |\n| `narrowViewportContext`                                              | narrow-layout boolean                                                                                     |\n| `reducedMotionContext`                                               | `prefers-reduced-motion` media query                                                                      |\n\nLazy contexts subscribe on first consumer and tear down after the last consumer: `labelsContext`, `fullEntitiesContext`, `configEntriesContext`, `manifestsContext`, `triggerDescriptionsContext`, and `conditionDescriptionsContext`.\n\nThe single-field contexts such as `localizeContext`, `themesContext`, and `userContext` are deprecated. Use grouped contexts instead.\n\n## Consumption Patterns\n\nImport `consume` from `src/common/decorators/consume`, not from `@lit/context`. The `@lit/context` version forces a host update on every context change, so every consumed field rerenders the component, even without `@state()`, and `@transform` cannot skip anything. Our `consume` leaves update scheduling to the field:\n\n- `@state()` plus `@consume`: rerenders when the context value changes.\n- `@state()` plus `@consume` plus `@transform`: rerenders only when the transformed value changes.\n- `@consume` alone: the field stays current but never triggers a render. Use this for values only read in event handlers or callbacks, such as `apiContext`, or for data read on demand, such as picker item callbacks.\n\nIn reactive controllers, extend `ContextController` and decorate fields with `consumeContext({ context, subscribe, transform })` from the same file. `transform` stores one part of the context value, and `contextUpdated()` runs only when that part changes. `@state()` and `@transform` do not work on controller fields; call `host.requestUpdate()` when the host needs to rerender. Example: `src/data/service-info-controller.ts`.\n\nFor lazily created subscriptions, use `ContextSubscriptionController` from the same file instead of `@lit/context`'s `ContextConsumer`, which also forces a host update. Its callback must store the value in a reactive field or call `host.requestUpdate()` itself when the host needs to rerender.\n\nUse entity-scoped helpers when the component watches an entity ID held on the host:\n\n```ts\n@state() @consumeEntityState({ entityIdPath: [\"_config\", \"entity\"] })\nprivate _stateObj?: HassEntity;\n\n@state() @consumeEntityRegistryEntry({ entityIdPath: [\"_config\", \"entity\"] })\nprivate _entity?: EntityRegistryDisplayEntry;\n\n@state() @consumeLocalize()\nprivate _localize!: LocalizeFunc;\n```\n\nUse `consumeEntityStates` when the host property contains one or more entity IDs. It filters missing entities and preserves the previous record when none of the selected entities changed.\n\n```ts\n@state() @consumeEntityStates({ entityIdPath: [\"_config\", \"entities\"] })\nprivate _stateObjs?: Record<string, HassEntity>;\n```\n\nFor a single field from a grouped context, pair `@consume` with `@transform`:\n\n```ts\n@state()\n@consume({ context: uiContext, subscribe: true })\n@transform<HomeAssistantUI, Themes>({ transformer: ({ themes }) => themes })\nprivate _themes!: Themes;\n```\n\nUse `@transform` with `watch` when the transformer depends on a host property, such as a computed entity ID. `consumeEntityState` and `consumeEntityStates` only watch the first path segment.\n\nTo consume a whole group untransformed, omit `@transform` and type the field as `ContextType<typeof statesContext>` or the matching context type.\n\n## Review Checklist\n\n- The component consumes the narrowest context needed for the data it reads.\n- A broad `hass` property is kept only when the component is a container or external API requires it.\n- Entity-scoped reads use the consume helpers rather than ad hoc context transforms.\n- Context fields read during render or `willUpdate` are marked `@state()` so updates trigger rendering. Fields only read outside the render cycle are left without `@state()`.\n- Tests and mocks only provide the data the component actually consumes.\n","frontmatter":{"name":"ha-frontend-contexts","description":"Home Assistant frontend Lit context and hass migration guidance. Use when adding or changing component state access, replacing hass reads, consuming entity or registry contexts, or reviewing rerender behavior."},"isInternal":false,"tokens":1427,"sizeBytes":7644},{"name":"SKILL.md","path":".agents/skills/ha-frontend-demo/SKILL.md","rawUrl":"https://raw.githubusercontent.com/home-assistant/frontend/HEAD/.agents/skills/ha-frontend-demo/SKILL.md","title":"ha-frontend-demo","category":"anthropic-skill","format":"markdown","content":"---\nname: ha-frontend-demo\ndescription: Home Assistant standalone demo structure, configurations, URL navigation, mocked APIs, shared demo stubs used by gallery, and verification. Use when changing files under demo/, changing gallery consumers of demo/src/stubs/, or running and validating the demo.\n---\n\n# HA Frontend Demo\n\nUse this skill for standalone demo work and changes to shared demo stubs consumed by the gallery. Follow the persistent repository guidance in `AGENTS.md` and load matching specialist skills alongside this skill, especially `ha-frontend-testing` when running or validating the demo.\n\n## Purpose\n\nThe demo is the full Home Assistant frontend running against a mocked backend and is published at <https://demo.home-assistant.io>. It needs no Home Assistant server, which makes it the easiest way to load the real UI in a browser, for example to take screenshots.\n\n## Running The Demo\n\nRun commands from the repository root:\n\n```bash\npnpm dev:demo               # Development server on http://localhost:8090\npnpm dev:demo --background  # Detached; also supports --status/--stop/--logs\n```\n\nUse the E2E workflows documented in `ha-frontend-testing` when validating the demo.\n\n## Opening A Specific Demo\n\nThe demo contains multiple demo configurations. Select one directly with the `demo` query parameter, for example `http://localhost:8090/?demo=<slug>`. The valid slugs are defined in `demoConfigs` in `demo/src/configs/demo-configs.ts`, so \"the second demo\" means the slug of the second entry in that list. An unknown slug falls back to the default, which is the first entry.\n\n## Opening A Specific Page\n\nThe demo build uses hash-based routing: the frontend path goes in the URL hash, and the `demo` query parameter goes before the `#`. The URL format is:\n\n```text\nhttp://localhost:8090/?demo=<slug>#/<path>\n```\n\nUseful paths:\n\n- `/lovelace/0`: The selected demo's dashboard, also the default when no hash is given.\n- `/energy`: The energy dashboard. Its tabs are views: `/energy/overview` (Summary), `/energy/electricity`, `/energy/gas`, `/energy/water`, and `/energy/now`.\n- `/map`, `/history`, `/todo`, `/config`: Other sidebar panels.\n\nFor example, the water tab of the energy dashboard of the second demo is:\n\n```text\nhttp://localhost:8090/?demo=<second slug>#/energy/water\n```\n\n## Structure\n\n- `demo/src/ha-demo.ts`: Root element that sets up all backend mocks.\n- `demo/src/configs/<slug>/`: One directory per demo configuration, containing entities, dashboard, and theme data.\n- `demo/src/configs/demo-configs.ts`: Registry of demo configurations and URL slug handling.\n- `demo/src/stubs/`: Mocked WebSocket and REST APIs.\n- `demo/script/develop_demo`, `demo/script/build_demo`: Development server and static build wrappers.\n\n## Core Data\n\nThe service descriptions in `src/fake_data/demo_services.ts` and the trigger and condition descriptions and icons in `demo/src/stubs/automation_platforms_*.ts` are generated from Home Assistant Core. Do not edit them by hand. Run `script/gen_demo_core_data` to regenerate them from the core `dev` branch, and add a domain to the lists in `build-scripts/gulp/gen-demo-core-data.js` when the demo needs its services, triggers, or conditions. Their translations come from the backend translations merged into the demo build.\n\n## Shared Gallery Stubs\n\n`demo/src/stubs/` is shared, not demo-private: gallery pages import from it directly. Before changing or removing anything a stub does, search for its callers across `demo/src/` and `gallery/src/`, then check the affected gallery pages as well as the demo.\n\nThe two consumers use a stub differently, so demo behavior does not predict gallery behavior. A gallery page calls stubs against the `hass` from `provideHass`, where `hass.config` is the shared `demoConfig` object. A stub that mutates `hass.config` in place is therefore visible to the page. `demo/src/ha-demo.ts` copies `components` into a new array before the stubs run, so the same mutation never reaches the demo. A change can look correct in the demo while quietly breaking a gallery page.\n\n## Verification\n\nLoad `ha-frontend-testing` and use its demo and gallery workflows. Validate both consumers when changing shared stubs. Documentation-only changes do not require code tests unless examples or commands change.\n","frontmatter":{"name":"ha-frontend-demo","description":"Home Assistant standalone demo structure, configurations, URL navigation, mocked APIs, shared demo stubs used by gallery, and verification. Use when changing files under demo/, changing gallery consumers of demo/src/stubs/, or running and validating the demo."},"isInternal":false,"tokens":965,"sizeBytes":4297},{"name":"SKILL.md","path":".agents/skills/ha-frontend-events/SKILL.md","rawUrl":"https://raw.githubusercontent.com/home-assistant/frontend/HEAD/.agents/skills/ha-frontend-events/SKILL.md","title":"ha-frontend-events","category":"anthropic-skill","format":"markdown","content":"---\nname: ha-frontend-events\ndescription: Home Assistant frontend event patterns. Use when typing event handlers, using HASSDomEvent types, dispatching with fireEvent, or declaring HASSDomEvents and event maps.\n---\n\n# HA Frontend Events\n\nUse this skill when implementing or reviewing event listeners and custom event contracts. Cross-load `ha-frontend-components` when the work also involves dialogs, forms, alerts, shortcuts, tooltips, panels, Lovelace cards, or buttons.\n\n## Event Handling\n\nUse the event types from `src/common/dom/fire_event.ts` instead of plain `Event`, generic `CustomEvent`, or element casts when they express the handler contract:\n\n- Use `HASSDomCurrentTargetEvent<T>` to read the element on which the listener was registered through `ev.currentTarget`.\n- Use `HASSDomTargetEvent<T>` only to read the element that originated the event through `ev.target`.\n- Use `HASSDomEvent<T>` to read a custom event payload through `ev.detail`.\n- Use `ValueChangedEvent<T>` from `src/types.ts` for the standard `value-changed` event.\n- Prefer an event type exported by the component being listened to, such as `HaSelectSelectEvent<T, Clearable>` or `HaDropdownSelectEvent<TValue, TData>`, over reconstructing its detail type.\n- Use `ActionHandlerEvent` from `src/data/lovelace/action_handler.ts` for Lovelace tap, hold, and double-tap handlers.\n\nImport event and element types with `import type`:\n\n```ts\nimport type {\n  HASSDomCurrentTargetEvent,\n  HASSDomEvent,\n  HASSDomTargetEvent,\n} from \"../common/dom/fire_event\";\nimport type { HaCheckbox } from \"../components/ha-checkbox\";\nimport type { HaEntityPicker } from \"../components/entity/ha-entity-picker\";\nimport type { HaRadioGroup } from \"../components/radio/ha-radio-group\";\nimport type { ValueChangedEvent } from \"../types\";\n```\n\nType the handler so the selected property can be read directly. Do not cast `ev.currentTarget` or assign it to a single-use variable:\n\n```ts\nprivate _scopeChanged(ev: HASSDomCurrentTargetEvent<HaRadioGroup>): void {\n  this._scope = ev.currentTarget.value;\n}\n\nprivate _checkedChanged(ev: HASSDomTargetEvent<HaCheckbox>): void {\n  this._checked = ev.target.checked;\n}\n\nprivate _valueChanged(ev: ValueChangedEvent<string>): void {\n  this._value = ev.detail.value;\n}\n\nprivate _itemSelected(ev: HASSDomEvent<{ id: string }>): void {\n  this._selectedId = ev.detail.id;\n}\n```\n\nUse intersections when a handler needs more than one facet of an event. Keep the native event type when the handler reads native fields such as `key`, modifier keys, `dataTransfer`, or focus relationships:\n\n```ts\nprivate _entityChanged(\n  ev: ValueChangedEvent<string> & HASSDomCurrentTargetEvent<HaEntityPicker>\n): void {\n  ev.currentTarget.value = ev.detail.value;\n}\n\nprivate _keyDown(\n  ev: KeyboardEvent & HASSDomCurrentTargetEvent<HTMLInputElement>\n): void {\n  if (ev.key === \"Enter\") {\n    this._submit(ev.currentTarget.value);\n  }\n}\n```\n\nDispatch Home Assistant component events with `fireEvent()` instead of constructing `Event` or `CustomEvent` directly. Register the event name and detail type by augmenting `HASSDomEvents`; use `undefined` when an event has no detail. `fireEvent()` constrains event names and supplied detail, and events bubble and cross shadow boundaries by default:\n\n```ts\nfireEvent(this, \"item-selected\", { id: item.id });\nfireEvent(this, \"refresh-requested\");\n```\n\nWhen an event is already registered, derive handler and listener types from its registration rather than repeating the payload shape:\n\n```ts\nprivate _itemSelected(\n  ev: HASSDomEvent<HASSDomEvents[\"item-selected\"]>\n): void {\n  this._selectedId = ev.detail.id;\n}\n```\n\n`HASSDomEvents` types `fireEvent()` calls. Augment `HTMLElementEventMap` for typed listeners on HTML elements, or `GlobalEventHandlersEventMap` when the event is handled on global event targets.\n\nIn component files, prefer placing global event declarations after the class at the bottom of the file. Preserve the existing placement when editing established files; foundational type, helper, and mixin files commonly keep declarations near the top before their consumers.\n\n```ts\ndeclare global {\n  interface HASSDomEvents {\n    \"item-selected\": { id: string };\n    \"refresh-requested\": undefined;\n  }\n\n  interface HTMLElementEventMap {\n    \"item-selected\": HASSDomEvent<HASSDomEvents[\"item-selected\"]>;\n  }\n}\n```\n","frontmatter":{"name":"ha-frontend-events","description":"Home Assistant frontend event patterns. Use when typing event handlers, using HASSDomEvent types, dispatching with fireEvent, or declaring HASSDomEvents and event maps."},"isInternal":false,"tokens":995,"sizeBytes":4343},{"name":"SKILL.md","path":".agents/skills/ha-frontend-gallery/SKILL.md","rawUrl":"https://raw.githubusercontent.com/home-assistant/frontend/HEAD/.agents/skills/ha-frontend-gallery/SKILL.md","title":"ha-frontend-gallery","category":"anthropic-skill","format":"markdown","content":"---\nname: ha-frontend-gallery\ndescription: Home Assistant frontend gallery structure, pages, demos, content, and verification. Use when changing files under gallery/, including gallery markdown, TypeScript demos, sidebar entries, mock data, page generation, or gallery builds.\n---\n\n# HA Frontend Gallery\n\nUse this skill for all work under `gallery/`. Follow the persistent repository guidance in `AGENTS.md` and load the matching specialist skills alongside this gallery-specific guidance.\n\n## Quick Reference\n\nRun commands from the repository root unless noted otherwise:\n\n```bash\ngallery/script/develop_gallery  # Start the gallery development server\ngallery/script/build_gallery    # Build the static gallery\npnpm lint                       # ESLint, Prettier, TypeScript, and Lit checks\npnpm lint:types                 # TypeScript compiler, without file arguments\n```\n\nNever run `pnpm lint:types` or `tsc` with file arguments. File arguments make `tsc` ignore `tsconfig.json` and can emit `.js` files into `src/`.\n\n## Purpose\n\nThe gallery is a developer and designer reference for Home Assistant frontend UI patterns. It documents component APIs, shows realistic Lovelace and more-info states, captures brand and copy guidance, and provides reproducible demos that are safe to inspect outside a running Home Assistant instance.\n\n- Prefer demonstrating real production components from `src/` instead of creating gallery-only replacements.\n- Keep fake state, sample data, and demo-only helpers inside `gallery/`.\n- Do not move gallery stubs or demo data into production code unless a production feature explicitly needs them.\n- Do not hand-edit generated output under `gallery/build/` or `gallery/dist/`.\n\n## Structure\n\n- `gallery/sidebar.js`: Defines gallery sections, headers, and explicit page ordering.\n- `gallery/script/develop_gallery`: Wrapper for the `develop-gallery` gulp task.\n- `gallery/script/build_gallery`: Wrapper for the `build-gallery` gulp task.\n- `gallery/src/entrypoint.js`: Creates the `<ha-gallery>` shell.\n- `gallery/src/ha-gallery.ts`: Renders the drawer, page routing, markdown descriptions, demos, edit links, and RTL toggle.\n- `gallery/src/html/index.html.template`: HTML template used by the gallery build.\n- `gallery/src/pages/<category>/<page>.markdown`: Optional page description and frontmatter.\n- `gallery/src/pages/<category>/<page>.ts`: Optional live demo module for the same page ID.\n- `gallery/src/components/`: Gallery-only demo wrappers like `demo-card`, `demo-cards`, `demo-more-info`, and `page-description`.\n- `gallery/src/data/`: Fake `hass`, demo states, mock traces, and reusable sample data.\n- `gallery/public/`: Static assets copied into the gallery output.\n\n## Page Model\n\nGallery pages are generated by `gather-gallery-pages` in `build-scripts/gulp/gallery.js`.\n\n- A page ID is the path under `gallery/src/pages/` without the extension, like `components/ha-button`.\n- A `.markdown` file and a `.ts` file with the same page ID become one gallery page.\n- A page may have only markdown, only a TypeScript demo, or both.\n- Markdown can contain YAML frontmatter with `title` and optional `subtitle`.\n- Markdown that contains only frontmatter contributes metadata without rendering a description block.\n- TypeScript demo modules are dynamically imported for side effects when the page is opened.\n- A demo module must define a custom element named `demo-${category}-${page}` with slashes replaced by hyphens, like `demo-components-ha-button` for `components/ha-button`.\n- `gallery/src/ha-gallery.ts` renders that element with `dynamicElement()` based on the current page ID.\n\n## Sidebar\n\nUse `gallery/sidebar.js` when a page needs a visible section, section header, or deterministic ordering.\n\n- `category` must match the first directory name under `gallery/src/pages/`.\n- `header` is the section label shown in the drawer.\n- `pages` is optional. When present, listed pages keep that exact order.\n- Pages in a category that are not listed are appended alphabetically after the listed pages.\n- New categories without a sidebar entry are appended by the generator with their category name as the header.\n- If a listed page does not exist, the generator logs an error during `gather-gallery-pages`.\n\n### Subsections\n\nA section can group its pages under named subsections instead of one flat list. Use this for large categories where related pages should sit together.\n\n- `subsections` is an array of `{ header, pages }`. It is mutually exclusive with a flat `pages` array on the same group.\n- Each subsection `header` is a non-collapsible label rendered inside the section's expansion panel; the section stays the only collapsible level.\n- Listed pages keep their per-subsection order.\n- Any pages found in the category but not listed in a subsection are collected into a generated `Other` subsection, appended alphabetically. The `Other` subsection is omitted when there are no leftovers.\n- A listed page that does not exist still logs an error during `gather-gallery-pages`.\n- Use sentence case for subsection headers and follow the content standards below.\n\n## Markdown Pages\n\nUse markdown pages for explanations, design guidance, API notes, and copy standards.\n\n- Start with frontmatter when the page needs a title or subtitle.\n- Use sentence case for titles, headings, labels, and UI copy.\n- Put the live example before the reference API when that makes the page easier to scan.\n- Use fenced code blocks with a language tag for copyable examples.\n- Keep examples short and focused on the behavior being documented.\n- Prefer real component names and attributes over prose-only descriptions.\n- Use Home Assistant terminology from `ha-frontend-user-facing-text`.\n- For remove/delete and add/create wording, follow `gallery/src/pages/misc/remove-delete-add-create.markdown`.\n\nGallery markdown is documentation content and is not localized with `localize`. If demo code creates production UI strings, follow the localization and copy guidance in `ha-frontend-user-facing-text`.\n\n## Demo Components\n\nUse TypeScript demo pages for interactive or stateful examples.\n\n- Import production components from `src/` using the correct relative path from the demo file.\n- Import reusable gallery helpers from `gallery/src/components/` when they already model the pattern.\n- Use `demo-card` and `demo-cards` for Lovelace card examples that render YAML card configs.\n- Use `demo-more-info` and `demo-more-infos` for more-info dialog examples.\n- Use shared mock data from `gallery/src/data/` instead of repeating large fake state objects inline.\n- Show meaningful states, such as loading, unavailable, empty, error, active, inactive, and disabled when relevant.\n- Check responsive behavior and the gallery RTL toggle when layout or direction-sensitive UI changes.\n- Keep unavoidable casts or loose demo parsing local to the demo helper or demo page.\n\nThe gallery ESLint config allows `console` for gallery diagnostics. Do not copy that exception into production frontend code.\n\n## Content Standards\n\nFollow the detailed copy standards in `ha-frontend-user-facing-text`: use American English, sentence case, active voice, inclusive language, direct user-focused wording, and consistent Home Assistant terminology.\n\n- Use `Home Assistant` in full, not `HA` or `HASS`.\n- Use `integration` instead of `component` for product concepts.\n- Use `Remove` for reversible disassociation and `Delete` for permanent deletion.\n- Use `Add` for existing items and `Create` for something made from scratch.\n- Avoid Latin abbreviations like `e.g.` and `i.e.` in prose.\n- Avoid stitching sentence fragments together in production UI examples.\n\n## Verification\n\n- For markdown, sidebar, and page-generation changes, run `gallery/script/build_gallery`.\n- For TypeScript demo or gallery shell changes, run the smallest relevant check plus `pnpm lint` when practical.\n- For type checking, run `pnpm lint:types` without file arguments.\n- For visual changes, run `gallery/script/develop_gallery` and check the affected page on desktop, narrow viewport, and RTL when relevant.\n- If verification is skipped, state which command was skipped and why.\n","frontmatter":{"name":"ha-frontend-gallery","description":"Home Assistant frontend gallery structure, pages, demos, content, and verification. Use when changing files under gallery/, including gallery markdown, TypeScript demos, sidebar entries, mock data, page generation, or gallery builds."},"isInternal":false,"tokens":1688,"sizeBytes":8151},{"name":"SKILL.md","path":".agents/skills/ha-frontend-lit/SKILL.md","rawUrl":"https://raw.githubusercontent.com/home-assistant/frontend/HEAD/.agents/skills/ha-frontend-lit/SKILL.md","title":"ha-frontend-lit","category":"anthropic-skill","format":"markdown","content":"---\nname: ha-frontend-lit\ndescription: Home Assistant frontend Lit conventions. Use when working with reactive properties, internal state, DOM queries, lifecycle methods, or render-derived state.\n---\n\n# HA Frontend Lit\n\nUse this skill when implementing or reviewing Lit component state, DOM access, lifecycle methods, or rendering behavior. Cross-load `ha-frontend-types` for Home Assistant data contracts, assertions, and lifecycle parameter types.\n\n## Reactive Fields\n\nThis project currently uses Lit's TypeScript experimental decorators with `useDefineForClassFields: false`. Match existing declarations and do not introduce standard-decorator `accessor` syntax unless the project changes decorator mode.\n\n- Use `@property()` for public reactive API and `@state()` for private reactive state.\n- Prefer inferred types for initialized reactive fields when inference preserves the intended type; annotate when widening or an external contract requires it.\n\n## DOM Queries\n\nPrefer Lit's `@query()` or `@queryAll()` decorators for fixed selectors in the component's render root.\n\n- Type the decorated field with the narrowest useful DOM or component interface.\n- Keep the field optional when it may be absent at the point of access, including conditional rendering or pre-render lifecycle access.\n- Use a definite assignment assertion only when every call site runs after the node is guaranteed to exist.\n- The optional second argument to `@query()`, as in `@query(\"#target\", true)`, caches the first query result. Use it only when later renders cannot replace the queried node.\n- Use a direct query when the selector is dynamic or the target is outside the component's render root. Before querying a child, consider whether the required value belongs in parent state or data flow.\n\n## Render-Derived State\n\n- Prefer render-local values for inexpensive structures used only by that render.\n- Assign a render-local value once when repeated evaluation is non-trivial or a local name improves clarity.\n- Keep purely presentational derivations in `render()`. Use stored state or `willUpdate()` when the value must participate in lifecycle work, reflection, CSS, or non-render consumers.\n- Use `memoizeOne` for pure, argument-derived transforms when stable input identity avoids meaningful repeated work. Keep inputs explicit and limited, and do not add caching without a credible benefit over computing the value directly.\n\n## References\n\n- [Reactive properties](https://lit.dev/docs/components/properties/)\n- [Decorators](https://lit.dev/docs/components/decorators/)\n- [Shadow DOM queries](https://lit.dev/docs/components/shadow-dom/#query)\n- [Reactive update cycle](https://lit.dev/docs/components/lifecycle/)\n","frontmatter":{"name":"ha-frontend-lit","description":"Home Assistant frontend Lit conventions. Use when working with reactive properties, internal state, DOM queries, lifecycle methods, or render-derived state."},"isInternal":false,"tokens":536,"sizeBytes":2706},{"name":"SKILL.md","path":".agents/skills/ha-frontend-review/SKILL.md","rawUrl":"https://raw.githubusercontent.com/home-assistant/frontend/HEAD/.agents/skills/ha-frontend-review/SKILL.md","title":"ha-frontend-review","category":"anthropic-skill","format":"markdown","content":"---\nname: ha-frontend-review\ndescription: Home Assistant frontend PR and review guidance, including implementation quality, existing review feedback, established design patterns, and the authority of UI/UX evidence. Use when reviewing frontend changes, preparing a PR, checking recurring review issues, or applying the PR template.\n---\n\n# HA Frontend Review\n\nUse this skill when reviewing Home Assistant frontend changes or preparing a pull request.\n\n## Review Preparation\n\n1. Establish the review scope from the diff and the behavior changed by it.\n2. Before identifying findings, use the [project skill catalogue](../../../AGENTS.md#project-skills) to load every matching companion's `SKILL.md`. Follow applicable companion references in those skills as well; this review skill supplies the workflow, and companions supply the domain-specific criteria.\n3. Apply all loaded guidance within that scope. Report only problems introduced or worsened by the changeset, anchored to changed lines. Read surrounding code to understand affected behavior, not to generate unrelated cleanup or migration requests.\n\n## Pull Request Body\n\nWhen creating a pull request, use `.github/PULL_REQUEST_TEMPLATE.md` as the body.\n\n- Do not omit, reorder, or rewrite template sections.\n- Check the appropriate \"Type of change\" box based on the actual change.\n- Do not check checklist items on behalf of the user.\n- If the PR includes UI changes, remind the user to add screenshots or a short video.\n- Explain what the change does for users, not only implementation details.\n- Use Markdown.\n\n## Pre-Submission Checklist\n\n- `pnpm lint` passes when practical for the scope.\n- `pnpm test` or focused relevant tests are green when practical for the scope.\n- Each test added by the change protects real logic, not the look of a component.\n- User-facing text is localized and follows `ha-frontend-user-facing-text` guidance.\n- Components handle loading, error, unavailable, and missing-entity states.\n- Entity existence is checked before property access.\n- Event listeners and subscriptions are cleaned up.\n- UI is accessible to screen readers and keyboard users.\n\n## Recurring Review Issues\n\nScope and public surface:\n\n- Keep changes independently reviewable and limited to the requested area.\n- Prefer existing Home Assistant helpers, Lit primitives, and component seams over parallel implementations.\n- Challenge new public properties and optional feature surface when transient options or existing seams meet the requirement with less lifecycle and consistency cost.\n\nStateful and asynchronous UI:\n\n- Review transitions in both directions, not only individual rendered states.\n- When controls reappear, restore valid defaults instead of retaining state that was only valid while they were hidden.\n- Establish immutable dirty-state baselines before asynchronous work, guard against stale responses, and preserve unsaved state in mounted editors.\n- Determine an action's current meaning before applying dirty-state checks, especially when an action can change between Save and Close.\n\nReadiness and invalidation:\n\n- Treat readiness as the first displayable terminal result, including stable empty and error states.\n- Register child readiness before resolving the parent, do not treat fallback work as terminal, and replay readiness correctly for cached or reused panels.\n- Ensure every value read by memoized output participates in its invalidation.\n\nRepository-owned contracts:\n\n- Consult the public [frontend contributor documentation](https://contributors.home-assistant.io/frontend/) for documented architecture, data flow, design, and development workflows.\n- For new leaf components, load `ha-frontend-contexts` and verify they consume narrow contexts instead of introducing a broad `hass` property; containers and external APIs may still require `hass`.\n- Verify backend assumptions against the owning Core, Supervisor, or WebSocket implementation, and component assumptions against the exported component contract.\n- Prefer canonical repository helpers and test setup over duplicate local implementations.\n- Promote AI-review concerns into durable guidance only when supported by code evidence, reproduced behavior, an accepted corrective commit, or human-maintainer validation.\n\nUser experience and accessibility:\n\n- Forms need proper labels, helper text, and validation feedback.\n- Form markup should not cause password managers to identify fields incorrectly.\n- Clickable areas should be large enough for touch interaction.\n- Hover, active, disabled, loading, and focus states should be clear.\n\nDialog and modal patterns:\n\n- Multi-step operations should show progress.\n- Dialog state should survive background operations correctly.\n- Cancel and close buttons should behave consistently.\n- Defaults should be helpful without blocking user override.\n\nComponent design patterns:\n\n- Terminology should be consistent. Use words like \"Join\" or \"Apply\" instead of \"Group\" when that better matches the user action.\n- Visual hierarchy should use appropriate font sizes, weights, and spacing ratios.\n- Components should align to the design grid.\n- Badges and indicators should be placed consistently.\n\nCode quality:\n\n- Null and undefined paths should be handled explicitly.\n- Potentially undefined array and object access should be guarded.\n- Event handlers, timers, observers, and subscriptions should be cleaned up.\n\nConfiguration and props:\n\n- Make configuration fields optional when sensible.\n- Provide reasonable defaults.\n- Keep APIs extensible without adding speculative abstractions.\n- Validate configuration before applying changes.\n\n## UI/UX Evidence\n\nFor user-facing changes, establish the existing design context as part of frontend review:\n\n- Treat an applicable gallery specification as the authoritative repository source for the documented component or interaction. Inspect its written guidance and demos when present.\n- Note relevant production designs, approved designs, and shared `ha-*` components so the implementation can also be compared with established frontend behaviour.\n- Require the applicable gallery documentation or demo to change when the implementation intentionally changes behaviour documented there.\n- Prefer an established input layout or appropriate shared component over a raw input or ad hoc control.\n- Treat explicit UI/UX approval, current repository guidance, and direct human or workflow instructions as stronger evidence than inference.\n- Treat linked tasks as evidence of the problem and stated requirements, not automatic UI/UX approval.\n- When the direction remains uncertain, search frontend pull requests that carry or previously carried **Needs UX**. Inspect their label history, comments, and reviews rather than relying on the current label alone.\n- Judge relevant feedback by its content and surrounding discussion. When GitHub provides `author_association`, prioritise feedback marked `MEMBER`.\n- Prefer recent feedback about similar interactions or components. Label removal can show that a workflow gate moved on, but does not prove approval without the surrounding discussion.\n- Summarise the applicable guidance instead of maintaining a reviewer list or treating one historical decision as a permanent rule.\n\n## Review Flow\n\n- Before reviewing a pull request, read its existing comments, reviews, and threads, including their status, resolver, and Copilot resolution reason when available.\n- Prioritise substantive human feedback, especially from authors marked `MEMBER`, and validate agent-generated feedback against the code and repository guidance.\n- Do not duplicate unresolved findings as new inline comments; reference any that still need action in the review summary. Treat resolved feedback as closed only when the resolution reason or surrounding discussion supports that outcome; otherwise validate it against the current code before suppressing it. Respect **Won't fix** and **Incorrect** reasons.\n- When a later commit addresses an unresolved thread, note in the review summary that it looks addressed, linking the thread and the commit, so it can be resolved. Do not report it as still open.\n- Identify behavioral regressions, bugs, accessibility issues, and missing tests that `ha-frontend-testing` calls for first.\n- Do not ask for new tests on visual components. If the visuals clearly changed and the PR has no screenshots or videos, suggest adding them instead.\n- When the change adds something repository guidance rules out, such as a rendering test, open the comment with the removal (\"Remove this test file\") and cite the guidance in one sentence. Do not critique how the unwanted code is written, as that reads as a request to fix it.\n- Record the applicable UI/UX evidence for user-facing changes, whether or not further input is needed.\n- Keep style-only comments secondary unless they affect maintainability or user experience.\n- Prefer small, direct fixes over large refactors during review follow-up.\n","frontmatter":{"name":"ha-frontend-review","description":"Home Assistant frontend PR and review guidance, including implementation quality, existing review feedback, established design patterns, and the authority of UI/UX evidence. Use when reviewing frontend changes, preparing a PR, checking recurring review issues, or applying the PR template."},"isInternal":false,"tokens":1641,"sizeBytes":8962},{"name":"SKILL.md","path":".agents/skills/ha-frontend-styling/SKILL.md","rawUrl":"https://raw.githubusercontent.com/home-assistant/frontend/HEAD/.agents/skills/ha-frontend-styling/SKILL.md","title":"ha-frontend-styling","category":"anthropic-skill","format":"markdown","content":"---\nname: ha-frontend-styling\ndescription: Home Assistant frontend styling, theming, spacing, responsive layout, RTL, and View Transitions guidance. Use when editing CSS, layout, motion, or visual component structure.\n---\n\n# HA Frontend Styling\n\nUse this skill when editing CSS, layout, visual hierarchy, theme integration, responsive behavior, RTL support, or view transitions.\n\n## Theme And Layout Basics\n\n- Use Home Assistant CSS custom properties instead of hardcoded colors.\n- Use `--ha-space-*` spacing tokens instead of hardcoded spacing where possible.\n- Keep components mobile-first and enhance for desktop.\n- Keep layouts RTL-safe. Prefer logical properties when they fit.\n- Prefer `ha-*` components and current Web Awesome wrappers.\n- Avoid adding new legacy Material Web Components (`mwc-*`).\n- Scope styles to the component. Do not rely on global styles for component internals.\n\nSpacing tokens are defined in `src/resources/theme/core.globals.ts`. The scale runs from `--ha-space-1` at 4px through `--ha-space-20` at 80px in 4px increments. Common values are `--ha-space-2` at 8px, `--ha-space-4` at 16px, and `--ha-space-8` at 32px.\n\n```ts\nstatic get styles() {\n  return css`\n    :host {\n      padding: var(--ha-space-4);\n      color: var(--primary-text-color);\n      background-color: var(--card-background-color);\n    }\n\n    .content {\n      gap: var(--ha-space-2);\n    }\n\n    @media (max-width: 600px) {\n      :host {\n        padding: var(--ha-space-2);\n      }\n    }\n  `;\n}\n```\n\n## Interaction States\n\n- Make touch targets large enough for mobile.\n- Provide clear hover, active, focus, disabled, loading, error, and unavailable states.\n- Preserve keyboard navigation and visible focus indicators.\n- Maintain WCAG AA contrast for text and essential UI affordances.\n\n## View Transitions\n\nUse the View Transitions API only for meaningful continuity between DOM states.\n\nCore resources:\n\n- Utility wrapper: `src/common/util/view-transition.ts`, `withViewTransition()`.\n- Launch-screen fade example: `src/util/launch-screen.ts`.\n- Animation keyframes: `src/resources/theme/animations.globals.ts`.\n- Animation duration tokens: `src/resources/theme/core.globals.ts`.\n\nImplementation rules:\n\n- Use `withViewTransition()` for fallback behavior.\n- Keep transitions simple. Subtle fades and crossfades usually work best.\n- Use `--ha-animation-duration-fast`, `--ha-animation-duration-normal`, or `--ha-animation-duration-slow` for timing.\n- Ensure each `view-transition-name` is unique at any given time.\n- Remember only one view transition can run at a time.\n- View transitions operate at document level and do not work inside Shadow DOM style isolation. For web components, set `view-transition-name` on `:host` or use document-level transitions.\n- The root gets `view-transition-name: root` by default. Target `::view-transition-group(root)` to customize the default page transition.\n\n## Review Checklist\n\n- Styling uses theme variables and spacing tokens where practical.\n- Layout is mobile-first and RTL-safe.\n- Component styles are scoped.\n- Interactive states are clear and accessible.\n- Motion is subtle, tokenized, and respects reduced-motion behavior through existing globals.\n","frontmatter":{"name":"ha-frontend-styling","description":"Home Assistant frontend styling, theming, spacing, responsive layout, RTL, and View Transitions guidance. Use when editing CSS, layout, motion, or visual component structure."},"isInternal":false,"tokens":711,"sizeBytes":3195},{"name":"SKILL.md","path":".agents/skills/ha-frontend-testing/SKILL.md","rawUrl":"https://raw.githubusercontent.com/home-assistant/frontend/HEAD/.agents/skills/ha-frontend-testing/SKILL.md","title":"ha-frontend-testing","category":"anthropic-skill","format":"markdown","content":"---\nname: ha-frontend-testing\ndescription: Home Assistant frontend testing and validation workflow. Use when adding or updating tests, running lint, TypeScript checks, Vitest, Playwright e2e suites, dev servers, or chart-data benchmarks.\n---\n\n# HA Frontend Testing\n\nUse this skill when choosing or running validation for frontend changes.\n\n## Test Helpers\n\n- Before adding or changing tests, inspect the relevant suite's existing helpers and fixtures. Reuse them instead of duplicating setup, test data, navigation, interactions, waits, or assertions.\n- When the same test flow appears more than once, move it into the closest suite-local helper with a focused interface.\n- Keep one-off test behaviour in the test unless a helper makes the intent materially clearer. Do not hide the behaviour under test behind broad, configurable abstractions.\n\n## Core Commands\n\n```bash\npnpm lint          # ESLint + Prettier + TypeScript + Lit\npnpm format        # Auto-fix ESLint + Prettier\npnpm lint:types    # TypeScript compiler, run without file arguments\npnpm test          # Vitest\npnpm build         # Full production build\npnpm dev           # App dev server\npnpm dev:serve     # Local serving dev server\n```\n\nNever run `tsc` or `pnpm lint:types` with file arguments. File arguments make `tsc` ignore `tsconfig.json` and can emit `.js` files into `src/`.\n\nFor focused type feedback on one file, use editor diagnostics instead of a file-scoped `tsc` command.\n\n## Production Builds\n\nProduction builds support foreground and managed background execution:\n\n```bash\npnpm build                         # Full foreground build\npnpm build --background            # Full managed background build\npnpm build --modern                # Modern frontend_latest bundle only\npnpm build --modern --background   # Modern managed background build\npnpm build --status\npnpm build --logs [--follow]\npnpm build --stop\n```\n\nUse `pnpm build --modern --background` for production bundle-size or browser performance comparisons that only need modern browser output. It runs the normal metadata and static preparation, minifies and compresses the modern `frontend_latest` bundle and shared static assets, and generates modern-only entry pages and service workers. It deliberately skips the legacy bundle and its service worker.\n\nDo not pass `--help`, `--background`, or `--modern` to `script/build_frontend`; that raw script does not parse arguments and always starts the full foreground build. Use `pnpm build` for managed builds. App builds and development servers keep exclusive ownership of `hass_frontend/` for their lifetime.\n\nManaged app, demo, gallery, and E2E app workflows share one lifetime lock, so only one build or development server can run at a time.\n\n## When To Add Tests\n\n- Write tests for code that computes something: data processing, utilities, config validation, and strategies.\n- Do not write rendering tests. This includes views, panels, and components whose text, styles, slots, or option defaults are checked, or that only put context and helper data into a template.\n- Do not try to cover every scenario, especially for behaviour that changes often.\n- If you are not sure a test is useful, describe it and what it would catch, and let the user decide.\n- Tests never talk to a real Home Assistant. Replace `callWS`, `callApi`, and the connection with fakes.\n\n## Dev Servers\n\n`pnpm dev` builds and watches the app, served by a running Home Assistant core configured through `development_repo`.\n\n`pnpm dev:serve` also serves locally and supports `-c` for the core URL and `-p` for the port. The default is 8124, or 8123 in a devcontainer.\n\nDev server commands support `--background`, `--status`, `--stop`, and `--logs [--follow]`. `pnpm dev`, `pnpm dev:serve`, `pnpm dev:demo`, and `pnpm dev:gallery` also support `--fetch-translations`; this runs translation fetching, including first-time GitHub device authentication, under the workflow lock before starting the watcher. It works in foreground and background modes. Prefer managed background mode while iterating so the watcher stays available across test runs without occupying the terminal. `pnpm dev` and `pnpm dev:serve` share one managed process slot because both write the app output.\n\n## Playwright E2E\n\nEach suite has its own dev server port. Playwright reuses an existing server locally when its configured URL responds; otherwise it performs a slow full build. When a development watcher is being reused, rspack recompiles on save and reruns should not need a restart.\n\nStart the relevant suite server, then run that suite:\n\n| Suite   | Background server                            | Test command            |\n| ------- | -------------------------------------------- | ----------------------- |\n| App     | `pnpm test:e2e:app:dev --background` on 8095 | `pnpm test:e2e:app`     |\n| Demo    | `pnpm dev:demo --background` on 8090         | `pnpm test:e2e:demo`    |\n| Gallery | `pnpm dev:gallery --background` on 8100      | `pnpm test:e2e:gallery` |\n\nThe custom development wrappers use `/__ha_dev_status` to identify and manage their own suites. Playwright server reuse checks the configured URL instead. Wrapper start and stop operations are idempotent for a matching suite and reject an unrelated process occupying the port.\n\nLocal runs against a watched development server do not always match CI's clean build artifacts, environment, sharding, or worker configuration. Use background servers for the fast iteration loop, but confirm the relevant CI jobs complete successfully before considering E2E changes verified.\n\nUse `-g \"<title>\" --project=chromium` to narrow a run. `pnpm test:e2e` runs suites sequentially when managed servers are unavailable to prevent cold builds racing over shared generated assets. Run suites directly; piping through output truncation hides progress and failures.\n\nThe app suite uses a stripped-down harness for e2e. Demo and gallery use their normal dev servers.\n\n## Benchmarks\n\nFor chart data transforms such as history, statistics, energy, and downsampling, read and follow the complete workflow in `test/benchmarks/README.md` before making benchmark or optimization changes.\n\nThat workflow owns the baseline, noise analysis, guardrails, acceptance thresholds, and reporting requirements. In particular, optimizations must keep output bit-identical; never update snapshots or modify fixtures to make an optimization pass.\n\n## Verification Selection\n\n- Documentation-only change: no code test required unless examples or commands changed.\n- Type-only or utility change: run focused Vitest if available, then `pnpm lint:types` if practical.\n- Lit component change: run relevant tests plus lint or typecheck depending on scope.\n- E2E-sensitive flow: start the relevant e2e dev server and run the narrow Playwright suite.\n- Broad refactor: run `pnpm lint` and relevant test suites when practical.\n","frontmatter":{"name":"ha-frontend-testing","description":"Home Assistant frontend testing and validation workflow. Use when adding or updating tests, running lint, TypeScript checks, Vitest, Playwright e2e suites, dev servers, or chart-data benchmarks."},"isInternal":false,"tokens":1472,"sizeBytes":6884},{"name":"SKILL.md","path":".agents/skills/ha-frontend-types/SKILL.md","rawUrl":"https://raw.githubusercontent.com/home-assistant/frontend/HEAD/.agents/skills/ha-frontend-types/SKILL.md","title":"ha-frontend-types","category":"anthropic-skill","format":"markdown","content":"---\nname: ha-frontend-types\ndescription: Home Assistant frontend TypeScript conventions. Use when defining or reviewing backend data contracts, optional schemas, shared types, assertions, or Lit lifecycle types.\n---\n\n# HA Frontend Types\n\nUse this skill for Home Assistant-specific TypeScript contracts and type choices.\n\n## Home Assistant Data Contracts\n\nVerify data contracts against the source that owns them, such as Home Assistant Core, Supervisor, a WebSocket handler, or an exported component type. Do not shape a type around assumptions made by its current frontend consumers.\n\n- Match required, optional, nullable, and defaulted fields to the producer and runtime contract. Preserve optional configuration fields when omission is supported and has defined behavior.\n- Use distinct request and response types when their wire shapes differ.\n- When changing a shared contract, check affected consumers, tests, fixtures, and mocks.\n\n## Reuse Home Assistant Contracts\n\n- Reuse the canonical Home Assistant type when one exists. Define shared contract types in the data or API module that owns them.\n- Reuse types exported by components and helpers rather than reconstructing their payloads. For event types, follow `ha-frontend-events`.\n- When one domain contract is used across modules, define and export it from the module that owns that contract.\n\nPrefer an existing owning contract. Introduce a frontend-specific type when the frontend shape or boundary genuinely differs.\n\n## Assertions\n\nPrefer accurate types and runtime narrowing. Use assertions or TypeScript suppressions at boundaries where the runtime invariant is understood but cannot be expressed cleanly; keep them narrow and explain non-obvious invariants.\n\n## Lit Lifecycle Types\n\nFor Lit lifecycle methods that receive changed properties, use `PropertyValues<this>` when the method only needs public reactive properties:\n\n```ts\nprotected willUpdate(changedProperties: PropertyValues<this>) {\n  // ...\n}\n```\n\nUse unparameterized `PropertyValues` when the method inspects private or protected reactive properties, which are not keys of `this`. Do not add assertions solely to retain `PropertyValues<this>`.\n\n## Enforced Baseline\n\nUse `import type` for type-only imports. This is enforced by the repository ESLint configuration.\n","frontmatter":{"name":"ha-frontend-types","description":"Home Assistant frontend TypeScript conventions. Use when defining or reviewing backend data contracts, optional schemas, shared types, assertions, or Lit lifecycle types."},"isInternal":false,"tokens":430,"sizeBytes":2296},{"name":"SKILL.md","path":".agents/skills/ha-frontend-user-facing-text/SKILL.md","rawUrl":"https://raw.githubusercontent.com/home-assistant/frontend/HEAD/.agents/skills/ha-frontend-user-facing-text/SKILL.md","title":"ha-frontend-user-facing-text","category":"anthropic-skill","format":"markdown","content":"---\nname: ha-frontend-user-facing-text\ndescription: Home Assistant frontend copy, localization, terminology, and user-facing text guidance. Use when adding or reviewing labels, buttons, dialogs, errors, translations, or UI strings.\n---\n\n# HA Frontend User-Facing Text\n\nUse this skill for all user-facing text, translations, labels, buttons, dialog copy, errors, helper text, and review comments about wording.\n\n## Localization\n\n- All user-facing text must be translatable.\n- Add translation keys to `src/translations/en.json` when introducing new strings.\n- Use the localization system instead of inline user-visible strings.\n- Prefer complete localized strings with placeholders over concatenating translated fragments.\n- Give translators enough context through key naming and placeholders.\n\n```ts\nthis._localize(\"ui.panel.config.updates.updates_refreshed\", {\n  count: 5,\n});\n```\n\n## Voice And Style\n\n- Use American English.\n- Use a friendly, informational tone.\n- Address users directly with \"you\" and \"your\" when appropriate.\n- Be inclusive, objective, and non-discriminatory.\n- Be concise and clear.\n- Use active voice.\n- Avoid jargon where a familiar home automation term works.\n- Always write \"Home Assistant\" in full. Do not use \"HA\" or \"HASS\" in user-facing copy.\n- Spell out terms when possible.\n- Use sentence case for titles, headings, buttons, labels, and UI elements.\n- Use the Oxford comma in lists.\n- Prefer \"like\" over \"e.g.\" and \"for example\" over \"i.e.\".\n- Avoid all caps for emphasis. Use wording, bold, or italics instead.\n- Write for both technical and non-technical users.\n\nSentence case examples:\n\n- Use: \"Create new automation\"\n- Avoid: \"Create New Automation\"\n- Use: \"Device settings\"\n- Avoid: \"Device Settings\"\n\n## Terminology\n\nUse \"integration\" instead of \"component\" for user-facing product language unless referring to a frontend component in developer context.\n\nTechnical product terms are lowercase in prose: automation, entity, device, service.\n\n## Delete, Remove, Create, Add\n\nUse \"Remove\" for actions that can be restored or reapplied:\n\n- Removing a user's permission.\n- Removing a user from a group.\n- Removing links between items.\n- Removing a widget from a dashboard.\n- Removing an item from a cart.\n\nUse \"Delete\" for permanent, non-recoverable actions:\n\n- Deleting a field.\n- Deleting a value in a field.\n- Deleting a task.\n- Deleting a group.\n- Deleting a permission.\n- Deleting a calendar event.\n\nUse \"Add\" for already-existing items:\n\n- Adding a permission to a user.\n- Adding a user to a group.\n- Adding links between items.\n- Adding a widget to a dashboard.\n- Adding an item to a cart.\n\nUse \"Create\" for something made from scratch:\n\n- Creating a new field.\n- Creating a new task.\n- Creating a new group.\n- Creating a new permission.\n- Creating a new calendar event.\n\nCreate pairs with Delete. Add pairs with Remove.\n\n## Review Checklist\n\n- Text is localized.\n- Copy uses sentence case.\n- \"Home Assistant\" is written in full.\n- Delete/Remove and Create/Add match recoverability and object lifecycle.\n- Placeholders are used instead of string concatenation.\n- The wording is concise and understandable without implementation knowledge.\n","frontmatter":{"name":"ha-frontend-user-facing-text","description":"Home Assistant frontend copy, localization, terminology, and user-facing text guidance. Use when adding or reviewing labels, buttons, dialogs, errors, translations, or UI strings."},"isInternal":false,"tokens":700,"sizeBytes":3178}],"systemPromptSnippet":"<yakaai_skills repo=\"home-assistant/frontend\">\n<!-- File: AGENTS.md (Tokens: ~1003 | Category: root-instruction) -->\n# Home Assistant Frontend Agent Guide\n\nYou are helping develop the Home Assistant frontend. This repository is a TypeScript application built from Lit-based Web Components for the Home Assistant web UI.\n\n## Essential Commands\n\n```bash\npnpm lint          # ESLint + Prettier + TypeScript + Lit\npnpm format        # Auto-fix ESLint + Prettier\npnpm lint:types    # TypeScript compiler, run without file arguments\npnpm test          # Vitest\npnpm dev           # App dev server, supports --background/--status/--stop/--logs\npnpm dev:serve     # Local serving dev server, supports -c core URL, -p port, and dev flags\n```\n\nNever run `tsc` or `pnpm lint:types` with file arguments. When `tsc` receives file arguments, it ignores `tsconfig.json` and can emit `.js` files into `src/`. Always run `pnpm lint:types` without arguments. For individual file type checking, rely on editor diagnostics.\n\n## Architecture\n\n- The frontend uses custom elements built with Lit and TypeScript strict mode.\n- Components communicate with the backend through the Home Assistant WebSocket API.\n- Use `ha-` for Home Assistant components, `hui-` for Lovelace UI components, and `dialog-` for dialogs.\n- Prefer `ha-*` components and current Web Awesome wrappers. Avoid adding new legacy `mwc-*` usage.\n- Leaf components should consume narrow Lit contexts instead of taking the broad `hass` object unless they are containers that own and provide `hass`.\n\n## Development Standards\n\n- Use strict TypeScript, proper interfaces, and `import type` for type-only imports.\n- Avoid `any`; model data with existing Home Assistant types or narrow new types.\n- Keep imports organized and remove unused imports.\n- Do not use `console`; use existing logging or user-visible error patterns.\n- Use `@state()` for internal Lit state and `@property()` for public API.\n- Do not query or manipulate DOM manually when Lit decorators, component refs, or render state are appropriate.\n- Scope styles to components, use theme custom properties, and keep layouts mobile-first and RTL-safe.\n- All user-facing text must be localized through the translation system.\n- Do not write tests just because you changed some code. Write a test when there is real logic that could break without anyone noticing, and explain what the test protects.\n\n## Project Skills\n\nDetailed guidance lives in `.agents/skills/<name>/SKILL.md`. Load every matching skill before detailed implementation or review. For reviews, load `ha-frontend-review` alongside all companions that apply to the changed code or behavior:\n\n- `ha-frontend-contexts`: Lit contexts, `hass` migration, and rerender-sensitive state access.\n- `ha-frontend-components`: dialogs, forms, alerts, shortcuts, tooltips, panels, and Lovelace cards.\n- `ha-frontend-events`: event handler typing, custom event dispatch, and event-map declarations.\n- `ha-frontend-types`: backend data contracts, optional schemas, shared types, assertions, and lifecycle types.\n- `ha-frontend-lit`: reactive fields, DOM queries, lifecycle behavior, and render-derived state.\n- `ha-frontend-styling`: theme variables, spacing tokens, responsive layout, RTL, and view transitions.\n- `ha-frontend-testing`: lint, typecheck, Vitest, Playwright e2e dev servers, and benchmarks.\n- `ha-frontend-query-params`: typed URL query params, `QueryParamsController`, data table URL sync, in-app links, and My Home Assistant redirect params.\n- `ha-frontend-user-facing-text`: localization, terminology, sentence case, and Home Assistant text style.\n- `ha-frontend-review`: PR template use, existing review feedback, review checklist, recurring issues, and UI/UX evidence including gallery specifications.\n- `ha-frontend-ux-readiness`: UI/UX routing that complements frontend review for new user experiences.\n- `ha-frontend-gallery`: gallery pages, demos, sidebar structure, content, and verification.\n- `ha-frontend-demo`: standalone demo structure, configurations, navigation, shared stubs, and verification.\n\n## Pull Requests\n\nWhen creating a pull request, use `.github/PULL_REQUEST_TEMPLATE.md` as the PR body. Preserve template sections, check only the appropriate type-of-change boxes, and do not check checklist items on behalf of the user. If the PR includes UI changes, remind the user to add screenshots or a short video.\n\n## AI policy\n\nThis project follows the [Open Home Foundation AI Policy](AI_POLICY.md).\nAutonomous contributions are not accepted: a human must review, understand,\nand be able to explain every change before it is submitted. Do not open\nissues or pull requests autonomously, and do not post comments on behalf of\na user without their review.\n\n<!-- File: .agents/skills/ha-frontend-components/SKILL.md (Tokens: ~1082 | Category: anthropic-skill) -->\n---\nname: ha-frontend-components\ndescription: Home Assistant frontend component patterns. Use when implementing or reviewing dialogs, ha-form, ha-alert, keyboard shortcuts, tooltips, panels, Lovelace cards, or ha-button usage.\n---\n\n# HA Frontend Components\n\nUse this skill when creating or reviewing Home Assistant UI components and common interaction patterns.\n\nCross-load `ha-frontend-events` when component work includes event listener typing, custom event dispatch, or event-map declarations.\n\n## Dialogs\n\nOpen dialogs through the fire-event pattern:\n\n```ts\nfireEvent(this, \"show-dialog\", {\n  dialogTag: \"dialog-example\",\n  dialogImport: () => import(\"./dialog-example\"),\n  dialogParams: { title: \"Example\", data: someData },\n});\n```\n\nDialog implementation requirements:\n\n- Use `ha-dialog`.\n- Use `DialogMixin`, which implements `HassDialogNext<T>`, for new dialogs. See `src/dialogs/dialog-mixin.ts`.\n- Read dialog parameters from the mixin's `params` property and render the dialog open. Return `nothing` while required parameters are absent.\n- Call the mixin's `closeDialog()` to close a new dialog. The mixin handles the `closed` event, fires `dialog-closed`, and removes the host element.\n- Existing dialogs may implement the legacy `HassDialog<T>` interface from `src/dialogs/make-dialog-manager.ts`.\n- Preserve the existing `showDialog()`, open-state, and close-event lifecycle when maintaining a legacy dialog; do not copy that lifecycle into a `DialogMixin` dialog.\n- Use `header-title` and `header-subtitle` for simple header text.\n- Use slots when standard header attributes are not enough.\n- Use `ha-dialog-footer` with `primaryAction` and `secondaryAction` slots.\n- Add `autofocus` to the first focusable element, such as `<ha-form autofocus>`, and forward it internally if needed.\n\nUse standard dialog widths: `small`, `medium`, `large`, or `full`. Avoid custom dialog sizing unless there is a clear product need.\n\n## Buttons\n\n`ha-button` wraps the Web Awesome button in `src/components/ha-button.ts`.\n\nAxes:\n\n- `variant`: `brand`, `neutral`, `danger`, `warning`, `success`.\n- `appearance`: `accent`, `filled`, `outlined`, `plain`.\n- `size`: `xs`, `s`, `m`, `l`, `xl`.\n\nCommon usage:\n\n- Use `appearance=\"filled\"` for primary emphasis when needed.\n- Use `appearance=\"plain\"` for cancel and dismiss actions.\n- Use `variant=\"danger\"` for destructive actions.\n- Place primary actions in `slot=\"primaryAction\"` and secondary actions in `slot=\"secondaryAction\"`.\n\n## Forms\n\n`ha-form` is schema-driven with `HaFormSchema[]` and supports common selectors for entities, devices, areas, targets, numbers, booleans, time, actions, text, objects, selects, icons, media, and location.\n\nUse `computeLabel`, `computeError`, and `computeHelper` for translated labels, validation, and helper text.\n\n```ts\n<ha-form\n  .hass=${this.hass}\n  .data=${this._data}\n  .schema=${this._schema}\n  .error=${this._errors}\n  .computeLabel=${(schema) => this._localize(`ui.panel.${schema.name}`)}\n  @value-changed=${this._valueChanged}\n></ha-form>\n```\n\n## Alerts\n\nUse `ha-alert` for user-visible status messaging.\n\n- Alert types: `error`, `warning`, `info`, `success`.\n- Useful properties: `title`, `alert-type`, `dismissable`, `narrow`.\n- Slots: `icon` for custom leading icon, `action` for custom action content.\n- Content is announced by screen readers when dynamically displayed.\n\n```ts\nhtml`\n  <ha-alert alert-type=\"error\">${this._localize(\"ui.example.error\")}</ha-alert>\n  <ha-alert alert-type=\"warning\" .title=${this._localize(\"ui.example.warning\")}>\n    ${this._localize(\"ui.example.description\")}\n  </ha-alert>\n  <ha-alert alert-type=\"success\" dismissable>\n    ${this._localize(\"ui.example.success\")}\n  </ha-alert>\n`;\n```\n\n## Shortcuts And Tooltips\n\nUse `ShortcutManager` from `src/common/keyboard/shortcuts.ts` for keyboard shortcuts. It blocks shortcuts in input fields, can prevent shortcuts during text selection, and supports character and KeyCode shortcuts for non-latin keyboards. See `src/state/quick-bar-mixin.ts` for global shortcut examples.\n\nUse `ha-tooltip` from `src/components/ha-tooltip.ts` for contextual hover help. See `src/components/ha-label.ts` for an example.\n\n## Panels And Lovelace Cards\n\nPanels commonly extend `SubscribeMixin(LitElement)` and receive route and narrow-layout properties.\n\nLovelace cards should implement `LovelaceCard`, validate config in `setConfig()`, handle loading, error, unavailable, and missing-entity states, and add a configuration editor when needed.\n\nCards are user-story surfaces. Support different households, entity types, responsive layouts, and accessible interaction states.\n\n<!-- File: .agents/skills/ha-frontend-contexts/SKILL.md (Tokens: ~1427 | Category: anthropic-skill) -->\n---\nname: ha-frontend-contexts\ndescription: Home Assistant frontend Lit context and hass migration guidance. Use when adding or changing component state access, replacing hass reads, consuming entity or registry contexts, or reviewing rerender behavior.\n---\n\n# HA Frontend Contexts\n\nUse this skill when a component reads Home Assistant state, registries, localization, services, config, UI data, connection state, or API helpers.\n\n## Goal\n\nMove leaf components away from the broad `hass: HomeAssistant` object. Broad `hass` access rerenders components for unrelated changes, hides the data a component depends on, and makes tests harder to mock.\n\nContainer components may keep `hass` when they own it and feed providers. Leaf components should consume the narrowest context that covers their reads.\n\n## Core Files\n\n- Context definitions: `src/data/context/index.ts`\n- `consume` decorator, `ContextSubscriptionController`, and `consumeContext` / `ContextController` for reactive controllers: `src/common/decorators/consume.ts` (use instead of `@lit/context`'s `consume` and `ContextConsumer`, enforced by ESLint)\n- Entity-scoped consume helpers: `src/common/decorators/consume-context-entry.ts`\n- Transform decorator: `src/common/decorators/transform.ts`\n- Canonical migration example: `src/panels/lovelace/cards/hui-button-card.ts`\n- Providers are wired by `contextMixin` on `HassBaseEl`; consumers do not wire providers manually.\n\n## Context Selection\n\n| Context                                                              | Replaces                                                                                                  |\n| -------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------- |\n| `statesContext`                                                      | `hass.states`                                                                                             |\n| `entitiesContext`, `devicesContext`, `areasContext`, `floorsContext` | `hass.entities`, `hass.devices`, `hass.areas`, `hass.floors`                                              |\n| `registriesContext`                                                  | all four registries together                                                                              |\n| `servicesContext`                                                    | `hass.services`                                                                                           |\n| `internationalizationContext`                                        | `hass.localize`, `hass.locale`, `hass.language`                                                           |\n| `formattersContext`                                                  | entity and attribute formatters                                                                           |\n| `configContext`                                                      | `hass.config`, `hass.user`, `hass.auth`, `hass.userData`                                                  |\n| `connectionContext`                                                  | `hass.connection`, `hass.connected`, `hass.debugConnection`, `hass.hassUrl`                               |\n| `apiContext`                                                         | `hass.callService`, `hass.callApi`, `hass.callApiRaw`, `hass.callWS`, `hass.sendWS`, `hass.fetchWithAuth` |\n| `uiContext`                                                          | themes, selected theme, panels, sidebar, and UI state                                                     |\n| `narrowViewportContext`                                              | narrow-layout boolean                                                                                     |\n| `reducedMotionContext`                                               | `prefers-reduced-motion` media query                                                                      |\n\nLazy contexts subscribe on first consumer and tear down after the last consumer: `labelsContext`, `fullEntitiesContext`, `configEntriesContext`, `manifestsContext`, `triggerDescriptionsContext`, and `conditionDescriptionsContext`.\n\nThe single-field contexts such as `localizeContext`, `themesContext`, and `userContext` are deprecated. Use grouped contexts instead.\n\n## Consumption Patterns\n\nImport `consume` from `src/common/decorators/consume`, not from `@lit/context`. The `@lit/context` version forces a host update on every context change, so every consumed field rerenders the component, even without `@state()`, and `@transform` cannot skip anything. Our `consume` leaves update scheduling to the field:\n\n- `@state()` plus `@consume`: rerenders when the context value changes.\n- `@state()` plus `@consume` plus `@transform`: rerenders only when the transformed value changes.\n- `@consume` alone: the field stays current but never triggers a render. Use this for values only read in event handlers or callbacks, such as `apiContext`, or for data read on demand, such as picker item callbacks.\n\nIn reactive controllers, extend `ContextController` and decorate fields with `consumeContext({ context, subscribe, transform })` from the same file. `transform` stores one part of the context value, and `contextUpdated()` runs only when that part changes. `@state()` and `@transform` do not work on controller fields; call `host.requestUpdate()` when the host needs to rerender. Example: `src/data/service-info-controller.ts`.\n\nFor lazily created subscriptions, use `ContextSubscriptionController` from the same file instead of `@lit/context`'s `ContextConsumer`, which also forces a host update. Its callback must store the value in a reactive field or call `host.requestUpdate()` itself when the host needs to rerender.\n\nUse entity-scoped helpers when the component watches an entity ID held on the host:\n\n```ts\n@state() @consumeEntityState({ entityIdPath: [\"_config\", \"entity\"] })\nprivate _stateObj?: HassEntity;\n\n@state() @consumeEntityRegistryEntry({ entityIdPath: [\"_config\", \"entity\"] })\nprivate _entity?: EntityRegistryDisplayEntry;\n\n@state() @consumeLocalize()\nprivate _localize!: LocalizeFunc;\n```\n\nUse `consumeEntityStates` when the host property contains one or more entity IDs. It filters missing entities and preserves the previous record when none of the selected entities changed.\n\n```ts\n@state() @consumeEntityStates({ entityIdPath: [\"_config\", \"entities\"] })\nprivate _stateObjs?: Record<string, HassEntity>;\n```\n\nFor a single field from a grouped context, pair `@consume` with `@transform`:\n\n```ts\n@state()\n@consume({ context: uiContext, subscribe: true })\n@transform<HomeAssistantUI, Themes>({ transformer: ({ themes }) => themes })\nprivate _themes!: Themes;\n```\n\nUse `@transform` with `watch` when the transformer depends on a host property, such as a computed entity ID. `consumeEntityState` and `consumeEntityStates` only watch the first path segment.\n\nTo consume a whole group untransformed, omit `@transform` and type the field as `ContextType<typeof statesContext>` or the matching context type.\n\n## Review Checklist\n\n- The component consumes the narrowest context needed for the data it reads.\n- A broad `hass` property is kept only when the component is a container or external API requires it.\n- Entity-scoped reads use the consume helpers rather than ad hoc context transforms.\n- Context fields read during render or `willUpdate` are marked `@state()` so updates trigger rendering. Fields only read outside the render cycle are left without `@state()`.\n- Tests and mocks only provide the data the component actually consumes.\n\n<!-- File: .agents/skills/ha-frontend-demo/SKILL.md (Tokens: ~965 | Category: anthropic-skill) -->\n---\nname: ha-frontend-demo\ndescription: Home Assistant standalone demo structure, configurations, URL navigation, mocked APIs, shared demo stubs used by gallery, and verification. Use when changing files under demo/, changing gallery consumers of demo/src/stubs/, or running and validating the demo.\n---\n\n# HA Frontend Demo\n\nUse this skill for standalone demo work and changes to shared demo stubs consumed by the gallery. Follow the persistent repository guidance in `AGENTS.md` and load matching specialist skills alongside this skill, especially `ha-frontend-testing` when running or validating the demo.\n\n## Purpose\n\nThe demo is the full Home Assistant frontend running against a mocked backend and is published at <https://demo.home-assistant.io>. It needs no Home Assistant server, which makes it the easiest way to load the real UI in a browser, for example to take screenshots.\n\n## Running The Demo\n\nRun commands from the repository root:\n\n```bash\npnpm dev:demo               # Development server on http://localhost:8090\npnpm dev:demo --background  # Detached; also supports --status/--stop/--logs\n```\n\nUse the E2E workflows documented in `ha-frontend-testing` when validating the demo.\n\n## Opening A Specific Demo\n\nThe demo contains multiple demo configurations. Select one directly with the `demo` query parameter, for example `http://localhost:8090/?demo=<slug>`. The valid slugs are defined in `demoConfigs` in `demo/src/configs/demo-configs.ts`, so \"the second demo\" means the slug of the second entry in that list. An unknown slug falls back to the default, which is the first entry.\n\n## Opening A Specific Page\n\nThe demo build uses hash-based routing: the frontend path goes in the URL hash, and the `demo` query parameter goes before the `#`. The URL format is:\n\n```text\nhttp://localhost:8090/?demo=<slug>#/<path>\n```\n\nUseful paths:\n\n- `/lovelace/0`: The selected demo's dashboard, also the default when no hash is given.\n- `/energy`: The energy dashboard. Its tabs are views: `/energy/overview` (Summary), `/energy/electricity`, `/energy/gas`, `/energy/water`, and `/energy/now`.\n- `/map`, `/history`, `/todo`, `/config`: Other sidebar panels.\n\nFor example, the water tab of the energy dashboard of the second demo is:\n\n```text\nhttp://localhost:8090/?demo=<second slug>#/energy/water\n```\n\n## Structure\n\n- `demo/src/ha-demo.ts`: Root element that sets up all backend mocks.\n- `demo/src/configs/<slug>/`: One directory per demo configuration, containing entities, dashboard, and theme data.\n- `demo/src/configs/demo-configs.ts`: Registry of demo configurations and URL slug handling.\n- `demo/src/stubs/`: Mocked WebSocket and REST APIs.\n- `demo/script/develop_demo`, `demo/script/build_demo`: Development server and static build wrappers.\n\n## Core Data\n\nThe service descriptions in `src/fake_data/demo_services.ts` and the trigger and condition descriptions and icons in `demo/src/stubs/automation_platforms_*.ts` are generated from Home Assistant Core. Do not edit them by hand. Run `script/gen_demo_core_data` to regenerate them from the core `dev` branch, and add a domain to the lists in `build-scripts/gulp/gen-demo-core-data.js` when the demo needs its services, triggers, or conditions. Their translations come from the backend translations merged into the demo build.\n\n## Shared Gallery Stubs\n\n`demo/src/stubs/` is shared, not demo-private: gallery pages import from it directly. Before changing or removing anything a stub does, search for its callers across `demo/src/` and `gallery/src/`, then check the affected gallery pages as well as the demo.\n\nThe two consumers use a stub differently, so demo behavior does not predict gallery behavior. A gallery page calls stubs against the `hass` from `provideHass`, where `hass.config` is the shared `demoConfig` object. A stub that mutates `hass.config` in place is therefore visible to the page. `demo/src/ha-demo.ts` copies `components` into a new array before the stubs run, so the same mutation never reaches the demo. A change can look correct in the demo while quietly breaking a gallery page.\n\n## Verification\n\nLoad `ha-frontend-testing` and use its demo and gallery workflows. Validate both consumers when changing shared stubs. Documentation-only changes do not require code tests unless examples or commands change.\n\n<!-- File: .agents/skills/ha-frontend-events/SKILL.md (Tokens: ~995 | Category: anthropic-skill) -->\n---\nname: ha-frontend-events\ndescription: Home Assistant frontend event patterns. Use when typing event handlers, using HASSDomEvent types, dispatching with fireEvent, or declaring HASSDomEvents and event maps.\n---\n\n# HA Frontend Events\n\nUse this skill when implementing or reviewing event listeners and custom event contracts. Cross-load `ha-frontend-components` when the work also involves dialogs, forms, alerts, shortcuts, tooltips, panels, Lovelace cards, or buttons.\n\n## Event Handling\n\nUse the event types from `src/common/dom/fire_event.ts` instead of plain `Event`, generic `CustomEvent`, or element casts when they express the handler contract:\n\n- Use `HASSDomCurrentTargetEvent<T>` to read the element on which the listener was registered through `ev.currentTarget`.\n- Use `HASSDomTargetEvent<T>` only to read the element that originated the event through `ev.target`.\n- Use `HASSDomEvent<T>` to read a custom event payload through `ev.detail`.\n- Use `ValueChangedEvent<T>` from `src/types.ts` for the standard `value-changed` event.\n- Prefer an event type exported by the component being listened to, such as `HaSelectSelectEvent<T, Clearable>` or `HaDropdownSelectEvent<TValue, TData>`, over reconstructing its detail type.\n- Use `ActionHandlerEvent` from `src/data/lovelace/action_handler.ts` for Lovelace tap, hold, and double-tap handlers.\n\nImport event and element types with `import type`:\n\n```ts\nimport type {\n  HASSDomCurrentTargetEvent,\n  HASSDomEvent,\n  HASSDomTargetEvent,\n} from \"../common/dom/fire_event\";\nimport type { HaCheckbox } from \"../components/ha-checkbox\";\nimport type { HaEntityPicker } from \"../components/entity/ha-entity-picker\";\nimport type { HaRadioGroup } from \"../components/radio/ha-radio-group\";\nimport type { ValueChangedEvent } from \"../types\";\n```\n\nType the handler so the selected property can be read directly. Do not cast `ev.currentTarget` or assign it to a single-use variable:\n\n```ts\nprivate _scopeChanged(ev: HASSDomCurrentTargetEvent<HaRadioGroup>): void {\n  this._scope = ev.currentTarget.value;\n}\n\nprivate _checkedChanged(ev: HASSDomTargetEvent<HaCheckbox>): void {\n  this._checked = ev.target.checked;\n}\n\nprivate _valueChanged(ev: ValueChangedEvent<string>): void {\n  this._value = ev.detail.value;\n}\n\nprivate _itemSelected(ev: HASSDomEvent<{ id: string }>): void {\n  this._selectedId = ev.detail.id;\n}\n```\n\nUse intersections when a handler needs more than one facet of an event. Keep the native event type when the handler reads native fields such as `key`, modifier keys, `dataTransfer`, or focus relationships:\n\n```ts\nprivate _entityChanged(\n  ev: ValueChangedEvent<string> & HASSDomCurrentTargetEvent<HaEntityPicker>\n): void {\n  ev.currentTarget.value = ev.detail.value;\n}\n\nprivate _keyDown(\n  ev: KeyboardEvent & HASSDomCurrentTargetEvent<HTMLInputElement>\n): void {\n  if (ev.key === \"Enter\") {\n    this._submit(ev.currentTarget.value);\n  }\n}\n```\n\nDispatch Home Assistant component events with `fireEvent()` instead of constructing `Event` or `CustomEvent` directly. Register the event name and detail type by augmenting `HASSDomEvents`; use `undefined` when an event has no detail. `fireEvent()` constrains event names and supplied detail, and events bubble and cross shadow boundaries by default:\n\n```ts\nfireEvent(this, \"item-selected\", { id: item.id });\nfireEvent(this, \"refresh-requested\");\n```\n\nWhen an event is already registered, derive handler and listener types from its registration rather than repeating the payload shape:\n\n```ts\nprivate _itemSelected(\n  ev: HASSDomEvent<HASSDomEvents[\"item-selected\"]>\n): void {\n  this._selectedId = ev.detail.id;\n}\n```\n\n`HASSDomEvents` types `fireEvent()` calls. Augment `HTMLElementEventMap` for typed listeners on HTML elements, or `GlobalEventHandlersEventMap` when the event is handled on global event targets.\n\nIn component files, prefer placing global event declarations after the class at the bottom of the file. Preserve the existing placement when editing established files; foundational type, helper, and mixin files commonly keep declarations near the top before their consumers.\n\n```ts\ndeclare global {\n  interface HASSDomEvents {\n    \"item-selected\": { id: string };\n    \"refresh-requested\": undefined;\n  }\n\n  interface HTMLElementEventMap {\n    \"item-selected\": HASSDomEvent<HASSDomEvents[\"item-selected\"]>;\n  }\n}\n```\n\n<!-- File: .agents/skills/ha-frontend-gallery/SKILL.md (Tokens: ~1688 | Category: anthropic-skill) -->\n---\nname: ha-frontend-gallery\ndescription: Home Assistant frontend gallery structure, pages, demos, content, and verification. Use when changing files under gallery/, including gallery markdown, TypeScript demos, sidebar entries, mock data, page generation, or gallery builds.\n---\n\n# HA Frontend Gallery\n\nUse this skill for all work under `gallery/`. Follow the persistent repository guidance in `AGENTS.md` and load the matching specialist skills alongside this gallery-specific guidance.\n\n## Quick Reference\n\nRun commands from the repository root unless noted otherwise:\n\n```bash\ngallery/script/develop_gallery  # Start the gallery development server\ngallery/script/build_gallery    # Build the static gallery\npnpm lint                       # ESLint, Prettier, TypeScript, and Lit checks\npnpm lint:types                 # TypeScript compiler, without file arguments\n```\n\nNever run `pnpm lint:types` or `tsc` with file arguments. File arguments make `tsc` ignore `tsconfig.json` and can emit `.js` files into `src/`.\n\n## Purpose\n\nThe gallery is a developer and designer reference for Home Assistant frontend UI patterns. It documents component APIs, shows realistic Lovelace and more-info states, captures brand and copy guidance, and provides reproducible demos that are safe to inspect outside a running Home Assistant instance.\n\n- Prefer demonstrating real production components from `src/` instead of creating gallery-only replacements.\n- Keep fake state, sample data, and demo-only helpers inside `gallery/`.\n- Do not move gallery stubs or demo data into production code unless a production feature explicitly needs them.\n- Do not hand-edit generated output under `gallery/build/` or `gallery/dist/`.\n\n## Structure\n\n- `gallery/sidebar.js`: Defines gallery sections, headers, and explicit page ordering.\n- `gallery/script/develop_gallery`: Wrapper for the `develop-gallery` gulp task.\n- `gallery/script/build_gallery`: Wrapper for the `build-gallery` gulp task.\n- `gallery/src/entrypoint.js`: Creates the `<ha-gallery>` shell.\n- `gallery/src/ha-gallery.ts`: Renders the drawer, page routing, markdown descriptions, demos, edit links, and RTL toggle.\n- `gallery/src/html/index.html.template`: HTML template used by the gallery build.\n- `gallery/src/pages/<category>/<page>.markdown`: Optional page description and frontmatter.\n- `gallery/src/pages/<category>/<page>.ts`: Optional live demo module for the same page ID.\n- `gallery/src/components/`: Gallery-only demo wrappers like `demo-card`, `demo-cards`, `demo-more-info`, and `page-description`.\n- `gallery/src/data/`: Fake `hass`, demo states, mock traces, and reusable sample data.\n- `gallery/public/`: Static assets copied into the gallery output.\n\n## Page Model\n\nGallery pages are generated by `gather-gallery-pages` in `build-scripts/gulp/gallery.js`.\n\n- A page ID is the path under `gallery/src/pages/` without the extension, like `components/ha-button`.\n- A `.markdown` file and a `.ts` file with the same page ID become one gallery page.\n- A page may have only markdown, only a TypeScript demo, or both.\n- Markdown can contain YAML frontmatter with `title` and optional `subtitle`.\n- Markdown that contains only frontmatter contributes metadata without rendering a description block.\n- TypeScript demo modules are dynamically imported for side effects when the page is opened.\n- A demo module must define a custom element named `demo-${category}-${page}` with slashes replaced by hyphens, like `demo-components-ha-button` for `components/ha-button`.\n- `gallery/src/ha-gallery.ts` renders that element with `dynamicElement()` based on the current page ID.\n\n## Sidebar\n\nUse `gallery/sidebar.js` when a page needs a visible section, section header, or deterministic ordering.\n\n- `category` must match the first directory name under `gallery/src/pages/`.\n- `header` is the section label shown in the drawer.\n- `pages` is optional. When present, listed pages keep that exact order.\n- Pages in a category that are not listed are appended alphabetically after the listed pages.\n- New categories without a sidebar entry are appended by the generator with their category name as the header.\n- If a listed page does not exist, the generator logs an error during `gather-gallery-pages`.\n\n### Subsections\n\nA section can group its pages under named subsections instead of one flat list. Use this for large categories where related pages should sit together.\n\n- `subsections` is an array of `{ header, pages }`. It is mutually exclusive with a flat `pages` array on the same group.\n- Each subsection `header` is a non-collapsible label rendered inside the section's expansion panel; the section stays the only collapsible level.\n- Listed pages keep their per-subsection order.\n- Any pages found in the category but not listed in a subsection are collected into a generated `Other` subsection, appended alphabetically. The `Other` subsection is omitted when there are no leftovers.\n- A listed page that does not exist still logs an error during `gather-gallery-pages`.\n- Use sentence case for subsection headers and follow the content standards below.\n\n## Markdown Pages\n\nUse markdown pages for explanations, design guidance, API notes, and copy standards.\n\n- Start with frontmatter when the page needs a title or subtitle.\n- Use sentence case for titles, headings, labels, and UI copy.\n- Put the live example before the reference API when that makes the page easier to scan.\n- Use fenced code blocks with a language tag for copyable examples.\n- Keep examples short and focused on the behavior being documented.\n- Prefer real component names and attributes over prose-only descriptions.\n- Use Home Assistant terminology from `ha-frontend-user-facing-text`.\n- For remove/delete and add/create wording, follow `gallery/src/pages/misc/remove-delete-add-create.markdown`.\n\nGallery markdown is documentation content and is not localized with `localize`. If demo code creates production UI strings, follow the localization and copy guidance in `ha-frontend-user-facing-text`.\n\n## Demo Components\n\nUse TypeScript demo pages for interactive or stateful examples.\n\n- Import production components from `src/` using the correct relative path from the demo file.\n- Import reusable gallery helpers from `gallery/src/components/` when they already model the pattern.\n- Use `demo-card` and `demo-cards` for Lovelace card examples that render YAML card configs.\n- Use `demo-more-info` and `demo-more-infos` for more-info dialog examples.\n- Use shared mock data from `gallery/src/data/` instead of repeating large fake state objects inline.\n- Show meaningful states, such as loading, unavailable, empty, error, active, inactive, and disabled when relevant.\n- Check responsive behavior and the gallery RTL toggle when layout or direction-sensitive UI changes.\n- Keep unavoidable casts or loose demo parsing local to the demo helper or demo page.\n\nThe gallery ESLint config allows `console` for gallery diagnostics. Do not copy that exception into production frontend code.\n\n## Content Standards\n\nFollow the detailed copy standards in `ha-frontend-user-facing-text`: use American English, sentence case, active voice, inclusive language, direct user-focused wording, and consistent Home Assistant terminology.\n\n- Use `Home Assistant` in full, not `HA` or `HASS`.\n- Use `integration` instead of `component` for product concepts.\n- Use `Remove` for reversible disassociation and `Delete` for permanent deletion.\n- Use `Add` for existing items and `Create` for something made from scratch.\n- Avoid Latin abbreviations like `e.g.` and `i.e.` in prose.\n- Avoid stitching sentence fragments together in production UI examples.\n\n## Verification\n\n- For markdown, sidebar, and page-generation changes, run `gallery/script/build_gallery`.\n- For TypeScript demo or gallery shell changes, run the smallest relevant check plus `pnpm lint` when practical.\n- For type checking, run `pnpm lint:types` without file arguments.\n- For visual changes, run `gallery/script/develop_gallery` and check the affected page on desktop, narrow viewport, and RTL when relevant.\n- If verification is skipped, state which command was skipped and why.\n\n<!-- File: .agents/skills/ha-frontend-lit/SKILL.md (Tokens: ~536 | Category: anthropic-skill) -->\n---\nname: ha-frontend-lit\ndescription: Home Assistant frontend Lit conventions. Use when working with reactive properties, internal state, DOM queries, lifecycle methods, or render-derived state.\n---\n\n# HA Frontend Lit\n\nUse this skill when implementing or reviewing Lit component state, DOM access, lifecycle methods, or rendering behavior. Cross-load `ha-frontend-types` for Home Assistant data contracts, assertions, and lifecycle parameter types.\n\n## Reactive Fields\n\nThis project currently uses Lit's TypeScript experimental decorators with `useDefineForClassFields: false`. Match existing declarations and do not introduce standard-decorator `accessor` syntax unless the project changes decorator mode.\n\n- Use `@property()` for public reactive API and `@state()` for private reactive state.\n- Prefer inferred types for initialized reactive fields when inference preserves the intended type; annotate when widening or an external contract requires it.\n\n## DOM Queries\n\nPrefer Lit's `@query()` or `@queryAll()` decorators for fixed selectors in the component's render root.\n\n- Type the decorated field with the narrowest useful DOM or component interface.\n- Keep the field optional when it may be absent at the point of access, including conditional rendering or pre-render lifecycle access.\n- Use a definite assignment assertion only when every call site runs after the node is guaranteed to exist.\n- The optional second argument to `@query()`, as in `@query(\"#target\", true)`, caches the first query result. Use it only when later renders cannot replace the queried node.\n- Use a direct query when the selector is dynamic or the target is outside the component's render root. Before querying a child, consider whether the required value belongs in parent state or data flow.\n\n## Render-Derived State\n\n- Prefer render-local values for inexpensive structures used only by that render.\n- Assign a render-local value once when repeated evaluation is non-trivial or a local name improves clarity.\n- Keep purely presentational derivations in `render()`. Use stored state or `willUpdate()` when the value must participate in lifecycle work, reflection, CSS, or non-render consumers.\n- Use `memoizeOne` for pure, argument-derived transforms when stable input identity avoids meaningful repeated work. Keep inputs explicit and limited, and do not add caching without a credible benefit over computing the value directly.\n\n## References\n\n- [Reactive properties](https://lit.dev/docs/components/properties/)\n- [Decorators](https://lit.dev/docs/components/decorators/)\n- [Shadow DOM queries](https://lit.dev/docs/components/shadow-dom/#query)\n- [Reactive update cycle](https://lit.dev/docs/components/lifecycle/)\n\n<!-- File: .agents/skills/ha-frontend-review/SKILL.md (Tokens: ~1641 | Category: anthropic-skill) -->\n---\nname: ha-frontend-review\ndescription: Home Assistant frontend PR and review guidance, including implementation quality, existing review feedback, established design patterns, and the authority of UI/UX evidence. Use when reviewing frontend changes, preparing a PR, checking recurring review issues, or applying the PR template.\n---\n\n# HA Frontend Review\n\nUse this skill when reviewing Home Assistant frontend changes or preparing a pull request.\n\n## Review Preparation\n\n1. Establish the review scope from the diff and the behavior changed by it.\n2. Before identifying findings, use the [project skill catalogue](../../../AGENTS.md#project-skills) to load every matching companion's `SKILL.md`. Follow applicable companion references in those skills as well; this review skill supplies the workflow, and companions supply the domain-specific criteria.\n3. Apply all loaded guidance within that scope. Report only problems introduced or worsened by the changeset, anchored to changed lines. Read surrounding code to understand affected behavior, not to generate unrelated cleanup or migration requests.\n\n## Pull Request Body\n\nWhen creating a pull request, use `.github/PULL_REQUEST_TEMPLATE.md` as the body.\n\n- Do not omit, reorder, or rewrite template sections.\n- Check the appropriate \"Type of change\" box based on the actual change.\n- Do not check checklist items on behalf of the user.\n- If the PR includes UI changes, remind the user to add screenshots or a short video.\n- Explain what the change does for users, not only implementation details.\n- Use Markdown.\n\n## Pre-Submission Checklist\n\n- `pnpm lint` passes when practical for the scope.\n- `pnpm test` or focused relevant tests are green when practical for the scope.\n- Each test added by the change protects real logic, not the look of a component.\n- User-facing text is localized and follows `ha-frontend-user-facing-text` guidance.\n- Components handle loading, error, unavailable, and missing-entity states.\n- Entity existence is checked before property access.\n- Event listeners and subscriptions are cleaned up.\n- UI is accessible to screen readers and keyboard users.\n\n## Recurring Review Issues\n\nScope and public surface:\n\n- Keep changes independently reviewable and limited to the requested area.\n- Prefer existing Home Assistant helpers, Lit primitives, and component seams over parallel implementations.\n- Challenge new public properties and optional feature surface when transient options or existing seams meet the requirement with less lifecycle and consistency cost.\n\nStateful and asynchronous UI:\n\n- Review transitions in both directions, not only individual rendered states.\n- When controls reappear, restore valid defaults instead of retaining state that was only valid while they were hidden.\n- Establish immutable dirty-state baselines before asynchronous work, guard against stale responses, and preserve unsaved state in mounted editors.\n- Determine an action's current meaning before applying dirty-state checks, especially when an action can change between Save and Close.\n\nReadiness and invalidation:\n\n- Treat readiness as the first displayable terminal result, including stable empty and error states.\n- Register child readiness before resolving the parent, do not treat fallback work as terminal, and replay readiness correctly for cached or reused panels.\n- Ensure every value read by memoized output participates in its invalidation.\n\nRepository-owned contracts:\n\n- Consult the public [frontend contributor documentation](https://contributors.home-assistant.io/frontend/) for documented architecture, data flow, design, and development workflows.\n- For new leaf components, load `ha-frontend-contexts` and verify they consume narrow contexts instead of introducing a broad `hass` property; containers and external APIs may still require `hass`.\n- Verify backend assumptions against the owning Core, Supervisor, or WebSocket implementation, and component assumptions against the exported component contract.\n- Prefer canonical repository helpers and test setup over duplicate local implementations.\n- Promote AI-review concerns into durable guidance only when supported by code evidence, reproduced behavior, an accepted corrective commit, or human-maintainer validation.\n\nUser experience and accessibility:\n\n- Forms need proper labels, helper text, and validation feedback.\n- Form markup should not cause password managers to identify fields incorrectly.\n- Clickable areas should be large enough for touch interaction.\n- Hover, active, disabled, loading, and focus states should be clear.\n\nDialog and modal patterns:\n\n- Multi-step operations should show progress.\n- Dialog state should survive background operations correctly.\n- Cancel and close buttons should behave consistently.\n- Defaults should be helpful without blocking user override.\n\nComponent design patterns:\n\n- Terminology should be consistent. Use words like \"Join\" or \"Apply\" instead of \"Group\" when that better matches the user action.\n- Visual hierarchy should use appropriate font sizes, weights, and spacing ratios.\n- Components should align to the design grid.\n- Badges and indicators should be placed consistently.\n\nCode quality:\n\n- Null and undefined paths should be handled explicitly.\n- Potentially undefined array and object access should be guarded.\n- Event handlers, timers, observers, and subscriptions should be cleaned up.\n\nConfiguration and props:\n\n- Make configuration fields optional when sensible.\n- Provide reasonable defaults.\n- Keep APIs extensible without adding speculative abstractions.\n- Validate configuration before applying changes.\n\n## UI/UX Evidence\n\nFor user-facing changes, establish the existing design context as part of frontend review:\n\n- Treat an applicable gallery specification as the authoritative repository source for the documented component or interaction. Inspect its written guidance and demos when present.\n- Note relevant production designs, approved designs, and shared `ha-*` components so the implementation can also be compared with established frontend behaviour.\n- Require the applicable gallery documentation or demo to change when the implementation intentionally changes behaviour documented there.\n- Prefer an established input layout or appropriate shared component over a raw input or ad hoc control.\n- Treat explicit UI/UX approval, current repository guidance, and direct human or workflow instructions as stronger evidence than inference.\n- Treat linked tasks as evidence of the problem and stated requirements, not automatic UI/UX approval.\n- When the direction remains uncertain, search frontend pull requests that carry or previously carried **Needs UX**. Inspect their label history, comments, and reviews rather than relying on the current label alone.\n- Judge relevant feedback by its content and surrounding discussion. When GitHub provides `author_association`, prioritise feedback marked `MEMBER`.\n- Prefer recent feedback about similar interactions or components. Label removal can show that a workflow gate moved on, but does not prove approval without the surrounding discussion.\n- Summarise the applicable guidance instead of maintaining a reviewer list or treating one historical decision as a permanent rule.\n\n## Review Flow\n\n- Before reviewing a pull request, read its existing comments, reviews, and threads, including their status, resolver, and Copilot resolution reason when available.\n- Prioritise substantive human feedback, especially from authors marked `MEMBER`, and validate agent-generated feedback against the code and repository guidance.\n- Do not duplicate unresolved findings as new inline comments; reference any that still need action in the review summary. Treat resolved feedback as closed only when the resolution reason or surrounding discussion supports that outcome; otherwise validate it against the current code before suppressing it. Respect **Won't fix** and **Incorrect** reasons.\n- When a later commit addresses an unresolved thread, note in the review summary that it looks addressed, linking the thread and the commit, so it can be resolved. Do not report it as still open.\n- Identify behavioral regressions, bugs, accessibility issues, and missing tests that `ha-frontend-testing` calls for first.\n- Do not ask for new tests on visual components. If the visuals clearly changed and the PR has no screenshots or videos, suggest adding them instead.\n- When the change adds something repository guidance rules out, such as a rendering test, open the comment with the removal (\"Remove this test file\") and cite the guidance in one sentence. Do not critique how the unwanted code is written, as that reads as a request to fix it.\n- Record the applicable UI/UX evidence for user-facing changes, whether or not further input is needed.\n- Keep style-only comments secondary unless they affect maintainability or user experience.\n- Prefer small, direct fixes over large refactors during review follow-up.\n\n<!-- File: .agents/skills/ha-frontend-styling/SKILL.md (Tokens: ~711 | Category: anthropic-skill) -->\n---\nname: ha-frontend-styling\ndescription: Home Assistant frontend styling, theming, spacing, responsive layout, RTL, and View Transitions guidance. Use when editing CSS, layout, motion, or visual component structure.\n---\n\n# HA Frontend Styling\n\nUse this skill when editing CSS, layout, visual hierarchy, theme integration, responsive behavior, RTL support, or view transitions.\n\n## Theme And Layout Basics\n\n- Use Home Assistant CSS custom properties instead of hardcoded colors.\n- Use `--ha-space-*` spacing tokens instead of hardcoded spacing where possible.\n- Keep components mobile-first and enhance for desktop.\n- Keep layouts RTL-safe. Prefer logical properties when they fit.\n- Prefer `ha-*` components and current Web Awesome wrappers.\n- Avoid adding new legacy Material Web Components (`mwc-*`).\n- Scope styles to the component. Do not rely on global styles for component internals.\n\nSpacing tokens are defined in `src/resources/theme/core.globals.ts`. The scale runs from `--ha-space-1` at 4px through `--ha-space-20` at 80px in 4px increments. Common values are `--ha-space-2` at 8px, `--ha-space-4` at 16px, and `--ha-space-8` at 32px.\n\n```ts\nstatic get styles() {\n  return css`\n    :host {\n      padding: var(--ha-space-4);\n      color: var(--primary-text-color);\n      background-color: var(--card-background-color);\n    }\n\n    .content {\n      gap: var(--ha-space-2);\n    }\n\n    @media (max-width: 600px) {\n      :host {\n        padding: var(--ha-space-2);\n      }\n    }\n  `;\n}\n```\n\n## Interaction States\n\n- Make touch targets large enough for mobile.\n- Provide clear hover, active, focus, disabled, loading, error, and unavailable states.\n- Preserve keyboard navigation and visible focus indicators.\n- Maintain WCAG AA contrast for text and essential UI affordances.\n\n## View Transitions\n\nUse the View Transitions API only for meaningful continuity between DOM states.\n\nCore resources:\n\n- Utility wrapper: `src/common/util/view-transition.ts`, `withViewTransition()`.\n- Launch-screen fade example: `src/util/launch-screen.ts`.\n- Animation keyframes: `src/resources/theme/animations.globals.ts`.\n- Animation duration tokens: `src/resources/theme/core.globals.ts`.\n\nImplementation rules:\n\n- Use `withViewTransition()` for fallback behavior.\n- Keep transitions simple. Subtle fades and crossfades usually work best.\n- Use `--ha-animation-duration-fast`, `--ha-animation-duration-normal`, or `--ha-animation-duration-slow` for timing.\n- Ensure each `view-transition-name` is unique at any given time.\n- Remember only one view transition can run at a time.\n- View transitions operate at document level and do not work inside Shadow DOM style isolation. For web components, set `view-transition-name` on `:host` or use document-level transitions.\n- The root gets `view-transition-name: root` by default. Target `::view-transition-group(root)` to customize the default page transition.\n\n## Review Checklist\n\n- Styling uses theme variables and spacing tokens where practical.\n- Layout is mobile-first and RTL-safe.\n- Component styles are scoped.\n- Interactive states are clear and accessible.\n- Motion is subtle, tokenized, and respects reduced-motion behavior through existing globals.\n\n<!-- File: .agents/skills/ha-frontend-testing/SKILL.md (Tokens: ~1472 | Category: anthropic-skill) -->\n---\nname: ha-frontend-testing\ndescription: Home Assistant frontend testing and validation workflow. Use when adding or updating tests, running lint, TypeScript checks, Vitest, Playwright e2e suites, dev servers, or chart-data benchmarks.\n---\n\n# HA Frontend Testing\n\nUse this skill when choosing or running validation for frontend changes.\n\n## Test Helpers\n\n- Before adding or changing tests, inspect the relevant suite's existing helpers and fixtures. Reuse them instead of duplicating setup, test data, navigation, interactions, waits, or assertions.\n- When the same test flow appears more than once, move it into the closest suite-local helper with a focused interface.\n- Keep one-off test behaviour in the test unless a helper makes the intent materially clearer. Do not hide the behaviour under test behind broad, configurable abstractions.\n\n## Core Commands\n\n```bash\npnpm lint          # ESLint + Prettier + TypeScript + Lit\npnpm format        # Auto-fix ESLint + Prettier\npnpm lint:types    # TypeScript compiler, run without file arguments\npnpm test          # Vitest\npnpm build         # Full production build\npnpm dev           # App dev server\npnpm dev:serve     # Local serving dev server\n```\n\nNever run `tsc` or `pnpm lint:types` with file arguments. File arguments make `tsc` ignore `tsconfig.json` and can emit `.js` files into `src/`.\n\nFor focused type feedback on one file, use editor diagnostics instead of a file-scoped `tsc` command.\n\n## Production Builds\n\nProduction builds support foreground and managed background execution:\n\n```bash\npnpm build                         # Full foreground build\npnpm build --background            # Full managed background build\npnpm build --modern                # Modern frontend_latest bundle only\npnpm build --modern --background   # Modern managed background build\npnpm build --status\npnpm build --logs [--follow]\npnpm build --stop\n```\n\nUse `pnpm build --modern --background` for production bundle-size or browser performance comparisons that only need modern browser output. It runs the normal metadata and static preparation, minifies and compresses the modern `frontend_latest` bundle and shared static assets, and generates modern-only entry pages and service workers. It deliberately skips the legacy bundle and its service worker.\n\nDo not pass `--help`, `--background`, or `--modern` to `script/build_frontend`; that raw script does not parse arguments and always starts the full foreground build. Use `pnpm build` for managed builds. App builds and development servers keep exclusive ownership of `hass_frontend/` for their lifetime.\n\nManaged app, demo, gallery, and E2E app workflows share one lifetime lock, so only one build or development server can run at a time.\n\n## When To Add Tests\n\n- Write tests for code that computes something: data processing, utilities, config validation, and strategies.\n- Do not write rendering tests. This includes views, panels, and components whose text, styles, slots, or option defaults are checked, or that only put context and helper data into a template.\n- Do not try to cover every scenario, especially for behaviour that changes often.\n- If you are not sure a test is useful, describe it and what it would catch, and let the user decide.\n- Tests never talk to a real Home Assistant. Replace `callWS`, `callApi`, and the connection with fakes.\n\n## Dev Servers\n\n`pnpm dev` builds and watches the app, served by a running Home Assistant core configured through `development_repo`.\n\n`pnpm dev:serve` also serves locally and supports `-c` for the core URL and `-p` for the port. The default is 8124, or 8123 in a devcontainer.\n\nDev server commands support `--background`, `--status`, `--stop`, and `--logs [--follow]`. `pnpm dev`, `pnpm dev:serve`, `pnpm dev:demo`, and `pnpm dev:gallery` also support `--fetch-translations`; this runs translation fetching, including first-time GitHub device authentication, under the workflow lock before starting the watcher. It works in foreground and background modes. Prefer managed background mode while iterating so the watcher stays available across test runs without occupying the terminal. `pnpm dev` and `pnpm dev:serve` share one managed process slot because both write the app output.\n\n## Playwright E2E\n\nEach suite has its own dev server port. Playwright reuses an existing server locally when its configured URL responds; otherwise it performs a slow full build. When a development watcher is being reused, rspack recompiles on save and reruns should not need a restart.\n\nStart the relevant suite server, then run that suite:\n\n| Suite   | Background server                            | Test command            |\n| ------- | -------------------------------------------- | ----------------------- |\n| App     | `pnpm test:e2e:app:dev --background` on 8095 | `pnpm test:e2e:app`     |\n| Demo    | `pnpm dev:demo --background` on 8090         | `pnpm test:e2e:demo`    |\n| Gallery | `pnpm dev:gallery --background` on 8100      | `pnpm test:e2e:gallery` |\n\nThe custom development wrappers use `/__ha_dev_status` to identify and manage their own suites. Playwright server reuse checks the configured URL instead. Wrapper start and stop operations are idempotent for a matching suite and reject an unrelated process occupying the port.\n\nLocal runs against a watched development server do not always match CI's clean build artifacts, environment, sharding, or worker configuration. Use background servers for the fast iteration loop, but confirm the relevant CI jobs complete successfully before considering E2E changes verified.\n\nUse `-g \"<title>\" --project=chromium` to narrow a run. `pnpm test:e2e` runs suites sequentially when managed servers are unavailable to prevent cold builds racing over shared generated assets. Run suites directly; piping through output truncation hides progress and failures.\n\nThe app suite uses a stripped-down harness for e2e. Demo and gallery use their normal dev servers.\n\n## Benchmarks\n\nFor chart data transforms such as history, statistics, energy, and downsampling, read and follow the complete workflow in `test/benchmarks/README.md` before making benchmark or optimization changes.\n\nThat workflow owns the baseline, noise analysis, guardrails, acceptance thresholds, and reporting requirements. In particular, optimizations must keep output bit-identical; never update snapshots or modify fixtures to make an optimization pass.\n\n## Verification Selection\n\n- Documentation-only change: no code test required unless examples or commands changed.\n- Type-only or utility change: run focused Vitest if available, then `pnpm lint:types` if practical.\n- Lit component change: run relevant tests plus lint or typecheck depending on scope.\n- E2E-sensitive flow: start the relevant e2e dev server and run the narrow Playwright suite.\n- Broad refactor: run `pnpm lint` and relevant test suites when practical.\n\n<!-- File: .agents/skills/ha-frontend-types/SKILL.md (Tokens: ~430 | Category: anthropic-skill) -->\n---\nname: ha-frontend-types\ndescription: Home Assistant frontend TypeScript conventions. Use when defining or reviewing backend data contracts, optional schemas, shared types, assertions, or Lit lifecycle types.\n---\n\n# HA Frontend Types\n\nUse this skill for Home Assistant-specific TypeScript contracts and type choices.\n\n## Home Assistant Data Contracts\n\nVerify data contracts against the source that owns them, such as Home Assistant Core, Supervisor, a WebSocket handler, or an exported component type. Do not shape a type around assumptions made by its current frontend consumers.\n\n- Match required, optional, nullable, and defaulted fields to the producer and runtime contract. Preserve optional configuration fields when omission is supported and has defined behavior.\n- Use distinct request and response types when their wire shapes differ.\n- When changing a shared contract, check affected consumers, tests, fixtures, and mocks.\n\n## Reuse Home Assistant Contracts\n\n- Reuse the canonical Home Assistant type when one exists. Define shared contract types in the data or API module that owns them.\n- Reuse types exported by components and helpers rather than reconstructing their payloads. For event types, follow `ha-frontend-events`.\n- When one domain contract is used across modules, define and export it from the module that owns that contract.\n\nPrefer an existing owning contract. Introduce a frontend-specific type when the frontend shape or boundary genuinely differs.\n\n## Assertions\n\nPrefer accurate types and runtime narrowing. Use assertions or TypeScript suppressions at boundaries where the runtime invariant is understood but cannot be expressed cleanly; keep them narrow and explain non-obvious invariants.\n\n## Lit Lifecycle Types\n\nFor Lit lifecycle methods that receive changed properties, use `PropertyValues<this>` when the method only needs public reactive properties:\n\n```ts\nprotected willUpdate(changedProperties: PropertyValues<this>) {\n  // ...\n}\n```\n\nUse unparameterized `PropertyValues` when the method inspects private or protected reactive properties, which are not keys of `this`. Do not add assertions solely to retain `PropertyValues<this>`.\n\n## Enforced Baseline\n\nUse `import type` for type-only imports. This is enforced by the repository ESLint configuration.\n\n<!-- File: .agents/skills/ha-frontend-user-facing-text/SKILL.md (Tokens: ~700 | Category: anthropic-skill) -->\n---\nname: ha-frontend-user-facing-text\ndescription: Home Assistant frontend copy, localization, terminology, and user-facing text guidance. Use when adding or reviewing labels, buttons, dialogs, errors, translations, or UI strings.\n---\n\n# HA Frontend User-Facing Text\n\nUse this skill for all user-facing text, translations, labels, buttons, dialog copy, errors, helper text, and review comments about wording.\n\n## Localization\n\n- All user-facing text must be translatable.\n- Add translation keys to `src/translations/en.json` when introducing new strings.\n- Use the localization system instead of inline user-visible strings.\n- Prefer complete localized strings with placeholders over concatenating translated fragments.\n- Give translators enough context through key naming and placeholders.\n\n```ts\nthis._localize(\"ui.panel.config.updates.updates_refreshed\", {\n  count: 5,\n});\n```\n\n## Voice And Style\n\n- Use American English.\n- Use a friendly, informational tone.\n- Address users directly with \"you\" and \"your\" when appropriate.\n- Be inclusive, objective, and non-discriminatory.\n- Be concise and clear.\n- Use active voice.\n- Avoid jargon where a familiar home automation term works.\n- Always write \"Home Assistant\" in full. Do not use \"HA\" or \"HASS\" in user-facing copy.\n- Spell out terms when possible.\n- Use sentence case for titles, headings, buttons, labels, and UI elements.\n- Use the Oxford comma in lists.\n- Prefer \"like\" over \"e.g.\" and \"for example\" over \"i.e.\".\n- Avoid all caps for emphasis. Use wording, bold, or italics instead.\n- Write for both technical and non-technical users.\n\nSentence case examples:\n\n- Use: \"Create new automation\"\n- Avoid: \"Create New Automation\"\n- Use: \"Device settings\"\n- Avoid: \"Device Settings\"\n\n## Terminology\n\nUse \"integration\" instead of \"component\" for user-facing product language unless referring to a frontend component in developer context.\n\nTechnical product terms are lowercase in prose: automation, entity, device, service.\n\n## Delete, Remove, Create, Add\n\nUse \"Remove\" for actions that can be restored or reapplied:\n\n- Removing a user's permission.\n- Removing a user from a group.\n- Removing links between items.\n- Removing a widget from a dashboard.\n- Removing an item from a cart.\n\nUse \"Delete\" for permanent, non-recoverable actions:\n\n- Deleting a field.\n- Deleting a value in a field.\n- Deleting a task.\n- Deleting a group.\n- Deleting a permission.\n- Deleting a calendar event.\n\nUse \"Add\" for already-existing items:\n\n- Adding a permission to a user.\n- Adding a user to a group.\n- Adding links between items.\n- Adding a widget to a dashboard.\n- Adding an item to a cart.\n\nUse \"Create\" for something made from scratch:\n\n- Creating a new field.\n- Creating a new task.\n- Creating a new group.\n- Creating a new permission.\n- Creating a new calendar event.\n\nCreate pairs with Delete. Add pairs with Remove.\n\n## Review Checklist\n\n- Text is localized.\n- Copy uses sentence case.\n- \"Home Assistant\" is written in full.\n- Delete/Remove and Create/Add match recoverability and object lifecycle.\n- Placeholders are used instead of string concatenation.\n- The wording is concise and understandable without implementation knowledge.\n</yakaai_skills>"}