@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.
- package/README.md +52 -0
- package/commands/aether_broadchurch_setup.md +163 -0
- package/commands/aether_build_my_app.md +148 -0
- package/commands/aether_configure_query_server.md +90 -0
- package/commands/aether_deploy_agent.md +153 -0
- package/commands/aether_deploy_mcp.md +163 -0
- package/commands/aether_update_branding.md +123 -0
- package/commands/aether_update_instructions.md +183 -0
- package/commands/aether_vercel_deploy.md +278 -0
- package/commands/aether_vercel_setup.md +528 -0
- package/package.json +26 -0
- package/rules/aether_aether.mdc +21 -0
- package/rules/aether_agents.mdc +166 -0
- package/rules/aether_api.mdc +211 -0
- package/rules/aether_architecture.mdc +141 -0
- package/rules/aether_branding.mdc +43 -0
- package/rules/aether_cookbook.mdc +489 -0
- package/rules/aether_design.mdc +48 -0
- package/rules/aether_git-support.mdc +48 -0
- package/rules/aether_instructions_warning.mdc +48 -0
- package/rules/aether_mcp-servers.mdc +108 -0
- package/rules/aether_pref.mdc +123 -0
- package/rules/aether_server.mdc +99 -0
- package/rules/aether_something-broke.mdc +73 -0
- package/rules/aether_ui.mdc +76 -0
- package/skills/aether_test-api-queries/SKILL.md +63 -0
- package/skills/aether_test-api-queries/query-api.js +175 -0
|
@@ -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.
|