@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,489 @@
1
+ ---
2
+ description: "Copy-paste UI patterns for common pages: entity search, data table, form, chart, master-detail. Read when building new pages or features."
3
+ alwaysApply: false
4
+ ---
5
+
6
+ # UI Pattern Cookbook
7
+
8
+ Copy-paste patterns using the project's actual composables and Vuetify components. Adapt to your needs.
9
+
10
+ ## 1. Entity Search Page
11
+
12
+ Search for entities by name and display results.
13
+
14
+ ```vue
15
+ <template>
16
+ <div class="d-flex flex-column fill-height pa-4">
17
+ <h1 class="text-h5 mb-4">Entity Search</h1>
18
+ <v-text-field
19
+ v-model="query"
20
+ label="Search entities"
21
+ prepend-inner-icon="mdi-magnify"
22
+ variant="outlined"
23
+ @keyup.enter="search"
24
+ :loading="loading"
25
+ />
26
+ <v-alert v-if="error" type="error" variant="tonal" class="mt-2" closable>
27
+ {{ error }}
28
+ </v-alert>
29
+ <v-list v-if="results.length" class="mt-4">
30
+ <v-list-item
31
+ v-for="(neid, i) in results"
32
+ :key="neid"
33
+ :title="names[i] || neid"
34
+ :subtitle="neid"
35
+ />
36
+ </v-list>
37
+ <v-empty-state
38
+ v-else-if="searched && !loading"
39
+ headline="No results"
40
+ icon="mdi-magnify-remove-outline"
41
+ />
42
+ </div>
43
+ </template>
44
+
45
+ <script setup lang="ts">
46
+ import { useElementalClient } from '@yottagraph-app/elemental-api/client';
47
+
48
+ const client = useElementalClient();
49
+ const query = ref('');
50
+ const results = ref<string[]>([]);
51
+ const names = ref<string[]>([]);
52
+ const loading = ref(false);
53
+ const error = ref<string | null>(null);
54
+ const searched = ref(false);
55
+
56
+ async function search() {
57
+ if (!query.value.trim()) return;
58
+ loading.value = true;
59
+ error.value = null;
60
+ searched.value = true;
61
+ try {
62
+ const res = await client.getNEID({
63
+ entityName: query.value.trim(),
64
+ maxResults: 10,
65
+ includeNames: true,
66
+ });
67
+ results.value = res.neids || [];
68
+ names.value = res.names || [];
69
+ } catch (e: any) {
70
+ error.value = e.message || 'Search failed';
71
+ results.value = [];
72
+ } finally {
73
+ loading.value = false;
74
+ }
75
+ }
76
+ </script>
77
+ ```
78
+
79
+ ## 2. Data Table Page
80
+
81
+ Sortable table with loading, empty, and error states.
82
+
83
+ ```vue
84
+ <template>
85
+ <div class="d-flex flex-column fill-height pa-4">
86
+ <h1 class="text-h5 mb-4">Data Table</h1>
87
+ <v-alert v-if="error" type="error" variant="tonal" class="mb-4" closable>
88
+ {{ error }}
89
+ </v-alert>
90
+ <v-data-table
91
+ :headers="headers"
92
+ :items="items"
93
+ :loading="loading"
94
+ density="comfortable"
95
+ hover
96
+ >
97
+ <template v-slot:item.actions="{ item }">
98
+ <v-btn icon size="small" variant="text" @click="onView(item)">
99
+ <v-icon>mdi-eye</v-icon>
100
+ </v-btn>
101
+ </template>
102
+ <template v-slot:no-data>
103
+ <v-empty-state headline="No data" icon="mdi-database-off" />
104
+ </template>
105
+ </v-data-table>
106
+ </div>
107
+ </template>
108
+
109
+ <script setup lang="ts">
110
+ const headers = [
111
+ { title: 'Name', key: 'name', sortable: true },
112
+ { title: 'Type', key: 'type', sortable: true },
113
+ { title: 'Updated', key: 'updatedAt', sortable: true },
114
+ { title: '', key: 'actions', sortable: false, width: 60 },
115
+ ];
116
+
117
+ const items = ref<any[]>([]);
118
+ const loading = ref(false);
119
+ const error = ref<string | null>(null);
120
+
121
+ function onView(item: any) {
122
+ // Handle row action
123
+ }
124
+
125
+ onMounted(async () => {
126
+ loading.value = true;
127
+ try {
128
+ // items.value = await fetchData();
129
+ } catch (e: any) {
130
+ error.value = e.message || 'Failed to load';
131
+ } finally {
132
+ loading.value = false;
133
+ }
134
+ });
135
+ </script>
136
+ ```
137
+
138
+ ## 3. Form with Validation
139
+
140
+ Form with field validation, submit handler, and user feedback.
141
+
142
+ ```vue
143
+ <template>
144
+ <v-container class="py-6" style="max-width: 600px">
145
+ <h1 class="text-h5 mb-4">Settings</h1>
146
+ <v-form ref="formRef" v-model="valid" @submit.prevent="submit">
147
+ <v-text-field
148
+ v-model="form.name"
149
+ label="Name"
150
+ :rules="[rules.required]"
151
+ variant="outlined"
152
+ class="mb-3"
153
+ />
154
+ <v-text-field
155
+ v-model="form.email"
156
+ label="Email"
157
+ :rules="[rules.required, rules.email]"
158
+ variant="outlined"
159
+ class="mb-3"
160
+ />
161
+ <v-select
162
+ v-model="form.role"
163
+ :items="['Admin', 'Editor', 'Viewer']"
164
+ label="Role"
165
+ variant="outlined"
166
+ class="mb-3"
167
+ />
168
+ <v-btn type="submit" color="primary" :loading="saving" :disabled="!valid">
169
+ Save
170
+ </v-btn>
171
+ </v-form>
172
+ </v-container>
173
+ </template>
174
+
175
+ <script setup lang="ts">
176
+ import { useNotification } from '~/composables/useNotification';
177
+
178
+ const { showSuccess, showError } = useNotification();
179
+ const formRef = ref();
180
+ const valid = ref(false);
181
+ const saving = ref(false);
182
+
183
+ const form = reactive({ name: '', email: '', role: 'Viewer' });
184
+
185
+ const rules = {
186
+ required: (v: string) => !!v || 'Required',
187
+ email: (v: string) => /.+@.+\..+/.test(v) || 'Invalid email',
188
+ };
189
+
190
+ async function submit() {
191
+ const { valid: isValid } = await formRef.value.validate();
192
+ if (!isValid) return;
193
+ saving.value = true;
194
+ try {
195
+ // await saveSettings(form);
196
+ showSuccess('Settings saved');
197
+ } catch (e: any) {
198
+ showError(e.message || 'Failed to save');
199
+ } finally {
200
+ saving.value = false;
201
+ }
202
+ }
203
+ </script>
204
+ ```
205
+
206
+ ## 4. Chart Page
207
+
208
+ Chart.js chart with dark theme colors.
209
+
210
+ ```vue
211
+ <template>
212
+ <div class="d-flex flex-column fill-height pa-4">
213
+ <h1 class="text-h5 mb-4">Analytics</h1>
214
+ <v-card class="flex-grow-1 pa-4">
215
+ <canvas ref="chartCanvas" />
216
+ </v-card>
217
+ </div>
218
+ </template>
219
+
220
+ <script setup lang="ts">
221
+ import { Chart, registerables } from 'chart.js';
222
+ Chart.register(...registerables);
223
+
224
+ const chartCanvas = ref<HTMLCanvasElement | null>(null);
225
+ let chart: Chart | null = null;
226
+
227
+ onMounted(() => {
228
+ if (!chartCanvas.value) return;
229
+ chart = new Chart(chartCanvas.value, {
230
+ type: 'line',
231
+ data: {
232
+ labels: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun'],
233
+ datasets: [
234
+ {
235
+ label: 'Mentions',
236
+ data: [12, 19, 3, 5, 2, 15],
237
+ borderColor: '#3fea00',
238
+ backgroundColor: 'rgba(63, 234, 0, 0.1)',
239
+ fill: true,
240
+ tension: 0.3,
241
+ },
242
+ ],
243
+ },
244
+ options: {
245
+ responsive: true,
246
+ maintainAspectRatio: false,
247
+ scales: {
248
+ x: { ticks: { color: '#999' }, grid: { color: '#222' } },
249
+ y: { ticks: { color: '#999' }, grid: { color: '#222' } },
250
+ },
251
+ plugins: {
252
+ legend: { labels: { color: '#e5e5e5' } },
253
+ },
254
+ },
255
+ });
256
+ });
257
+
258
+ onUnmounted(() => chart?.destroy());
259
+ </script>
260
+ ```
261
+
262
+ Note: `chart.js` must be installed (`npm install chart.js`). Use the brand colors: `#3fea00` (green), `#003bff` (blue), `#ff5c00` (orange).
263
+
264
+ ## 5. Dialog
265
+
266
+ Confirmation or form dialog. The global `VCard` default is `variant: 'outlined'` (transparent background), but `nuxt.config.ts` sets a nested `VDialog > VCard` default of `variant: 'flat'` so dialog cards get a solid background automatically. No manual override needed.
267
+
268
+ ```vue
269
+ <template>
270
+ <v-dialog v-model="open" max-width="500" persistent>
271
+ <v-card>
272
+ <v-card-title class="d-flex align-center">
273
+ <span>Confirm Action</span>
274
+ <v-spacer />
275
+ <v-btn icon variant="text" @click="open = false">
276
+ <v-icon>mdi-close</v-icon>
277
+ </v-btn>
278
+ </v-card-title>
279
+ <v-divider />
280
+ <v-card-text>
281
+ Are you sure you want to proceed? This action cannot be undone.
282
+ </v-card-text>
283
+ <v-divider />
284
+ <v-card-actions>
285
+ <v-spacer />
286
+ <v-btn variant="text" @click="open = false">Cancel</v-btn>
287
+ <v-btn color="primary" :loading="loading" @click="confirm">Confirm</v-btn>
288
+ </v-card-actions>
289
+ </v-card>
290
+ </v-dialog>
291
+ </template>
292
+
293
+ <script setup lang="ts">
294
+ const open = defineModel<boolean>({ default: false });
295
+ const emit = defineEmits<{ confirmed: [] }>();
296
+ const loading = ref(false);
297
+
298
+ async function confirm() {
299
+ loading.value = true;
300
+ try {
301
+ emit('confirmed');
302
+ open.value = false;
303
+ } finally {
304
+ loading.value = false;
305
+ }
306
+ }
307
+ </script>
308
+ ```
309
+
310
+ ## 6. Master-Detail View
311
+
312
+ Two-column layout with selectable list and detail panel.
313
+
314
+ ```vue
315
+ <template>
316
+ <div class="d-flex fill-height">
317
+ <!-- List panel -->
318
+ <v-card class="flex-shrink-0" style="width: 320px; overflow-y: auto" flat>
319
+ <v-list density="compact" nav>
320
+ <v-list-item
321
+ v-for="item in items"
322
+ :key="item.id"
323
+ :title="item.name"
324
+ :subtitle="item.type"
325
+ :active="selected?.id === item.id"
326
+ @click="selected = item"
327
+ />
328
+ </v-list>
329
+ <v-empty-state
330
+ v-if="!items.length"
331
+ headline="No items"
332
+ icon="mdi-playlist-remove"
333
+ density="compact"
334
+ />
335
+ </v-card>
336
+
337
+ <v-divider vertical />
338
+
339
+ <!-- Detail panel -->
340
+ <div class="flex-grow-1 overflow-y-auto pa-4">
341
+ <template v-if="selected">
342
+ <h2 class="text-h5 mb-2">{{ selected.name }}</h2>
343
+ <v-chip class="mb-4">{{ selected.type }}</v-chip>
344
+ <p>{{ selected.description }}</p>
345
+ </template>
346
+ <v-empty-state
347
+ v-else
348
+ headline="Select an item"
349
+ text="Choose from the list to see details"
350
+ icon="mdi-arrow-left"
351
+ />
352
+ </div>
353
+ </div>
354
+ </template>
355
+
356
+ <script setup lang="ts">
357
+ interface Item {
358
+ id: string;
359
+ name: string;
360
+ type: string;
361
+ description: string;
362
+ }
363
+
364
+ const items = ref<Item[]>([]);
365
+ const selected = ref<Item | null>(null);
366
+
367
+ onMounted(async () => {
368
+ // items.value = await fetchItems();
369
+ });
370
+ </script>
371
+ ```
372
+
373
+ ## 7. Get Filings for a Company
374
+
375
+ Fetch Edgar filings (or any relationship-linked documents) for an organization.
376
+
377
+ **Important:** `getLinkedEntities` only supports graph node types (person,
378
+ organization, location). Documents, filings, articles, and other types are
379
+ NOT supported — use `getPropertyValues` with the relationship PID instead.
380
+
381
+ ```vue
382
+ <template>
383
+ <div class="d-flex flex-column fill-height pa-4">
384
+ <h1 class="text-h5 mb-4">Company Filings</h1>
385
+ <v-text-field
386
+ v-model="query"
387
+ label="Company name"
388
+ prepend-inner-icon="mdi-magnify"
389
+ @keyup.enter="search"
390
+ :loading="loading"
391
+ />
392
+ <v-alert v-if="error" type="error" variant="tonal" class="mt-2" closable>
393
+ {{ error }}
394
+ </v-alert>
395
+ <v-data-table
396
+ v-if="filings.length"
397
+ :headers="headers"
398
+ :items="filings"
399
+ :loading="loading"
400
+ density="comfortable"
401
+ hover
402
+ class="mt-4"
403
+ />
404
+ <v-empty-state
405
+ v-else-if="searched && !loading"
406
+ headline="No filings found"
407
+ icon="mdi-file-document-off"
408
+ />
409
+ </div>
410
+ </template>
411
+
412
+ <script setup lang="ts">
413
+ import { useElementalClient } from '@yottagraph-app/elemental-api/client';
414
+
415
+ const client = useElementalClient();
416
+ const query = ref('');
417
+ const filings = ref<{ neid: string; name: string }[]>([]);
418
+ const loading = ref(false);
419
+ const error = ref<string | null>(null);
420
+ const searched = ref(false);
421
+
422
+ const headers = [
423
+ { title: 'NEID', key: 'neid', sortable: true },
424
+ { title: 'Name', key: 'name', sortable: true },
425
+ ];
426
+
427
+ async function getPropertyPidMap(client: ReturnType<typeof useElementalClient>) {
428
+ const schemaRes = await client.getSchema();
429
+ const properties = schemaRes.schema?.properties ?? (schemaRes as any).properties ?? [];
430
+ return new Map(properties.map((p: any) => [p.name, p.pid]));
431
+ }
432
+
433
+ async function search() {
434
+ if (!query.value.trim()) return;
435
+ loading.value = true;
436
+ error.value = null;
437
+ searched.value = true;
438
+ try {
439
+ const lookup = await client.getNEID({
440
+ entityName: query.value.trim(),
441
+ maxResults: 1,
442
+ includeNames: true,
443
+ });
444
+ if (!lookup.neids?.length) {
445
+ filings.value = [];
446
+ return;
447
+ }
448
+ const orgNeid = lookup.neids[0];
449
+
450
+ const pidMap = await getPropertyPidMap(client);
451
+ const filedPid = pidMap.get('filed');
452
+ if (!filedPid) {
453
+ error.value = '"filed" relationship not found in schema';
454
+ return;
455
+ }
456
+
457
+ const res = await client.getPropertyValues({
458
+ eids: JSON.stringify([orgNeid]),
459
+ pids: JSON.stringify([filedPid]),
460
+ });
461
+
462
+ const docNeids = res.values.map((v: any) =>
463
+ String(v.value).padStart(20, '0'),
464
+ );
465
+
466
+ const names = await Promise.all(
467
+ docNeids.map(async (neid: string) => {
468
+ try {
469
+ const r = await client.getNamedEntityReport(neid);
470
+ return r.name || neid;
471
+ } catch {
472
+ return neid;
473
+ }
474
+ }),
475
+ );
476
+
477
+ filings.value = docNeids.map((neid: string, i: number) => ({
478
+ neid,
479
+ name: names[i],
480
+ }));
481
+ } catch (e: any) {
482
+ error.value = e.message || 'Failed to load filings';
483
+ filings.value = [];
484
+ } finally {
485
+ loading.value = false;
486
+ }
487
+ }
488
+ </script>
489
+ ```
@@ -0,0 +1,48 @@
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
+ # Design
7
+
8
+ Read `DESIGN.md` before starting any work. It is the source of truth for project vision, architecture, and current implementation status.
9
+
10
+ Update `DESIGN.md` when you add, remove, or change the design or implementation status of a feature.
11
+
12
+ `DESIGN.md` serves as a record of the current state of the project. Do not include checklists, working plans, or documentation of changes or previous implementation details.
13
+
14
+ The sections of the design doc are flexible and you should add or remove sections to fit the needs of the project.
15
+
16
+ # Starter App is a Placeholder
17
+
18
+ The default UI that ships with this template (home page, chat page, entity
19
+ lookup) is **placeholder content** meant to demonstrate capabilities. When
20
+ building from a project brief or user request, feel free to:
21
+
22
+ - **Replace** `pages/index.vue` with the app's real home page
23
+ - **Remove** example pages (`entity-lookup.vue`, `chat.vue`, `mcp.vue`)
24
+ if the app doesn't need them
25
+ - **Restructure** the navigation, layout, and branding entirely
26
+ - **Keep** only the infrastructure: `composables/`, `server/api/kv/`,
27
+ `agents/`, and `pages/chat.vue` (if the app uses agent chat)
28
+
29
+ Do NOT treat the existing UI as something to preserve. Build what the
30
+ user described, using the template's infrastructure and patterns but not
31
+ its placeholder content. The only exception is if the user explicitly
32
+ asks to keep specific parts.
33
+
34
+ # Project Brief
35
+
36
+ If `DESIGN.md` has a `## Vision` section, it contains the project creator's original description of what they want to build — written during project setup in the Broadchurch Portal.
37
+
38
+ When a user opens the project for the first time and hasn't started building yet (the `## Status` section says "Run `/build-my-app`"), suggest running that command. If they ask "what should I build?" or similar, read the Vision section of DESIGN.md first.
39
+
40
+ # Feature docs
41
+
42
+ Feature docs are used as working documents to help a user and agent collaborate on feature implementation. A feature can be whatever size you need -- an entire page, a shared composable, or a single component.
43
+
44
+ When a user starts working on implementing a change, if they are using a feature doc, read the relevant feature doc before starting work. Then update the feature doc with every change you make. Be sure to create checklists as you plan work, and check off the checklists as the work is completed. Document design choices and open questions.
45
+
46
+ If the user is implementing a change that has no feature doc, encourage them to work with you to create one before starting work. A new feature doc should be created by copying `design/feature_template.md` to a new file in `design`, giving it an appropriate name, and editing it from there.
47
+
48
+ 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,48 @@
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
+ # Git Workflow
7
+
8
+ This rule applies both when an agent is committing its own work and when helping a user commit theirs.
9
+
10
+ ## Committing
11
+
12
+ Commit when you finish a meaningful unit of work — a feature, a fix, a refactor, or a coherent set of related changes. Do **not** commit after every individual edit. If you're making several related changes (e.g. updating references across multiple files for the same reason), bundle them into a single commit. If the user changes focus and starts working on something different, commit before starting the new work focus.
13
+
14
+ ### Steps
15
+
16
+ 1. **Run formatting first** — this is required or the commit will fail:
17
+ ```bash
18
+ npm run format
19
+ ```
20
+ 2. **Stage all files** — always use `git add -A`.
21
+ 3. **Commit** with the message format below.
22
+ 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.
23
+ 5. **Inform the user** that you made a commit.
24
+
25
+ ### Commit Message Format
26
+
27
+ ```
28
+ [Agent commit] {work summary}
29
+ ```
30
+
31
+ - The work summary should be 1–3 lines.
32
+ - Reuse checklist item language from the source doc when it fits; otherwise write a single concise description.
33
+
34
+ ### Pre-commit Failure Troubleshooting
35
+
36
+ If a commit fails with `✖ prettier --check [FAILED]` or `pre-commit script failed`, the fix is:
37
+
38
+ ```bash
39
+ npm run format
40
+ ```
41
+
42
+ Do **not** run Prettier directly — always use `npm run format`. After formatting, run `git add -A` again and retry the commit.
43
+
44
+ If a user asks about this error, explain the `npm run format` requirement.
45
+
46
+ ## Post-Feature: Encourage Push
47
+
48
+ After a feature is fully implemented and committed, use `AskQuestion` to encourage the user to push their branch.
@@ -0,0 +1,48 @@
1
+ ---
2
+ description: Warns users not to modify aether_ prefixed files in .cursor/
3
+ globs:
4
+ - .cursor/**/aether_*
5
+ alwaysApply: false
6
+ ---
7
+
8
+ # Aether Instructions Warning
9
+
10
+ **You are editing a file managed by the `@yottagraph-app/aether-instructions` package.**
11
+
12
+ Files prefixed with `aether_` are automatically installed and updated by the
13
+ Aether instructions system. They will be **overwritten** when you run
14
+ `/aether_update_instructions`.
15
+
16
+ ## Do Not
17
+
18
+ - Modify existing `aether_*` files directly (changes will be lost on update)
19
+ - Create new files with the `aether_` prefix (they will be deleted on update)
20
+
21
+ ## To Customize
22
+
23
+ If you need to modify the behavior of an `aether_*` rule or command:
24
+
25
+ 1. **Copy** the file to a new name **without** the `aether_` prefix
26
+ 2. Make your changes to the copy
27
+ 3. Your copy will not be affected by instruction updates
28
+
29
+ Example:
30
+
31
+ ```bash
32
+ # Copy the rule you want to customize
33
+ cp .cursor/rules/aether_api.mdc .cursor/rules/api_custom.mdc
34
+
35
+ # Edit your copy
36
+ # Your customizations in api_custom.mdc are preserved across updates
37
+ ```
38
+
39
+ ## Why This Matters
40
+
41
+ The `aether_` prefix creates a clear namespace:
42
+ - **`aether_*` files** = Package-managed, updated automatically
43
+ - **Other files** = Your custom rules/commands, never touched
44
+
45
+ This allows you to:
46
+ - Receive rule updates without losing your customizations
47
+ - Know at a glance which files are yours vs. package-provided
48
+ - Safely extend the system without conflicts