@yottagraph-app/aether-instructions 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,166 @@
1
+ ---
2
+ description: Rules for developing ADK agents in the agents/ directory
3
+ globs: agents/**
4
+ alwaysApply: false
5
+ ---
6
+
7
+ # Agent Development (Google ADK)
8
+
9
+ This project supports developing and deploying AI agents alongside the UI. Agents live in the `agents/` directory and deploy to Vertex AI Agent Engine via the `/deploy_agent` command.
10
+
11
+ ## Directory Structure
12
+
13
+ Each agent is a self-contained Python package. **Directory names must use underscores, not hyphens** (ADK uses the directory name as a Python identifier).
14
+
15
+ ```
16
+ agents/my_agent/ # Underscores only! "my-agent" will NOT work.
17
+ ├── __init__.py # Required (can be empty)
18
+ ├── agent.py # Required: must export root_agent
19
+ ├── requirements.txt # Required: must include google-adk
20
+ └── .env # Optional: local env vars (git-ignored)
21
+ ```
22
+
23
+ ## Writing an Agent
24
+
25
+ Agents use the [Google Agent Development Kit (ADK)](https://google.github.io/adk-docs/). The core pattern:
26
+
27
+ ```python
28
+ from google.adk.agents import Agent
29
+
30
+ def my_tool(query: str) -> dict:
31
+ """Tool docstrings become the LLM's understanding of what the tool does.
32
+ Be specific about parameters and return values."""
33
+ # Tool implementation
34
+ return {"result": "..."}
35
+
36
+ root_agent = Agent(
37
+ model="gemini-2.0-flash",
38
+ name="my_agent",
39
+ instruction="Clear instructions about the agent's purpose and capabilities.",
40
+ tools=[my_tool],
41
+ )
42
+ ```
43
+
44
+ Key rules:
45
+ - The `agent.py` file MUST export a variable named `root_agent`
46
+ - Tool functions should have detailed docstrings -- the LLM reads these to understand when and how to use each tool
47
+ - Use `google-adk` for the agent framework, `httpx` for HTTP calls
48
+ - Pin dependency versions in `requirements.txt` for reproducible deployments
49
+
50
+ ## Connecting to the Elemental API
51
+
52
+ Use `broadchurch_auth` for all Elemental API calls. It lives at
53
+ `agents/broadchurch_auth.py` and is automatically bundled into each agent
54
+ directory at deploy time. It handles:
55
+
56
+ - Reading the API URL from `broadchurch.yaml` (or `ELEMENTAL_API_URL` env var)
57
+ - Minting and caching GCP ID tokens in production
58
+ - Falling back to `ELEMENTAL_API_TOKEN` for local dev
59
+
60
+ ```python
61
+ from broadchurch_auth import elemental_client
62
+
63
+ def get_schema() -> dict:
64
+ """Get the yottagraph schema."""
65
+ resp = elemental_client.get("/elemental/metadata/schema")
66
+ resp.raise_for_status()
67
+ return resp.json()
68
+
69
+ def find_entities(expression: str, limit: int = 10) -> dict:
70
+ """Search for entities."""
71
+ resp = elemental_client.post("/elemental/find", data={"expression": expression, "limit": str(limit)})
72
+ resp.raise_for_status()
73
+ return resp.json()
74
+ ```
75
+
76
+ **Do NOT** hardcode URLs or manually handle auth tokens. Do NOT read
77
+ `broadchurch.yaml` directly — `broadchurch_auth` handles all of this.
78
+
79
+ Key endpoints:
80
+ - `GET /elemental/metadata/schema` — entity types and properties
81
+ - `POST /elemental/find` — search for entities
82
+ - `POST /elemental/entities/properties` — get entity property values
83
+ - `GET /entities/lookup?q=<name>` — look up entity by name
84
+
85
+ Requirements for agents using the Elemental API (add to `requirements.txt`):
86
+ ```
87
+ google-auth>=2.20.0
88
+ pyyaml>=6.0
89
+ ```
90
+
91
+ ## Local Testing
92
+
93
+ Test agents locally before deploying. Run `adk web` from the `agents/`
94
+ directory (NOT from inside the agent folder — ADK discovers agents by
95
+ scanning subdirectories):
96
+
97
+ ```bash
98
+ cd agents/ # The PARENT of your agent directories
99
+ pip install -r my_agent/requirements.txt
100
+
101
+ # Required: tell ADK to use Vertex AI for Gemini
102
+ export GOOGLE_CLOUD_PROJECT=broadchurch
103
+ export GOOGLE_CLOUD_LOCATION=us-central1
104
+ export GOOGLE_GENAI_USE_VERTEXAI=1
105
+
106
+ # For agents that call the Elemental API:
107
+ export ELEMENTAL_API_URL=https://stable-query.lovelace.ai
108
+ export ELEMENTAL_API_TOKEN=<your-token>
109
+
110
+ adk web # Opens browser UI at http://127.0.0.1:8000/dev-ui/
111
+ ```
112
+
113
+ Select your agent from the dropdown in the web UI. You can also run a single agent directly: `adk run my_agent/`
114
+
115
+ `broadchurch_auth.py` lives at the `agents/` root, so it's importable
116
+ during local dev (agents/ is the CWD → on sys.path). At deploy time, the
117
+ workflow copies it into the agent directory alongside `broadchurch.yaml`.
118
+
119
+ ## Deployment
120
+
121
+ Two ways to deploy:
122
+
123
+ 1. **Portal UI** -- Open the project in the Broadchurch Portal. It scans
124
+ `agents/` in your GitHub repo and shows undeployed agents with a Deploy
125
+ button. Make sure your code is pushed to `main` first.
126
+ 2. **Cursor command** -- Run `/deploy_agent`. It reads `broadchurch.yaml` and
127
+ triggers the same workflow.
128
+
129
+ Both paths trigger `deploy-agent.yml` in GitHub Actions, which runs
130
+ `adk deploy agent_engine` and then registers the agent with the Portal Gateway.
131
+
132
+ Manual CLI deployment (advanced):
133
+
134
+ ```bash
135
+ adk deploy agent_engine \
136
+ --project <from broadchurch.yaml> \
137
+ --region <from broadchurch.yaml> \
138
+ --staging_bucket <from broadchurch.yaml> \
139
+ --display_name "<agent-name>" \
140
+ --trace_to_cloud \
141
+ agents/<agent-name>/
142
+ ```
143
+
144
+ ## How Agents Reach the Chat UI
145
+
146
+ Once deployed, the agent is reachable through the Portal Gateway:
147
+
148
+ ```
149
+ Chat UI (pages/chat.vue)
150
+ → POST NUXT_PUBLIC_GATEWAY_URL/api/agents/{tenantId}/{agentId}/query
151
+ → Portal Gateway proxies to Vertex AI Agent Engine
152
+ → Agent runs, returns response
153
+ → Chat UI displays it
154
+ ```
155
+
156
+ The gateway URL and tenant ID come from `broadchurch.yaml` (injected as
157
+ `NUXT_PUBLIC_GATEWAY_URL` and `NUXT_PUBLIC_TENANT_ORG_ID`). The chat page
158
+ discovers available agents from the Portal config endpoint.
159
+
160
+ ## Agent Design Guidelines
161
+
162
+ - Keep agents focused: one agent per domain or task type
163
+ - Write clear, specific instructions -- the LLM follows them literally
164
+ - Tool docstrings are critical: they're the LLM's API documentation
165
+ - Handle errors gracefully in tools: return error messages, don't raise exceptions
166
+ - Use `get_schema` as a discovery tool so the agent can learn about entity types at runtime
@@ -0,0 +1,211 @@
1
+ ---
2
+ description: "Elemental API client usage, schema discovery, entity search, API gotchas, MCP servers. Read when building features that fetch or display data from the Query Server."
3
+ alwaysApply: false
4
+ ---
5
+ # Elemental API — The Platform Data Source
6
+
7
+ **This app is built on the Lovelace platform.** The Query Server is the
8
+ primary data source — use it first for any data needs (entities, news,
9
+ filings, sentiment, relationships, events). Do NOT call external APIs
10
+ (e.g. sec.gov, Wikipedia) for data that the platform already provides.
11
+
12
+ The Elemental API provides access to the Lovelace Knowledge Graph through
13
+ the Query Server. Use it to search for entities, retrieve properties,
14
+ explore relationships, and analyze sentiment. New data sources are added
15
+ regularly — use the discovery-first pattern to find what's available.
16
+
17
+ ## Skill Documentation
18
+
19
+ For full endpoint documentation, read the **elemental-api skill** in
20
+ `skills/elemental-api/`. Start with `SKILL.md`, then `overview.md`.
21
+ These files are copied from `@yottagraph-app/elemental-api-skill` during
22
+ `npm install` (postinstall step) — if the directory is empty, run
23
+ `npm install` first.
24
+
25
+ Key files:
26
+ - `entities.md` — entity search, details, and properties
27
+ - `find.md` — expression language for searching by type, property, and relationships
28
+ - `schema.md` — entity types (flavors), properties (PIDs), and schema endpoints
29
+ - `relationships.md` — entity connections
30
+ - `events.md` — events involving entities
31
+ - `articles.md` — news mentions and article content
32
+
33
+ ## Client Usage
34
+
35
+ All API calls go through `useElementalClient()` from `@yottagraph-app/elemental-api/client`.
36
+ Auth tokens and base URL are configured automatically by the `elemental-client` plugin.
37
+
38
+ ```typescript
39
+ import { useElementalClient } from '@yottagraph-app/elemental-api/client';
40
+
41
+ const client = useElementalClient();
42
+
43
+ const results = await client.getNEID({ entityName: 'Apple', maxResults: 5 });
44
+ const report = await client.getNamedEntityReport(results.neids[0]);
45
+ const schema = await client.getSchema();
46
+ ```
47
+
48
+ Types are also imported from the client:
49
+
50
+ ```typescript
51
+ import type { NamedEntityReport, GetNEIDResponse } from '@yottagraph-app/elemental-api/client';
52
+ ```
53
+
54
+ ## Discovery-First Pattern
55
+
56
+ The knowledge graph contains many entity types and properties, and new datasets
57
+ are added regularly (e.g. Edgar filings, financial data). Do NOT hardcode entity
58
+ types or property names. Instead, discover them at runtime:
59
+
60
+ 1. **Get the schema** — `client.getSchema()` returns all entity types (flavors)
61
+ and properties (PIDs) available in the system. See `schema.md`.
62
+
63
+ The schema response contains:
64
+ - **Flavors** (entity types): Company, Person, GovernmentOrg, etc.
65
+ Each flavor has a numeric ID and a human-readable name.
66
+ - **PIDs** (properties): name, country, industry, lei_code, etc.
67
+ Each PID has a type (`data_str`, `data_int`, `data_nindex`, etc.).
68
+ - Properties with type `data_nindex` are references to other entities —
69
+ resolve them with another `getPropertyValues` call.
70
+
71
+ Use flavor names in `findEntities()` expressions and PID names in
72
+ `getPropertyValues()`.
73
+
74
+ 2. **Search with expressions** — `client.findEntities()` uses a JSON expression
75
+ language to search by type, property value, or relationship. See `find.md`.
76
+ 3. **Get property values** — `client.getPropertyValues()` fetches property data
77
+ for specific entities.
78
+
79
+ This pattern lets agents work with any dataset without needing hardcoded
80
+ knowledge of what's in the graph.
81
+
82
+ ## API Gotchas
83
+
84
+ > **WARNING -- `getSchema()` response nesting**: The generated TypeScript types
85
+ > put `flavors` and `properties` at the top level, but the actual API response
86
+ > nests them under a `schema` key. Using `response.properties` directly will
87
+ > crash with `Cannot read properties of undefined`. Always use:
88
+
89
+ ```typescript
90
+ const res = await client.getSchema();
91
+ const properties = res.schema?.properties ?? (res as any).properties ?? [];
92
+ const flavors = res.schema?.flavors ?? (res as any).flavors ?? [];
93
+ ```
94
+
95
+ > **WARNING -- `getPropertyValues()` takes JSON-stringified arrays**: The `eids`
96
+ > and `pids` parameters must be JSON-encoded strings, NOT native arrays. The
97
+ > TypeScript type is `string`, not `string[]`. Passing a raw array will silently
98
+ > return no data.
99
+
100
+ ```typescript
101
+ const values = await client.getPropertyValues({
102
+ eids: JSON.stringify(['00416400910670863867']),
103
+ pids: JSON.stringify(['name', 'country', 'industry']),
104
+ });
105
+ ```
106
+
107
+ ### `getLinkedEntities` only supports graph node types
108
+
109
+ `getLinkedEntities(neid, { entity_type: ['document'] })` will fail at
110
+ runtime with _"entity_type document not a valid graph node type"_. The
111
+ `/entities/{neid}/linked` endpoint only supports three entity types:
112
+ **person**, **organization**, **location**. Documents, filings, articles,
113
+ financial instruments, events, and all other types are excluded — even
114
+ though the schema shows relationships like `filed` connecting organizations
115
+ to documents.
116
+
117
+ To traverse relationships to non-graph-node types, use `getPropertyValues`
118
+ with the relationship PID instead. Relationship properties (`data_nindex`)
119
+ return linked entity IDs as values. Zero-pad the returned IDs to 20
120
+ characters to form valid NEIDs.
121
+
122
+ ```typescript
123
+ const pidMap = await getPropertyPidMap(client);
124
+ const filedPid = pidMap.get('filed')!;
125
+ const res = await client.getPropertyValues({
126
+ eids: JSON.stringify([orgNeid]),
127
+ pids: JSON.stringify([filedPid]),
128
+ });
129
+ const docNeids = res.values.map((v) => String(v.value).padStart(20, '0'));
130
+ ```
131
+
132
+ See the **cookbook** rule for a full "Get filings for a company" recipe.
133
+
134
+ ### `getNEID()` vs `findEntities()` for entity search
135
+
136
+ - **`client.getNEID()`** -- simple single-entity lookup by name
137
+ (`GET /entities/lookup`). Best for resolving one company/person name.
138
+ - **`client.findEntities()`** -- expression-based batch search
139
+ (`POST /entities/search`). Best for filtered searches (by type, property,
140
+ relationship). See `find.md` for the expression language.
141
+
142
+ ## Error Handling
143
+
144
+ ```typescript
145
+ try {
146
+ const data = await client.getNEID({ entityName: '...' });
147
+ } catch (error) {
148
+ console.error('API Error:', error);
149
+ showError('Failed to load data. Please try again.');
150
+ }
151
+ ```
152
+
153
+ Methods on `useElementalClient()` return data directly and throw on non-2xx
154
+ responses. For full `{ data, status, headers }` access, import the raw
155
+ functions instead:
156
+
157
+ ```typescript
158
+ import { getArticle } from '@yottagraph-app/elemental-api/client';
159
+
160
+ const response = await getArticle(artid);
161
+ if (response.status === 404) { /* handle not found */ }
162
+ ```
163
+
164
+ ## Lovelace MCP Servers (Optional)
165
+
166
+ Four MCP servers **may** be configured in `.cursor/mcp.json` for interactive
167
+ data exploration. They are NOT required — the REST client
168
+ (`useElementalClient()`) and skill docs cover the same data and are the
169
+ primary path for building features.
170
+
171
+ **Before referencing MCP tools, check your available tool list.** If tools
172
+ like `elemental_get_schema` don't appear in your MCP server list, the
173
+ servers aren't connected — just use the REST client and skill docs instead.
174
+ Do not report this as a problem; it's a normal configuration state.
175
+
176
+ | Server | What it provides |
177
+ |---|---|
178
+ | `lovelace-elemental` | Knowledge Graph: entities, relationships, events, sentiment, schema discovery |
179
+ | `lovelace-stocks` | Stock/financial market data |
180
+ | `lovelace-wiki` | Wikipedia entity enrichment |
181
+ | `lovelace-polymarket` | Prediction market data |
182
+
183
+ ### Setup
184
+
185
+ `.cursor/mcp.json` is auto-generated by `init-project.js`. If it's missing,
186
+ run `node init-project.js --local` to regenerate it. For provisioned projects,
187
+ the servers route through the Portal Gateway proxy (no credentials needed).
188
+ For local development without a gateway, the servers require an
189
+ `AUTH0_M2M_DEV_TOKEN` environment variable.
190
+
191
+ The MCP Explorer page (`pages/mcp.vue`) also connects to these servers
192
+ through the Portal Gateway for interactive exploration in the browser.
193
+
194
+ ### When MCP Servers Are Available
195
+
196
+ If the MCP tools appear in your tool list, you can use them for interactive
197
+ exploration alongside the REST client:
198
+
199
+ | Tool | Purpose |
200
+ |---|---|
201
+ | `elemental_get_schema` | Discover entity types (flavors), properties, and relationships |
202
+ | `elemental_get_entity` | Look up entity by name or NEID; returns properties |
203
+ | `elemental_get_related` | Related entities with type/relationship filters |
204
+ | `elemental_get_relationships` | Relationship types and counts between two entities |
205
+ | `elemental_graph_neighborhood` | Most influential neighbors of an entity |
206
+ | `elemental_graph_sentiment` | Sentiment analysis from news articles |
207
+ | `elemental_get_events` | Events for an entity or by search query |
208
+ | `elemental_health` | Health check |
209
+
210
+ MCP is convenient for schema discovery and entity resolution during planning.
211
+ For building UI features, always use the REST client (`useElementalClient()`).
@@ -0,0 +1,141 @@
1
+ ---
2
+ description: "Project structure, navigation patterns, data architecture, server routes, agents, MCP servers. Read when adding pages, navigation, or server-side functionality."
3
+ alwaysApply: false
4
+ ---
5
+
6
+ # Aether Architecture
7
+
8
+ Aether is an app framework built on Nuxt 3 + Vue 3 + Vuetify 3 + TypeScript. It follows standard Nuxt conventions -- pages in `pages/`, components in `components/`, composables in `composables/`, server routes in `server/api/`.
9
+
10
+ **Tech stack**: Nuxt 3 (SPA), Vue 3 Composition API (`<script setup>`), Vuetify 3, TypeScript (required), Auth0 (automatic).
11
+
12
+ **Data source**: This app runs on the Lovelace platform. The Query Server (Elemental API) is the primary data source -- use it for entities, news, filings, sentiment, relationships, and events. See the `api` rule. Do not call external APIs for data the platform provides.
13
+
14
+ ## Project Structure
15
+
16
+ ```
17
+ pages/ # Routes (file-based routing)
18
+ components/ # Reusable Vue components
19
+ composables/ # Shared logic (useXxx.ts)
20
+ server/api/ # Nitro server routes (deploy with app)
21
+ assets/ # CSS, fonts, static assets
22
+ utils/ # Pure utility functions
23
+ agents/ # ADK agents (Python, deploy to Vertex AI)
24
+ mcp-servers/ # MCP servers (Python, deploy to Cloud Run)
25
+ ```
26
+
27
+ ## Data Architecture
28
+
29
+ | Store | Purpose | When to use |
30
+ |---|---|---|
31
+ | Query Server (Elemental API) | Lovelace Knowledge Graph | Entities, news, relationships, events, sentiment, filings |
32
+ | KV (Upstash Redis) | User preferences, lightweight state | Settings, watchlists, UI state that should persist |
33
+ | Supabase (PostgreSQL) | App-specific relational data | Custom tables, complex queries (if connected) |
34
+
35
+ Use `useElementalClient()` for Query Server data (see `api` rule).
36
+ Use `Pref<T>` or `usePrefsStore()` for KV (see `pref` rule).
37
+ Check `.env` for `NUXT_PUBLIC_SUPABASE_URL` before using Supabase.
38
+
39
+ ## Adding Pages
40
+
41
+ Create `.vue` files in `pages/`. Nuxt generates routes automatically:
42
+
43
+ ```
44
+ pages/index.vue → /
45
+ pages/dashboard.vue → /dashboard
46
+ pages/settings.vue → /settings
47
+ pages/reports/index.vue → /reports
48
+ ```
49
+
50
+ ## App Shell
51
+
52
+ `app.vue` provides `AppHeader` (branding, user menu, settings) and renders `<NuxtPage />`. There is no prescribed layout beyond the header -- design whatever UX fits the problem.
53
+
54
+ ## Adding Navigation
55
+
56
+ Only add navigation if the app genuinely needs multiple views. Choose the pattern that fits the UX:
57
+
58
+ **Sidebar** -- add to `app.vue` for persistent section navigation:
59
+
60
+ ```vue
61
+ <v-navigation-drawer permanent app>
62
+ <v-list nav density="compact">
63
+ <v-list-item title="Dashboard" prepend-icon="mdi-view-dashboard" to="/" />
64
+ <v-list-item title="Reports" prepend-icon="mdi-chart-bar" to="/reports" />
65
+ </v-list>
66
+ </v-navigation-drawer>
67
+ ```
68
+
69
+ **Top tabs** -- add to `app.vue` or a layout component:
70
+
71
+ ```vue
72
+ <v-tabs>
73
+ <v-tab to="/">Dashboard</v-tab>
74
+ <v-tab to="/reports">Reports</v-tab>
75
+ </v-tabs>
76
+ ```
77
+
78
+ **In-page tabs** -- for subsections within a single page:
79
+
80
+ ```vue
81
+ <v-tabs v-model="tab">
82
+ <v-tab value="overview">Overview</v-tab>
83
+ <v-tab value="details">Details</v-tab>
84
+ </v-tabs>
85
+ <v-window v-model="tab">
86
+ <v-window-item value="overview">...</v-window-item>
87
+ <v-window-item value="details">...</v-window-item>
88
+ </v-window>
89
+ ```
90
+
91
+ **No nav** -- single-page apps can put everything on `pages/index.vue`.
92
+
93
+ ## Naming Conventions
94
+
95
+ - **Pages**: `kebab-case.vue`
96
+ - **Components**: `PascalCase.vue`
97
+ - **Composables**: `useCamelCase.ts`
98
+ - **Server routes**: `kebab-case.<method>.ts`
99
+
100
+ ## New Page Checklist
101
+
102
+ - [ ] Created a design doc in `design/` (copy `design/feature_template.md`)
103
+ - [ ] Page in `pages/` with `<script setup lang="ts">`
104
+ - [ ] Uses Vuetify components and the project's dark theme
105
+ - [ ] Updated `DESIGN.md` with current status
106
+
107
+ ## Server Routes
108
+
109
+ Nitro server routes live in `server/api/` and deploy with the app to Vercel. They're auto-registered by Nuxt:
110
+
111
+ ```
112
+ server/api/my-data/fetch.get.ts → GET /api/my-data/fetch
113
+ server/api/my-data/submit.post.ts → POST /api/my-data/submit
114
+ ```
115
+
116
+ Call them from client code with `$fetch('/api/my-data/fetch')`. See the `server` cursor rule for patterns. Use server routes when you need to proxy external APIs (avoid CORS) or keep secrets off the client.
117
+
118
+ ## Beyond the UI: Agents, MCP Servers, and Server Routes
119
+
120
+ Aether apps are more than just a Nuxt SPA. The project contains three additional directories that deploy independently:
121
+
122
+ ### `agents/` -- ADK Agents (Python)
123
+
124
+ Each subdirectory is a self-contained Python agent that deploys to Vertex AI Agent Engine. See the `agents` cursor rule for development patterns. Agents are deployed via the Broadchurch Portal UI or the `/deploy_agent` Cursor command, both of which trigger `deploy-agent.yml`. Once deployed, the chat UI at `pages/chat.vue` talks to agents through the Portal Gateway (`NUXT_PUBLIC_GATEWAY_URL`).
125
+
126
+ ### `mcp-servers/` -- MCP Servers (Python)
127
+
128
+ Each subdirectory is a FastMCP server that deploys to Cloud Run. See the `mcp-servers` cursor rule. Deployed via Portal UI or `/deploy_mcp`, triggering `deploy-mcp.yml`. Agents can connect to MCP servers as tool providers.
129
+
130
+ ### `broadchurch.yaml`
131
+
132
+ Tenant-specific configuration generated during provisioning. Contains GCP project, org ID, service account, gateway URL, and query server URL. Read by deploy commands and GitHub Actions workflows. Don't edit manually.
133
+
134
+ ## Built-in Pages
135
+
136
+ These pages ship with the template and can be kept, modified, or removed based on the app's needs:
137
+
138
+ - `pages/chat.vue` -- Agent chat UI. Talks to deployed ADK agents through the Portal Gateway. Keep if the app uses AI agents.
139
+ - `pages/mcp.vue` -- MCP Explorer. Browse and test MCP server tools. Keep if the app uses MCP servers.
140
+ - `pages/entity-lookup.vue` -- Entity search tool. Useful for looking up NEIDs. Keep or remove based on the app.
141
+
@@ -0,0 +1,43 @@
1
+ ---
2
+ description: Apply when working on visual styling, colors, typography, theming, branding, or UI appearance.
3
+ alwaysApply: false
4
+ ---
5
+
6
+ # Lovelace Brand R2
7
+
8
+ The app follows Lovelace Brand R2 guidelines. For the full specification (color ramps, accessibility, visual effects), read `branding/BRANDING.md`.
9
+
10
+ ## Quick Reference
11
+
12
+ - **Theme**: Single dark theme. No light mode, no theme switching.
13
+ - **Core colors**: Jet Black `#0A0A0A`, Pure White `#FFFFFF`, Cyber Green `#3FEA00`, Sonic Silver `#757575`
14
+ - **Secondary colors**: Electric Blue `#003BFF`, Blaze Orange `#FF5C00`
15
+ - **Semantic**: Amber `#FF9F0A` (warnings), Red `#EF4444` (errors)
16
+ - **Fonts**: FK Grotesk (body), FK Grotesk Mono (headlines, buttons, code). Falls back to Inter / system-ui. See `public/fonts/README.md` for setup.
17
+ - **Icons**: Material Design Icons (mdi) via Vuetify
18
+
19
+ ## Branding Files
20
+
21
+ These files are copied wholesale from the canonical branding source (`moongoose/ui/news-ui`). Update them by running `/update_branding`. Do **not** edit them in place.
22
+
23
+ | File | Purpose |
24
+ |---|---|
25
+ | `branding/BRANDING.md` | Full brand specification |
26
+ | `composables/useNewsTheme.ts` | Theme colors and CSS variable application |
27
+ | `composables/useThemeClasses.ts` | Theme-aware class utilities |
28
+ | `assets/theme-styles.css` | Theme CSS classes |
29
+ | `assets/fonts.css` | `@font-face` declarations |
30
+ | `assets/brand-globals.css` | Global CSS variables, typography, utilities |
31
+ | `public/fonts/README.md` | Font setup instructions |
32
+ | `public/LL-logo-full-wht.svg` | Primary logo (white, for dark backgrounds) |
33
+
34
+ ## Vuetify Theme
35
+
36
+ The `lovelaceDark` Vuetify theme is defined in `nuxt.config.ts` under `vuetify.vuetifyOptions.theme`. This is the same theme used by the Broadchurch Portal. It provides Brand R2 colors to all Vuetify components automatically (primary = Cyber Green, secondary = Electric Blue, etc.). Component defaults (rounded buttons, outlined cards, etc.) are also set in `nuxt.config.ts` under `vuetify.vuetifyOptions.defaults`.
37
+
38
+ ## Integration
39
+
40
+ - `composables/useCustomTheme.ts` is a thin adapter around `useNewsTheme`. Use `useCustomTheme()` or `useThemeClasses()` in components.
41
+ - CSS variables (`--lv-green`, `--lv-black`, etc.) and theme classes (`.theme-card`, `.theme-text-primary`, etc.) are available globally.
42
+ - `useNewsTheme` calls `theme.change('lovelaceDark')` to activate the Vuetify theme. This must match the theme name in `nuxt.config.ts`.
43
+ - If you need to change how the branding integrates with Aether, modify the adapter -- not the branding files.