@yottagraph-app/aether-instructions 1.1.41 → 1.1.43

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (37) hide show
  1. package/AGENTS.md +18 -191
  2. package/README.md +13 -9
  3. package/commands/build_my_app.md +7 -8
  4. package/commands/deploy_agent.md +7 -7
  5. package/commands/update_branding.md +1 -1
  6. package/commands/update_instructions.md +25 -17
  7. package/package.json +2 -4
  8. package/skills/aether/SKILL.md +57 -0
  9. package/{rules/agents-data.mdc → skills/aether/agents-data.md} +3 -6
  10. package/{rules/agents.mdc → skills/aether/agents.md} +49 -28
  11. package/{rules/architecture.mdc → skills/aether/architecture.md} +14 -21
  12. package/{rules/branding.mdc → skills/aether/branding.md} +7 -12
  13. package/{rules/cookbook-data.mdc → skills/aether/cookbook-data.md} +13 -28
  14. package/{rules/cookbook.mdc → skills/aether/cookbook.md} +2 -9
  15. package/skills/aether/cursor-cloud.md +57 -0
  16. package/{rules/data.mdc → skills/aether/data.md} +80 -70
  17. package/skills/aether/deployment.md +14 -0
  18. package/{rules/design.mdc → skills/aether/design.md} +1 -6
  19. package/skills/aether/env.md +10 -0
  20. package/{rules/git-support.mdc → skills/aether/git-support.md} +4 -9
  21. package/{rules/instructions_warning.mdc → skills/aether/instructions_warning.md} +6 -12
  22. package/skills/aether/local-setup.md +15 -0
  23. package/{rules/mcp-servers.mdc → skills/aether/mcp-servers.md} +3 -7
  24. package/{rules/pref.mdc → skills/aether/pref.md} +9 -14
  25. package/skills/aether/server-data.md +48 -0
  26. package/skills/aether/server.md +60 -0
  27. package/{rules/something-broke.mdc → skills/aether/something-broke.md} +3 -7
  28. package/{rules/server.mdc → skills/aether/storage.md} +78 -108
  29. package/{rules/ui.mdc → skills/aether/ui.md} +2 -6
  30. package/skills/elemental-mcp-patterns/SKILL.md +57 -51
  31. package/variants/mcp-only/commands/build_my_app.md +6 -6
  32. package/variants/mcp-only/{rules/agents-data.mdc → skills/aether/agents-data.md} +0 -6
  33. package/variants/mcp-only/{rules/cookbook-data.mdc → skills/aether/cookbook-data.md} +3 -6
  34. package/variants/mcp-only/{rules/data.mdc → skills/aether/data.md} +1 -6
  35. package/variants/mcp-only/{rules/server-data.mdc → skills/aether/server-data.md} +9 -15
  36. package/rules/aether.mdc +0 -21
  37. package/rules/server-data.mdc +0 -54
@@ -1,7 +1,3 @@
1
- ---
2
- description: "Data access via 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
1
  # Data — Elemental API (platform data source)
6
2
 
7
3
  **This app is built on the Lovelace platform.** The Query Server is the
@@ -81,10 +77,10 @@ Test those with curl before implementing them in code.
81
77
  The gateway proxy authenticates on your behalf — no Auth0 tokens needed.
82
78
  Read `broadchurch.yaml` for the three values you need:
83
79
 
84
- | YAML path | Purpose |
85
- |---|---|
86
- | `gateway.url` | Portal Gateway base URL |
87
- | `tenant.org_id` | Your tenant ID (path segment) |
80
+ | YAML path | Purpose |
81
+ | -------------------- | ------------------------------------ |
82
+ | `gateway.url` | Portal Gateway base URL |
83
+ | `tenant.org_id` | Your tenant ID (path segment) |
88
84
  | `gateway.qs_api_key` | API key (sent as `X-Api-Key` header) |
89
85
 
90
86
  Build the request URL as `{gateway.url}/api/qs/{tenant.org_id}/{endpoint}`
@@ -141,9 +137,9 @@ Elemental API patterns. **Use these instead of writing from scratch:**
141
137
 
142
138
  ```typescript
143
139
  const { flavors, properties, flavorByName, pidByName, refresh } = useElementalSchema();
144
- await refresh(); // fetches once, then cached
145
- const articleFid = flavorByName('article'); // → string | null
146
- const namePid = pidByName('name'); // → string | null
140
+ await refresh(); // fetches once, then cached
141
+ const articleFid = flavorByName('article'); // → string | null
142
+ const namePid = pidByName('name'); // → string | null
147
143
  ```
148
144
 
149
145
  Handles the dual response shapes (`res.schema.flavors` vs `res.flavors`)
@@ -152,14 +148,20 @@ and the `fid`/`findex` naming inconsistency automatically.
152
148
  ### `utils/elementalHelpers` — Gateway URL Helpers
153
149
 
154
150
  ```typescript
155
- import { buildGatewayUrl, getApiKey, padNeid, searchEntities, getEntityName } from '~/utils/elementalHelpers';
156
-
157
- const url = buildGatewayUrl('entities/search'); // full gateway URL
158
- const key = getApiKey(); // from runtimeConfig
159
- const neid = padNeid('4926132345040704022'); // → "04926132345040704022"
151
+ import {
152
+ buildGatewayUrl,
153
+ getApiKey,
154
+ padNeid,
155
+ searchEntities,
156
+ getEntityName,
157
+ } from '~/utils/elementalHelpers';
158
+
159
+ const url = buildGatewayUrl('entities/search'); // full gateway URL
160
+ const key = getApiKey(); // from runtimeConfig
161
+ const neid = padNeid('4926132345040704022'); // → "04926132345040704022"
160
162
 
161
163
  const results = await searchEntities('Microsoft'); // batch name search
162
- const name = await getEntityName(neid); // display name lookup
164
+ const name = await getEntityName(neid); // display name lookup
163
165
  ```
164
166
 
165
167
  ## Client Usage
@@ -174,7 +176,10 @@ const client = useElementalClient();
174
176
 
175
177
  const schema = await client.getSchema();
176
178
  const entities = await client.findEntities({
177
- expression: JSON.stringify({ type: 'comparison', comparison: { operator: 'string_like', pid: 8, value: 'Apple' } }),
179
+ expression: JSON.stringify({
180
+ type: 'comparison',
181
+ comparison: { operator: 'string_like', pid: 8, value: 'Apple' },
182
+ }),
178
183
  limit: 5,
179
184
  });
180
185
  ```
@@ -185,8 +190,8 @@ All methods return data directly and throw on non-2xx responses.
185
190
 
186
191
  **Entity search and lookup:**
187
192
 
188
- | Method | Signature | Purpose |
189
- |---|---|---|
193
+ | Method | Signature | Purpose |
194
+ | -------------- | -------------------------- | --------------------------------------- |
190
195
  | `findEntities` | `(body: FindEntitiesBody)` | Expression-based search (see `find.md`) |
191
196
 
192
197
  > **Entity search**: Use `findEntities()` with `string_like` on the name PID
@@ -201,25 +206,25 @@ All methods return data directly and throw on non-2xx responses.
201
206
 
202
207
  **Properties and schema:**
203
208
 
204
- | Method | Signature | Purpose |
205
- |---|---|---|
206
- | `getSchema` | `()` | All entity types (flavors) and properties (PIDs) |
209
+ | Method | Signature | Purpose |
210
+ | ------------------- | ---------------------------------------- | ------------------------------------------------------------------------------------ |
211
+ | `getSchema` | `()` | All entity types (flavors) and properties (PIDs) |
207
212
  | `getPropertyValues` | `(body: { eids: string, pids: string })` | Property values (eids: JSON array of NEID strings; pids: JSON array of numeric PIDs) |
208
- | `summarizeProperty` | `(pid: number)` | Summary stats for a property |
213
+ | `summarizeProperty` | `(pid: number)` | Summary stats for a property |
209
214
 
210
215
  **Relationships and graph:**
211
216
 
212
- | Method | Signature | Purpose |
213
- |---|---|---|
217
+ | Method | Signature | Purpose |
218
+ | -------------- | -------------------------------- | ------------------------------------------------------------ |
214
219
  | `findEntities` | `(body: { expression, limit? })` | Find linked entities via `linked` expression (see `find.md`) |
215
220
 
216
221
  **Other:**
217
222
 
218
- | Method | Signature | Purpose |
219
- |---|---|---|
220
- | `getHealth` | `()` | Health check |
221
- | `getStatus` | `()` | Server status and capabilities |
222
- | `adaMessage` | `(body: AdaMessageBody)` | Ada AI chat |
223
+ | Method | Signature | Purpose |
224
+ | ------------ | ------------------------ | ------------------------------ |
225
+ | `getHealth` | `()` | Health check |
226
+ | `getStatus` | `()` | Server status and capabilities |
227
+ | `adaMessage` | `(body: AdaMessageBody)` | Ada AI chat |
223
228
 
224
229
  ## Discovery-First Pattern
225
230
 
@@ -230,16 +235,16 @@ types or property names. Instead, discover them at runtime:
230
235
  1. **Get the schema** — `client.getSchema()` returns all entity types (flavors)
231
236
  and properties (PIDs) available in the system. See `schema.md`.
232
237
 
233
- The schema response contains:
234
- - **Flavors** (entity types): Company, Person, GovernmentOrg, etc.
235
- Each flavor has a numeric ID and a human-readable name.
236
- - **PIDs** (properties): name, country, industry, lei_code, etc.
237
- Each PID has a type (`data_str`, `data_int`, `data_nindex`, etc.).
238
- - Properties with type `data_nindex` are references to other entities —
239
- resolve them with another `getPropertyValues` call.
238
+ The schema response contains:
239
+ - **Flavors** (entity types): Company, Person, GovernmentOrg, etc.
240
+ Each flavor has a numeric ID and a human-readable name.
241
+ - **PIDs** (properties): name, country, industry, lei_code, etc.
242
+ Each PID has a type (`data_str`, `data_int`, `data_nindex`, etc.).
243
+ - Properties with type `data_nindex` are references to other entities —
244
+ resolve them with another `getPropertyValues` call.
240
245
 
241
- Use flavor names in `findEntities()` expressions and PID names in
242
- `getPropertyValues()`.
246
+ Use flavor names in `findEntities()` expressions and PID names in
247
+ `getPropertyValues()`.
243
248
 
244
249
  2. **Search with expressions** — `client.findEntities()` uses a JSON expression
245
250
  language to search by type, property value, or relationship. See `find.md`.
@@ -269,10 +274,10 @@ For reliable agent behavior, prefer typed semantics over string heuristics:
269
274
 
270
275
  There are two schema endpoints with **different response shapes**:
271
276
 
272
- | Endpoint | Flavors at | Flavor ID field | Detail level |
273
- |----------|-----------|-----------------|--------------|
274
- | `GET /schema` | top-level (`res.flavors`) | `findex` | Rich (display names, units, domains) |
275
- | `GET /elemental/metadata/schema` | nested (`res.schema.flavors`) | `fid` | Basic (name + type only) |
277
+ | Endpoint | Flavors at | Flavor ID field | Detail level |
278
+ | -------------------------------- | ----------------------------- | --------------- | ------------------------------------ |
279
+ | `GET /schema` | top-level (`res.flavors`) | `findex` | Rich (display names, units, domains) |
280
+ | `GET /elemental/metadata/schema` | nested (`res.schema.flavors`) | `fid` | Basic (name + type only) |
276
281
 
277
282
  The TypeScript client's `getSchema()` calls `/elemental/metadata/schema`,
278
283
  so the response nests data under `.schema`. The generated types may suggest
@@ -296,12 +301,12 @@ The flavor identifier has **different field names** depending on the endpoint:
296
301
  Same value, different key. Always use a fallback:
297
302
 
298
303
  ```typescript
299
- const articleFlavor = flavors.find(f => f.name === 'article');
304
+ const articleFlavor = flavors.find((f) => f.name === 'article');
300
305
  // Always use String() — safe for small IDs (12) and required for large ones
301
306
  const articleFid = String(articleFlavor?.fid ?? articleFlavor?.findex ?? '');
302
307
 
303
308
  // When building a FID lookup map:
304
- const fidMap = new Map(flavors.map(f => [String(f.fid ?? f.findex), f.name]));
309
+ const fidMap = new Map(flavors.map((f) => [String(f.fid ?? f.findex), f.name]));
305
310
  ```
306
311
 
307
312
  The `is_type` expression in `/elemental/find` always uses the `fid` key
@@ -352,14 +357,14 @@ const filingNeid = String(res.values[0].value).padStart(20, '0'); // "0492613234
352
357
  ```typescript
353
358
  // WRONG — PIDs are numbers, not strings:
354
359
  const values = await client.getPropertyValues({
355
- eids: JSON.stringify(['00416400910670863867']),
356
- pids: JSON.stringify(['name', 'country', 'industry']), // FAILS
360
+ eids: JSON.stringify(['00416400910670863867']),
361
+ pids: JSON.stringify(['name', 'country', 'industry']), // FAILS
357
362
  });
358
363
 
359
364
  // CORRECT — use numeric PIDs from getSchema():
360
365
  const values = await client.getPropertyValues({
361
- eids: JSON.stringify(['00416400910670863867']),
362
- pids: JSON.stringify([8, 313]), // 8=name, 313=country (from schema)
366
+ eids: JSON.stringify(['00416400910670863867']),
367
+ pids: JSON.stringify([8, 313]), // 8=name, 313=country (from schema)
363
368
  });
364
369
  ```
365
370
 
@@ -409,7 +414,7 @@ const res = await client.getPropertyValues({
409
414
  const docNeids = (res.values ?? []).map((v) => String(v.value).padStart(20, '0'));
410
415
  ```
411
416
 
412
- See the **cookbook-data** rule for a full "Get filings for a company" recipe.
417
+ See [cookbook-data.md](cookbook-data.md) in this skill for a full "Get filings for a company" recipe.
413
418
 
414
419
  ### Expression language pitfalls
415
420
 
@@ -455,14 +460,17 @@ source-specific schemas.
455
460
  `getPropertyValues()` with the relationship PID. Values are entity IDs
456
461
  that must be zero-padded to 20 characters.
457
462
 
458
- See the **cookbook-data** rule (news feed recipe) for a full example.
463
+ See [cookbook-data.md](cookbook-data.md) (news feed recipe) for a full example.
459
464
 
460
465
  ## Error Handling
461
466
 
462
467
  ```typescript
463
468
  try {
464
469
  const data = await client.findEntities({
465
- expression: JSON.stringify({ type: 'comparison', comparison: { operator: 'string_like', pid: 8, value: 'Apple' } }),
470
+ expression: JSON.stringify({
471
+ type: 'comparison',
472
+ comparison: { operator: 'string_like', pid: 8, value: 'Apple' },
473
+ }),
466
474
  limit: 5,
467
475
  });
468
476
  } catch (error) {
@@ -479,7 +487,9 @@ functions instead:
479
487
  import { getArticle } from '@yottagraph-app/elemental-api/client';
480
488
 
481
489
  const response = await getArticle(artid);
482
- if (response.status === 404) { /* handle not found */ }
490
+ if (response.status === 404) {
491
+ /* handle not found */
492
+ }
483
493
  ```
484
494
 
485
495
  ## Lovelace MCP Servers
@@ -490,25 +500,25 @@ data exploration. **Check your tool list** — if tools like
490
500
  exploration interface before writing code. If they don't appear, the
491
501
  servers aren't connected; use curl and the skill docs instead.
492
502
 
493
- | Server | What it provides |
494
- |---|---|
495
- | `lovelace-elemental` | Knowledge Graph: entities, relationships, events, sentiment, schema discovery |
496
- | `lovelace-stocks` | Stock/financial market data |
497
- | `lovelace-wiki` | Wikipedia entity enrichment |
498
- | `lovelace-polymarket` | Prediction market data |
503
+ | Server | What it provides |
504
+ | --------------------- | ----------------------------------------------------------------------------- |
505
+ | `lovelace-elemental` | Knowledge Graph: entities, relationships, events, sentiment, schema discovery |
506
+ | `lovelace-stocks` | Stock/financial market data |
507
+ | `lovelace-wiki` | Wikipedia entity enrichment |
508
+ | `lovelace-polymarket` | Prediction market data |
499
509
 
500
510
  ### MCP Tool Quick Reference
501
511
 
502
- | Tool | Purpose | Use to verify... |
503
- |---|---|---|
504
- | `elemental_get_schema` | Discover entity types (flavors), properties, and relationships | Flavor IDs, property IDs, data types |
505
- | `elemental_get_entity` | Look up entity by name or NEID; returns properties | Entity resolution, property shapes |
506
- | `elemental_get_related` | Related entities with type/relationship filters | Relationship types and traversal |
507
- | `elemental_get_relationships` | Relationship types and counts between two entities | Edge types between specific entities |
508
- | `elemental_graph_neighborhood` | Most influential neighbors of an entity | Graph connectivity |
509
- | `elemental_graph_sentiment` | Sentiment analysis from news articles | Sentiment data availability |
510
- | `elemental_get_events` | Events for an entity or by search query | Event categories and shapes |
511
- | `elemental_health` | Health check | Server connectivity |
512
+ | Tool | Purpose | Use to verify... |
513
+ | ------------------------------ | -------------------------------------------------------------- | ------------------------------------ |
514
+ | `elemental_get_schema` | Discover entity types (flavors), properties, and relationships | Flavor IDs, property IDs, data types |
515
+ | `elemental_get_entity` | Look up entity by name or NEID; returns properties | Entity resolution, property shapes |
516
+ | `elemental_get_related` | Related entities with type/relationship filters | Relationship types and traversal |
517
+ | `elemental_get_relationships` | Relationship types and counts between two entities | Edge types between specific entities |
518
+ | `elemental_graph_neighborhood` | Most influential neighbors of an entity | Graph connectivity |
519
+ | `elemental_graph_sentiment` | Sentiment analysis from news articles | Sentiment data availability |
520
+ | `elemental_get_events` | Events for an entity or by search query | Event categories and shapes |
521
+ | `elemental_health` | Health check | Server connectivity |
512
522
 
513
523
  MCP tools handle entity resolution, PID lookups, and NEID formatting
514
524
  automatically. Use them to discover IDs and verify data exists before
@@ -0,0 +1,14 @@
1
+ ### App (Nuxt UI + server routes)
2
+
3
+ Vercel auto-deploys on every push to `main`. Preview deployments are created for
4
+ other branches. The app is available at `{slug}.yottagraph.app`.
5
+
6
+ ### Agents (`agents/`)
7
+
8
+ Each subdirectory in `agents/` is a self-contained Python ADK agent. Deploy via
9
+ the Portal UI or `/deploy_agent` in Cursor.
10
+
11
+ ### MCP Servers (`mcp-servers/`)
12
+
13
+ Each subdirectory in `mcp-servers/` is a Python FastMCP server. Deploy via
14
+ the Portal UI or `/deploy_mcp` in Cursor.
@@ -1,8 +1,3 @@
1
- ---
2
- description: "DESIGN.md workflow, feature docs, starter app is placeholder. Read when starting work, planning features, or updating project design."
3
- alwaysApply: false
4
- ---
5
-
6
1
  # Design
7
2
 
8
3
  Read `DESIGN.md` before starting any work. It is the source of truth for project vision, architecture, and current implementation status.
@@ -58,4 +53,4 @@ If the user is implementing a change that has no feature doc, encourage them to
58
53
 
59
54
  **Exception for initial app builds:** When building a new app from scratch via `/build_my_app`, skip per-page feature docs. They add overhead during greenfield builds where the entire app is being created at once. Start using feature docs once the initial app is built and you're iterating on individual features.
60
55
 
61
- It is a good practice to close out one feature doc and start a new one as the focus of the work shifts. It is acceptable to be working with multiple feature docs at once, if the user is working on multiple unconnected or loosely connected features. The sections of the feature doc are flexible and you should add or remove sections to fit the needs of the project.
56
+ It is a good practice to close out one feature doc and start a new one as the focus of the work shifts. It is acceptable to be working with multiple feature docs at once, if the user is working on multiple unconnected or loosely connected features. The sections of the feature doc are flexible and you should add or remove sections to fit the needs of the project.
@@ -0,0 +1,10 @@
1
+ | Variable | Purpose | Default |
2
+ | ---------------------------------- | -------------------------------- | --------------------------------------- |
3
+ | `NUXT_PUBLIC_APP_ID` | Unique app identifier | derived from directory name |
4
+ | `NUXT_PUBLIC_APP_NAME` | Display name | derived from directory name |
5
+ | `NUXT_PUBLIC_USER_NAME` | Set to any value to bypass Auth0 | `dev-user` in local mode |
6
+ | `NUXT_PUBLIC_QUERY_SERVER_ADDRESS` | Query Server URL | read from `broadchurch.yaml` if present |
7
+ | `NUXT_PUBLIC_GATEWAY_URL` | Portal Gateway for agent chat | read from `broadchurch.yaml` if present |
8
+ | `NUXT_PUBLIC_TENANT_ORG_ID` | Auth0 org ID for this tenant | read from `broadchurch.yaml` if present |
9
+
10
+ See `.env.example` for the full list.
@@ -1,11 +1,6 @@
1
- ---
2
- description: "Git commit workflow and conventions. Apply when finishing implementation work, making commits, or troubleshooting git/pre-commit failures."
3
- alwaysApply: false
4
- ---
5
-
6
1
  # Git Workflow
7
2
 
8
- This rule applies both when an agent is committing its own work and when helping a user commit theirs.
3
+ This guidance applies both when an agent is committing its own work and when helping a user commit theirs.
9
4
 
10
5
  ## Committing
11
6
 
@@ -14,9 +9,9 @@ Commit when you finish a meaningful unit of work — a feature, a fix, a refacto
14
9
  ### Steps
15
10
 
16
11
  1. **Run formatting first** — this is required or the commit will fail:
17
- ```bash
18
- npm run format
19
- ```
12
+ ```bash
13
+ npm run format
14
+ ```
20
15
  2. **Stage all files** — always use `git add -A`.
21
16
  3. **Commit** with the message format below.
22
17
  4. **Verify the commit succeeded** with `git status`. If it failed, see Pre-commit Failure Troubleshooting below. Do not proceed until the commit is confirmed.
@@ -1,8 +1,3 @@
1
- ---
2
- description: Warns users not to modify package-managed instruction files in .cursor/
3
- alwaysApply: false
4
- ---
5
-
6
1
  # Aether Instructions Warning
7
2
 
8
3
  **You are editing a file managed by the `@yottagraph-app/aether-instructions` package.**
@@ -10,9 +5,8 @@ alwaysApply: false
10
5
  Package-managed files are tracked by `.cursor/.aether-instructions-manifest`.
11
6
  They will be **overwritten** when you run `/update_instructions`.
12
7
 
13
- This includes files under `.cursor/rules/`, `.cursor/commands/`,
14
- `.cursor/skills/`, **and the root `AGENTS.md`** (tracked as `root/AGENTS.md`
15
- in the manifest).
8
+ This includes files under `.cursor/commands/`, `.cursor/skills/`, **and the
9
+ root `AGENTS.md`** (tracked as `root/AGENTS.md` in the manifest).
16
10
 
17
11
  ## Do Not
18
12
 
@@ -20,7 +14,7 @@ in the manifest).
20
14
 
21
15
  ## To Customize
22
16
 
23
- If you need to modify a package-provided rule, command, or the root `AGENTS.md`:
17
+ If you need to modify a package-provided command, skill topic, or the root `AGENTS.md`:
24
18
 
25
19
  1. **Copy** the file to a new name
26
20
  2. Make your changes to the copy
@@ -30,8 +24,8 @@ If you need to modify a package-provided rule, command, or the root `AGENTS.md`:
30
24
  Examples:
31
25
 
32
26
  ```bash
33
- # Customize a rule
34
- cp .cursor/rules/data.mdc .cursor/rules/data_custom.mdc
27
+ # Customize a skill topic
28
+ cp .cursor/skills/aether/data.md .cursor/skills/aether/data_custom.md
35
29
 
36
30
  # Customize AGENTS.md
37
31
  cp AGENTS.md AGENTS.local.md
@@ -44,7 +38,7 @@ cp AGENTS.md AGENTS.local.md
44
38
  ## How It Works
45
39
 
46
40
  - `.cursor/.aether-instructions-manifest` lists every file installed by the
47
- package (one relative path per line, e.g. `rules/data.mdc`). Entries
41
+ package (one relative path per line, e.g. `skills/aether/data.md`). Entries
48
42
  prefixed with `root/` refer to files at the tenant repo root
49
43
  (currently only `root/AGENTS.md`).
50
44
  - `/update_instructions` deletes manifest entries, extracts fresh files from
@@ -0,0 +1,15 @@
1
+ ## Manual / Local Setup
2
+
3
+ Node 20 is the baseline (pinned in `.nvmrc`). Newer versions generally work.
4
+
5
+ ```bash
6
+ npm run init -- --local # creates .env with dev defaults (no Auth0)
7
+ npm install # all deps are public on npmjs.com -- no tokens needed
8
+ npm run dev # dev server on port 3000
9
+ ```
10
+
11
+ For the full interactive wizard (project name, Auth0, query server, etc.):
12
+
13
+ ```bash
14
+ npm run init # interactive, or --non-interactive for CI (see --help)
15
+ ```
@@ -1,9 +1,3 @@
1
- ---
2
- description: Rules for developing MCP servers in the mcp-servers/ directory
3
- globs: mcp-servers/**
4
- alwaysApply: false
5
- ---
6
-
7
1
  # MCP Server Development (FastMCP)
8
2
 
9
3
  This project supports developing and deploying custom MCP (Model Context Protocol) servers alongside the UI. Servers live in the `mcp-servers/` directory and deploy to Google Cloud Run via the `/deploy_mcp` command.
@@ -63,6 +57,7 @@ if __name__ == "__main__":
63
57
  ```
64
58
 
65
59
  Key rules:
60
+
66
61
  - The FastMCP instance MUST be named `mcp` — the Dockerfile runs `fastmcp run server:mcp` to find it
67
62
  - FastMCP 2.x takes a single positional name argument: `FastMCP("name")`. Do NOT pass `name=` or `description=` as keyword arguments
68
63
  - Use `@mcp.tool()` decorators to define tools
@@ -88,6 +83,7 @@ python -m fastmcp run server:mcp --transport streamable-http --host 0.0.0.0 --po
88
83
  ## Deployment
89
84
 
90
85
  Deploy with the `/deploy_mcp` Cursor command. This will:
86
+
91
87
  1. Build a container image via Cloud Build
92
88
  2. Deploy to Cloud Run with IAM authentication
93
89
  3. Grant invoker permissions to tenant and Portal service accounts
@@ -102,7 +98,7 @@ Once deployed, agents can connect to your MCP server. Add the Cloud Run URL to y
102
98
 
103
99
  - One server per data domain or external service
104
100
  - Keep tools focused and well-documented
105
- - Return structured data (dicts/lists), not raw strings — MCP handles serialization via the protocol. (This differs from ADK agent tools, which should return formatted strings because the LLM reads tool output directly — see the `agents` rule.)
101
+ - Return structured data (dicts/lists), not raw strings — MCP handles serialization via the protocol. (This differs from ADK agent tools, which should return formatted strings because the LLM reads tool output directly — see [agents.md](agents.md) in this skill.)
106
102
  - Handle errors by returning descriptive error messages in the response
107
103
  - Use environment variables for API keys and configuration (never hardcode secrets)
108
104
  - For secrets, use GCP Secret Manager and access at runtime
@@ -1,8 +1,3 @@
1
- ---
2
- description: Apply when working with user preferences, settings persistence, usePrefsStore, the Pref class, KV storage, or app namespacing (NUXT_PUBLIC_APP_ID).
3
- alwaysApply: false
4
- ---
5
-
6
1
  # User Preferences
7
2
 
8
3
  Preferences are persisted to Upstash Redis (KV) via `usePrefsStore()` from
@@ -28,7 +23,7 @@ const myPref = new Pref<string>(docPath, 'fieldName', 'defaultValue');
28
23
  await myPref.initialize();
29
24
 
30
25
  myPref.r.value; // reactive ref (use in templates)
31
- myPref.v; // getter shorthand
26
+ myPref.v; // getter shorthand
32
27
  myPref.set('new value'); // persists to KV
33
28
  ```
34
29
 
@@ -79,13 +74,13 @@ routes directly from client-side code:
79
74
  ```typescript
80
75
  // Read
81
76
  const value = await $fetch('/api/kv/read', {
82
- params: { docPath: '/users/abc/settings', fieldName: 'theme' }
77
+ params: { docPath: '/users/abc/settings', fieldName: 'theme' },
83
78
  });
84
79
 
85
80
  // Write
86
81
  await $fetch('/api/kv/write', {
87
- method: 'POST',
88
- body: { docPath: '/users/abc/settings', fieldName: 'theme', value: '"dark"' }
82
+ method: 'POST',
83
+ body: { docPath: '/users/abc/settings', fieldName: 'theme', value: '"dark"' },
89
84
  });
90
85
  ```
91
86
 
@@ -121,8 +116,8 @@ function useMyFeaturePrefs() {
121
116
 
122
117
  ## Scope Guidance
123
118
 
124
- | App-specific | Global |
125
- |---|---|
126
- | Layout prefs, favorites | Language |
127
- | Watchlists, feature settings | Accessibility |
128
- | Feature-specific settings | Timezone, notifications |
119
+ | App-specific | Global |
120
+ | ---------------------------- | ----------------------- |
121
+ | Layout prefs, favorites | Language |
122
+ | Watchlists, feature settings | Accessibility |
123
+ | Feature-specific settings | Timezone, notifications |
@@ -0,0 +1,48 @@
1
+ # Server routes: Elemental API (Query Server)
2
+
3
+ Server routes can call the Elemental API through the Portal Gateway proxy,
4
+ just like client-side code does. The gateway URL, tenant org ID, and API key
5
+ are available via `useRuntimeConfig()`.
6
+
7
+ **NEVER use `readFileSync('broadchurch.yaml')` in server routes.** The YAML
8
+ file is read at build time by `nuxt.config.ts` and its values flow into
9
+ `runtimeConfig`. Nitro serverless functions (Vercel) don't bundle arbitrary
10
+ project files — `readFileSync` will crash with ENOENT in production even
11
+ though it works locally.
12
+
13
+ ```typescript
14
+ export default defineEventHandler(async (event) => {
15
+ const { public: config } = useRuntimeConfig();
16
+
17
+ const gatewayUrl = config.gatewayUrl; // Portal Gateway base URL
18
+ const orgId = config.tenantOrgId; // Tenant org ID (path segment)
19
+ const apiKey = config.qsApiKey; // API key for X-Api-Key header
20
+
21
+ if (!gatewayUrl || !orgId) {
22
+ throw createError({ statusCode: 503, statusMessage: 'Gateway not configured' });
23
+ }
24
+
25
+ const res = await $fetch(`${gatewayUrl}/api/qs/${orgId}/entities/search`, {
26
+ method: 'POST',
27
+ headers: {
28
+ 'Content-Type': 'application/json',
29
+ ...(apiKey && { 'X-Api-Key': apiKey }),
30
+ },
31
+ body: { queries: [{ queryId: 1, query: 'Microsoft' }], maxResults: 5 },
32
+ });
33
+
34
+ return res;
35
+ });
36
+ ```
37
+
38
+ Available runtime config keys (all under `runtimeConfig.public`):
39
+
40
+ | Key | Source | Purpose |
41
+ | -------------------- | ----------------------------------------- | ------------------------------ |
42
+ | `gatewayUrl` | `broadchurch.yaml` → `gateway.url` | Portal Gateway base URL |
43
+ | `tenantOrgId` | `broadchurch.yaml` → `tenant.org_id` | Tenant ID for API path |
44
+ | `qsApiKey` | `broadchurch.yaml` → `gateway.qs_api_key` | API key sent as `X-Api-Key` |
45
+ | `queryServerAddress` | `broadchurch.yaml` → `query_server.url` | Direct QS URL (prefer gateway) |
46
+
47
+ Build the request URL as `{gatewayUrl}/api/qs/{tenantOrgId}/{endpoint}`.
48
+ See [data.md](data.md) in this skill for endpoint reference and response shapes.
@@ -0,0 +1,60 @@
1
+ # Nitro Server Routes
2
+
3
+ The `server/` directory contains Nuxt's Nitro server layer. These routes deploy
4
+ with the app to Vercel -- they are NOT a separate service. They handle
5
+ server-side concerns like KV storage, database access, and image proxying
6
+ that can't run in the browser.
7
+
8
+ ## Directory Layout
9
+
10
+ ```
11
+ server/
12
+ ├── api/
13
+ │ ├── kv/ # KV (Upstash Redis) CRUD — read, write, delete, documents, status
14
+ │ └── avatar/[url].ts # Avatar image proxy
15
+ └── utils/
16
+ ├── redis.ts # Upstash Redis client (lazy-init from KV_REST_API_URL)
17
+ ├── neon.ts # Neon Postgres client (lazy-init from DATABASE_URL) — create if missing when Neon is provisioned
18
+ └── cookies.ts # Cookie handling (@hapi/iron)
19
+ ```
20
+
21
+ For KV and Neon Postgres access (client usage, provisioning checks, creating
22
+ tables, handling missing credentials gracefully), see
23
+ [storage.md](storage.md) in this skill. For calling the platform Query
24
+ Server from Nitro routes, see [server-data.md](server-data.md) in this
25
+ skill.
26
+
27
+ ## Adding Routes
28
+
29
+ Follow Nitro file-based routing. The filename determines the HTTP method and
30
+ path:
31
+
32
+ ```
33
+ server/api/my-resource.get.ts → GET /api/my-resource
34
+ server/api/my-resource.post.ts → POST /api/my-resource
35
+ server/api/my-resource/[id].get.ts → GET /api/my-resource/:id
36
+ ```
37
+
38
+ Route handler pattern:
39
+
40
+ ```typescript
41
+ export default defineEventHandler(async (event) => {
42
+ const params = getQuery(event); // query string
43
+ const body = await readBody(event); // POST body
44
+ const id = getRouterParam(event, 'id'); // path params
45
+
46
+ // ... implementation ...
47
+ return { result: 'data' };
48
+ });
49
+ ```
50
+
51
+ ## Key Differences from Client-Side Code
52
+
53
+ - Server routes run on the server (Node.js), not in the browser
54
+ - They have access to Redis, Neon Postgres, secrets, and server-only APIs
55
+ - They do NOT have access to Vue composables, Vuetify, or any client-side code
56
+ - Use `defineEventHandler`, not Vue component patterns
57
+
58
+ See [architecture.md](architecture.md) in this skill for the full data
59
+ architecture overview, [storage.md](storage.md) for KV/Postgres patterns,
60
+ and [pref.md](pref.md) for client-side KV preferences.
@@ -1,8 +1,3 @@
1
- ---
2
- description: "Error recovery and build failure troubleshooting. Apply when something broke, build failed, npm run build errors, or user wants to restore previous behavior."
3
- alwaysApply: false
4
- ---
5
-
6
1
  # Restoring Broken Functionality from Git History
7
2
 
8
3
  **Trigger phrases:** "this used to work", "this broke", "it was working before", "I want it back the way it was", "it looked right before", or similar.
@@ -12,6 +7,7 @@ alwaysApply: false
12
7
  ## 1. Gather Context
13
8
 
14
9
  Ask the user:
10
+
15
11
  - What specifically broke or changed?
16
12
  - When do they remember it last working? (A rough timeframe, a branch, a specific action, etc.)
17
13
 
@@ -33,7 +29,7 @@ Show the user the combined result (current code + restored pieces) and have them
33
29
 
34
30
  ## 5. Commit the Restoration
35
31
 
36
- Only after the user confirms the restored version is correct, follow the standard git workflow (see `git-support.mdc`) to commit the changes.
32
+ Only after the user confirms the restored version is correct, follow the standard git workflow (see [git-support.md](git-support.md) in this skill) to commit the changes.
37
33
 
38
34
  # Common Build Errors
39
35
 
@@ -49,7 +45,7 @@ import { myHelper } from '~/utils/myHelper';
49
45
 
50
46
  ### `Type 'X' is not assignable to type 'Y'`
51
47
 
52
- Usually an API response shape mismatch. Common case: `getSchema()` nests data under `response.schema` but TypeScript types suggest top-level access. See the `data` rule's API Gotchas section.
48
+ Usually an API response shape mismatch. Common case: `getSchema()` nests data under `response.schema` but TypeScript types suggest top-level access. See [data.md](data.md)'s API Gotchas section in this skill.
53
49
 
54
50
  ### `SyntaxError` or blank page with "missing export"
55
51