@yottagraph-app/aether-instructions 1.1.22 → 1.1.23
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/commands/build_my_app.md +51 -4
- package/package.json +1 -1
- package/rules/api.mdc +56 -0
- package/rules/cookbook.mdc +163 -1
package/commands/build_my_app.md
CHANGED
|
@@ -92,7 +92,47 @@ Key capabilities:
|
|
|
92
92
|
|
|
93
93
|
---
|
|
94
94
|
|
|
95
|
-
## Step 4:
|
|
95
|
+
## Step 4: Verify Data Availability
|
|
96
|
+
|
|
97
|
+
Before designing UX, verify that the data your app needs actually exists
|
|
98
|
+
in the knowledge graph. This prevents building features around empty data.
|
|
99
|
+
|
|
100
|
+
**If MCP tools are available:**
|
|
101
|
+
|
|
102
|
+
```
|
|
103
|
+
elemental_get_schema() → list entity types, confirm your target types exist
|
|
104
|
+
elemental_get_entity(entity="Microsoft") → verify entity lookup works with a known entity
|
|
105
|
+
elemental_get_entity(entity="Apple Inc") → try another known entity
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
If schema calls succeed but entity lookups return "not found," that means
|
|
109
|
+
the entity type exists in the schema but has no data. That's a **data
|
|
110
|
+
issue**, not a broken server. Try different, well-known entity names.
|
|
111
|
+
|
|
112
|
+
**If MCP tools are NOT available, use curl:**
|
|
113
|
+
|
|
114
|
+
```bash
|
|
115
|
+
# Read credentials from broadchurch.yaml
|
|
116
|
+
GW=$(grep 'url:' broadchurch.yaml | head -1 | sed 's/.*"\(.*\)".*/\1/')
|
|
117
|
+
ORG=$(grep 'org_id:' broadchurch.yaml | sed 's/.*"\(.*\)".*/\1/')
|
|
118
|
+
KEY=$(grep 'qs_api_key:' broadchurch.yaml | sed 's/.*"\(.*\)".*/\1/')
|
|
119
|
+
|
|
120
|
+
# List entity types
|
|
121
|
+
curl -s "$GW/api/qs/$ORG/elemental/metadata/schema" -H "X-Api-Key: $KEY"
|
|
122
|
+
|
|
123
|
+
# Search for a known entity
|
|
124
|
+
curl -s "$GW/api/qs/$ORG/entities/search" \
|
|
125
|
+
-X POST -H "Content-Type: application/json" -H "X-Api-Key: $KEY" \
|
|
126
|
+
-d '{"queries":[{"queryId":1,"query":"Microsoft"}],"maxResults":3,"includeNames":true}'
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
**Document what you find.** If certain entity types have sparse data, note
|
|
130
|
+
it in your UX plan. Design features around data that actually exists, and
|
|
131
|
+
mark aspirational features (that need more data) as future work.
|
|
132
|
+
|
|
133
|
+
---
|
|
134
|
+
|
|
135
|
+
## Step 5: Design the UX
|
|
96
136
|
|
|
97
137
|
Based on the brief, think about the right UX for this specific problem. Do NOT default to a sidebar-with-tabs layout. Consider:
|
|
98
138
|
|
|
@@ -116,7 +156,7 @@ Present the plan to the user and ask for approval before proceeding.
|
|
|
116
156
|
|
|
117
157
|
---
|
|
118
158
|
|
|
119
|
-
## Step
|
|
159
|
+
## Step 6: Build
|
|
120
160
|
|
|
121
161
|
Implement the plan:
|
|
122
162
|
|
|
@@ -128,6 +168,13 @@ Implement the plan:
|
|
|
128
168
|
6. Use Vuetify components and the project's dark theme
|
|
129
169
|
7. Update `DESIGN.md` with what you built
|
|
130
170
|
|
|
171
|
+
**Use the pre-built platform utilities:**
|
|
172
|
+
|
|
173
|
+
- `useElementalSchema()` — schema discovery with caching, flavor/PID lookup helpers
|
|
174
|
+
- `buildGatewayUrl()`, `getApiKey()`, `padNeid()` from `utils/elementalHelpers`
|
|
175
|
+
- `searchEntities()`, `getEntityName()` from `utils/elementalHelpers`
|
|
176
|
+
- `useElementalClient()` from `@yottagraph-app/elemental-api/client`
|
|
177
|
+
|
|
131
178
|
**Follow the project's coding conventions:**
|
|
132
179
|
|
|
133
180
|
- `<script setup lang="ts">` for all Vue components
|
|
@@ -136,7 +183,7 @@ Implement the plan:
|
|
|
136
183
|
|
|
137
184
|
---
|
|
138
185
|
|
|
139
|
-
## Step
|
|
186
|
+
## Step 7: Verify
|
|
140
187
|
|
|
141
188
|
After building, check dependencies are installed and run a build:
|
|
142
189
|
|
|
@@ -151,7 +198,7 @@ Then suggest the user run `npm run dev` to preview their app locally.
|
|
|
151
198
|
|
|
152
199
|
---
|
|
153
200
|
|
|
154
|
-
## Step
|
|
201
|
+
## Step 8: Next Steps
|
|
155
202
|
|
|
156
203
|
> Your app is taking shape! Here's what you can do next:
|
|
157
204
|
>
|
package/package.json
CHANGED
package/rules/api.mdc
CHANGED
|
@@ -50,6 +50,29 @@ elemental_get_related(entity="Apple",
|
|
|
50
50
|
MCP tells you the correct flavor IDs, property IDs, and data shapes. Use
|
|
51
51
|
these to inform your REST implementation.
|
|
52
52
|
|
|
53
|
+
**Verify MCP is working with known-good queries:**
|
|
54
|
+
|
|
55
|
+
```
|
|
56
|
+
elemental_get_schema() → should return flavors + properties
|
|
57
|
+
elemental_get_entity(entity="Microsoft") → should resolve to a company
|
|
58
|
+
elemental_get_entity(entity="Apple Inc") → another known entity
|
|
59
|
+
elemental_health() → server health check
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
**Interpreting MCP errors — do NOT assume the server is broken:**
|
|
63
|
+
|
|
64
|
+
- `entity not found` or 404 in entity lookup → the entity doesn't exist
|
|
65
|
+
in the knowledge graph, not a connectivity problem. Try a different entity.
|
|
66
|
+
- `failed to get property values: 404` → the entity was resolved but has
|
|
67
|
+
no data for those properties. The MCP server is working correctly.
|
|
68
|
+
- Schema calls succeed but entity calls fail → data is sparse for that
|
|
69
|
+
entity type. Try well-known entities (Microsoft, Apple Inc, JPMorgan).
|
|
70
|
+
- If `elemental_health()` fails → actual connectivity problem.
|
|
71
|
+
|
|
72
|
+
**Key insight:** A 404 from an MCP entity/property call means "not found,"
|
|
73
|
+
not "server broken." Always test with known entities before concluding
|
|
74
|
+
the server is down.
|
|
75
|
+
|
|
53
76
|
### Step 2: curl (verify exact request/response shapes)
|
|
54
77
|
|
|
55
78
|
MCP doesn't cover every REST endpoint (e.g. `/elemental/find` expressions).
|
|
@@ -101,11 +124,44 @@ All other endpoints accept `application/json`.
|
|
|
101
124
|
**Interpreting errors:** 400 = expression syntax is wrong. 500 = expression
|
|
102
125
|
is valid but the query failed (wrong PID, unsupported operator for that
|
|
103
126
|
property type). 200 + empty `eids` = query worked but no results match.
|
|
127
|
+
404 from entity/property endpoints = entity or data doesn't exist (not a
|
|
128
|
+
server error). Always test with known entities (e.g. search for "Microsoft")
|
|
129
|
+
before assuming the API is broken.
|
|
104
130
|
|
|
105
131
|
### Step 3: Implement with confidence
|
|
106
132
|
|
|
107
133
|
Now write your composable or server route, knowing the exact API shapes.
|
|
108
134
|
|
|
135
|
+
## Pre-Built Helpers
|
|
136
|
+
|
|
137
|
+
The template includes composables and utilities that handle common
|
|
138
|
+
Elemental API patterns. **Use these instead of writing from scratch:**
|
|
139
|
+
|
|
140
|
+
### `useElementalSchema()` — Schema Discovery with Caching
|
|
141
|
+
|
|
142
|
+
```typescript
|
|
143
|
+
const { flavors, properties, flavorByName, pidByName, refresh } = useElementalSchema();
|
|
144
|
+
await refresh(); // fetches once, then cached
|
|
145
|
+
const articleFid = flavorByName('article'); // → number | null
|
|
146
|
+
const namePid = pidByName('name'); // → typically 8
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
Handles the dual response shapes (`res.schema.flavors` vs `res.flavors`)
|
|
150
|
+
and the `fid`/`findex` naming inconsistency automatically.
|
|
151
|
+
|
|
152
|
+
### `utils/elementalHelpers` — Gateway URL Helpers
|
|
153
|
+
|
|
154
|
+
```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"
|
|
160
|
+
|
|
161
|
+
const results = await searchEntities('Microsoft'); // batch name search
|
|
162
|
+
const name = await getEntityName(neid); // display name lookup
|
|
163
|
+
```
|
|
164
|
+
|
|
109
165
|
## Client Usage
|
|
110
166
|
|
|
111
167
|
All API calls go through `useElementalClient()` from `@yottagraph-app/elemental-api/client`.
|
package/rules/cookbook.mdc
CHANGED
|
@@ -388,7 +388,169 @@ Two-column layout with selectable list and detail panel.
|
|
|
388
388
|
</script>
|
|
389
389
|
```
|
|
390
390
|
|
|
391
|
-
## 7.
|
|
391
|
+
## 7. News Feed — Recent Articles with Sentiment
|
|
392
|
+
|
|
393
|
+
Fetch recent articles from the knowledge graph. Uses `useElementalSchema()`
|
|
394
|
+
for runtime flavor/PID discovery and `buildGatewayUrl()` for gateway access.
|
|
395
|
+
|
|
396
|
+
```vue
|
|
397
|
+
<template>
|
|
398
|
+
<div class="d-flex flex-column fill-height pa-4">
|
|
399
|
+
<h1 class="text-h5 mb-4">Recent News</h1>
|
|
400
|
+
<v-alert v-if="error" type="error" variant="tonal" class="mb-4" closable>
|
|
401
|
+
{{ error }}
|
|
402
|
+
</v-alert>
|
|
403
|
+
<v-progress-linear v-if="loading" indeterminate class="mb-4" />
|
|
404
|
+
<v-list v-if="articles.length" lines="three">
|
|
405
|
+
<v-list-item v-for="a in articles" :key="a.neid">
|
|
406
|
+
<template #title>
|
|
407
|
+
<span>{{ a.name || a.neid }}</span>
|
|
408
|
+
<v-chip
|
|
409
|
+
v-if="a.sentiment"
|
|
410
|
+
size="x-small"
|
|
411
|
+
class="ml-2"
|
|
412
|
+
:color="a.sentiment > 0 ? 'success' : a.sentiment < 0 ? 'error' : 'grey'"
|
|
413
|
+
>
|
|
414
|
+
{{ a.sentiment > 0 ? 'Bullish' : a.sentiment < 0 ? 'Bearish' : 'Neutral' }}
|
|
415
|
+
</v-chip>
|
|
416
|
+
</template>
|
|
417
|
+
<template #subtitle>{{ a.neid }}</template>
|
|
418
|
+
</v-list-item>
|
|
419
|
+
</v-list>
|
|
420
|
+
<v-empty-state
|
|
421
|
+
v-else-if="!loading"
|
|
422
|
+
headline="No articles found"
|
|
423
|
+
icon="mdi-newspaper-variant-outline"
|
|
424
|
+
/>
|
|
425
|
+
</div>
|
|
426
|
+
</template>
|
|
427
|
+
|
|
428
|
+
<script setup lang="ts">
|
|
429
|
+
import { useElementalClient } from '@yottagraph-app/elemental-api/client';
|
|
430
|
+
import { padNeid } from '~/utils/elementalHelpers';
|
|
431
|
+
|
|
432
|
+
const client = useElementalClient();
|
|
433
|
+
const { flavorByName, pidByName, refresh: loadSchema } = useElementalSchema();
|
|
434
|
+
|
|
435
|
+
const articles = ref<{ neid: string; name: string; sentiment: number | null }[]>([]);
|
|
436
|
+
const loading = ref(false);
|
|
437
|
+
const error = ref<string | null>(null);
|
|
438
|
+
|
|
439
|
+
onMounted(async () => {
|
|
440
|
+
loading.value = true;
|
|
441
|
+
try {
|
|
442
|
+
await loadSchema();
|
|
443
|
+
const articleFid = flavorByName('article');
|
|
444
|
+
if (!articleFid) {
|
|
445
|
+
error.value = 'Article entity type not found in schema';
|
|
446
|
+
return;
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
const res = await client.findEntities({
|
|
450
|
+
expression: JSON.stringify({ type: 'is_type', is_type: { fid: articleFid } }),
|
|
451
|
+
limit: 20,
|
|
452
|
+
});
|
|
453
|
+
const neids: string[] = (res as any).eids ?? [];
|
|
454
|
+
|
|
455
|
+
if (!neids.length) {
|
|
456
|
+
return;
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
const namePid = pidByName('name');
|
|
460
|
+
const sentimentPid = pidByName('sentiment');
|
|
461
|
+
const pids = [namePid, sentimentPid].filter((p): p is number => p !== null);
|
|
462
|
+
|
|
463
|
+
const props = await client.getPropertyValues({
|
|
464
|
+
eids: JSON.stringify(neids),
|
|
465
|
+
pids: JSON.stringify(pids),
|
|
466
|
+
});
|
|
467
|
+
|
|
468
|
+
const valueMap = new Map<string, Record<number, any>>();
|
|
469
|
+
for (const v of (props as any).values ?? []) {
|
|
470
|
+
const eid = padNeid(v.eid ?? v.entity_id ?? '');
|
|
471
|
+
if (!valueMap.has(eid)) valueMap.set(eid, {});
|
|
472
|
+
valueMap.get(eid)![v.pid] = v.value;
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
articles.value = neids.map((neid) => {
|
|
476
|
+
const vals = valueMap.get(neid) ?? {};
|
|
477
|
+
return {
|
|
478
|
+
neid,
|
|
479
|
+
name: namePid ? (vals[namePid] as string) ?? neid : neid,
|
|
480
|
+
sentiment: sentimentPid ? (vals[sentimentPid] as number) ?? null : null,
|
|
481
|
+
};
|
|
482
|
+
});
|
|
483
|
+
} catch (e: any) {
|
|
484
|
+
error.value = e.message || 'Failed to load articles';
|
|
485
|
+
} finally {
|
|
486
|
+
loading.value = false;
|
|
487
|
+
}
|
|
488
|
+
});
|
|
489
|
+
</script>
|
|
490
|
+
```
|
|
491
|
+
|
|
492
|
+
## 8. Entity Search with Gateway Helpers
|
|
493
|
+
|
|
494
|
+
Simpler version of recipe #1 using the pre-built `searchEntities()` helper.
|
|
495
|
+
|
|
496
|
+
```vue
|
|
497
|
+
<template>
|
|
498
|
+
<div class="d-flex flex-column fill-height pa-4">
|
|
499
|
+
<h1 class="text-h5 mb-4">Entity Search</h1>
|
|
500
|
+
<v-text-field
|
|
501
|
+
v-model="query"
|
|
502
|
+
label="Search entities"
|
|
503
|
+
prepend-inner-icon="mdi-magnify"
|
|
504
|
+
variant="outlined"
|
|
505
|
+
@keyup.enter="search"
|
|
506
|
+
:loading="loading"
|
|
507
|
+
/>
|
|
508
|
+
<v-alert v-if="error" type="error" variant="tonal" class="mt-2" closable>
|
|
509
|
+
{{ error }}
|
|
510
|
+
</v-alert>
|
|
511
|
+
<v-list v-if="results.length" class="mt-4">
|
|
512
|
+
<v-list-item
|
|
513
|
+
v-for="r in results"
|
|
514
|
+
:key="r.neid"
|
|
515
|
+
:title="r.name"
|
|
516
|
+
:subtitle="r.neid"
|
|
517
|
+
/>
|
|
518
|
+
</v-list>
|
|
519
|
+
<v-empty-state
|
|
520
|
+
v-else-if="searched && !loading"
|
|
521
|
+
headline="No results"
|
|
522
|
+
icon="mdi-magnify-remove-outline"
|
|
523
|
+
/>
|
|
524
|
+
</div>
|
|
525
|
+
</template>
|
|
526
|
+
|
|
527
|
+
<script setup lang="ts">
|
|
528
|
+
import { searchEntities } from '~/utils/elementalHelpers';
|
|
529
|
+
|
|
530
|
+
const query = ref('');
|
|
531
|
+
const results = ref<{ neid: string; name: string }[]>([]);
|
|
532
|
+
const loading = ref(false);
|
|
533
|
+
const error = ref<string | null>(null);
|
|
534
|
+
const searched = ref(false);
|
|
535
|
+
|
|
536
|
+
async function search() {
|
|
537
|
+
if (!query.value.trim()) return;
|
|
538
|
+
loading.value = true;
|
|
539
|
+
error.value = null;
|
|
540
|
+
searched.value = true;
|
|
541
|
+
try {
|
|
542
|
+
results.value = await searchEntities(query.value.trim());
|
|
543
|
+
} catch (e: any) {
|
|
544
|
+
error.value = e.message || 'Search failed';
|
|
545
|
+
results.value = [];
|
|
546
|
+
} finally {
|
|
547
|
+
loading.value = false;
|
|
548
|
+
}
|
|
549
|
+
}
|
|
550
|
+
</script>
|
|
551
|
+
```
|
|
552
|
+
|
|
553
|
+
## 9. Get Filings for a Company
|
|
392
554
|
|
|
393
555
|
Fetch Edgar filings (or any relationship-linked documents) for an organization.
|
|
394
556
|
Uses `$fetch` for the initial entity search because `POST /entities/search`
|