diff --git a/AGENTS.md b/AGENTS.md index 3b16a4f..f0a4233 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -110,7 +110,8 @@ session with **no other memory of this project**. So: signature-verified (`verifyEvent`) before use. The first pass **streams** (`streamSasquatchAI`, OpenAI-style SSE) into a live bubble — raw JSON tool calls are suppressed so partial JSON never renders. Pollinations' `mistral` - slug is dead (404); only `openai` (gpt-oss-20b) is live. + slug is dead (404); only `openai` (gpt-oss-20b) is live. `findCoaches` + + `getCommunityStats` cover **all** Lab roles (kinds 35001–35014). - **ContextVM (CVM) server** (`cvm-server/`, Bun): standalone MCP-over-Nostr endpoint for *external* agents. Runs on this machine as a **system** service (`/etc/systemd/system/skatespots-cvm.service`, `sudo systemctl restart diff --git a/src/hooks/useCVMClient.ts b/src/hooks/useCVMClient.ts index d55c7b1..e582bde 100644 --- a/src/hooks/useCVMClient.ts +++ b/src/hooks/useCVMClient.ts @@ -97,7 +97,8 @@ export interface EventResult { */ export type LabRole = | 'coach' | 'filmer' | 'editor' | 'photographer' | 'builder' - | 'artist' | 'graffiti' | 'tattoo' | 'trainer' | 'musician' | 'judge'; + | 'artist' | 'graffiti' | 'tattoo' | 'trainer' | 'musician' | 'judge' + | 'organizer' | 'barber'; export interface CoachResult { role: LabRole; @@ -179,7 +180,9 @@ export interface CommunityStats { trainers: number; musicians: number; judges: number; - /** Total across every Lab craft (coaches..judges). */ + organizers: number; + barbers: number; + /** Total across every Lab craft (coaches..barbers). */ lab_total: number; shops: number; tours: number; @@ -552,11 +555,12 @@ export function useCVMClient() { location?: string; limit?: number; }): Promise<{ count: number; profiles: CoachResult[] }> => { - // Kind ↔ role for every Lab craft (35001..35012, minus 35006 = crews). + // Kind ↔ role for every Lab craft (35001..35014, minus 35006 = crews). const ROLE_KIND: Record = { coach: 35001, filmer: 35002, editor: 35003, photographer: 35004, builder: 35005, artist: 35007, graffiti: 35008, tattoo: 35009, - trainer: 35010, musician: 35011, judge: 35012, + trainer: 35010, musician: 35011, judge: 35012, organizer: 35013, + barber: 35014, }; const KIND_ROLE: Record = Object.fromEntries( Object.entries(ROLE_KIND).map(([role, kind]) => [kind, role as LabRole]) @@ -602,6 +606,7 @@ export function useCVMClient() { const SELF_TAGS = new Set([ 'skatecoach', 'skatefilmer', 'skateeditor', 'skatephotographer', 'skatebuilder', 'skateartist', 'skategraffiti', 'skatetattoo', 'skatetrainer', 'skatemusician', 'skatejudge', + 'skateorganizer', 'skatebarber', ]); const result: CoachResult[] = profiles.map((e) => { @@ -891,6 +896,7 @@ export function useCVMClient() { spots, events, gear, crews, shops, tours, coaches, filmers, editors, photographers, builders, artists, graffiti, tattoo, trainers, musicians, judges, + organizers, barbers, ] = await Promise.all([ relayGroup.query([{ kinds: [30818], '#t': ['skatespot'], limit: 2000 }], { signal }), relayGroup.query([{ kinds: [31923], limit: 500, since: now }], { signal }), @@ -909,6 +915,8 @@ export function useCVMClient() { relayGroup.query([{ kinds: [35010], limit: 500 }], { signal }), relayGroup.query([{ kinds: [35011], limit: 500 }], { signal }), relayGroup.query([{ kinds: [35012], limit: 500 }], { signal }), + relayGroup.query([{ kinds: [35013], limit: 500 }], { signal }), + relayGroup.query([{ kinds: [35014], limit: 500 }], { signal }), ]); const uniq = (rows: unknown) => @@ -925,6 +933,8 @@ export function useCVMClient() { const trainersN = uniq(trainers).length; const musiciansN = uniq(musicians).length; const judgesN = uniq(judges).length; + const organizersN = uniq(organizers).length; + const barbersN = uniq(barbers).length; return { spots: uniq(spots).filter( @@ -942,9 +952,12 @@ export function useCVMClient() { trainers: trainersN, musicians: musiciansN, judges: judgesN, + organizers: organizersN, + barbers: barbersN, lab_total: coachesN + filmersN + editorsN + photographersN + buildersN + - artistsN + graffitiN + tattooN + trainersN + musiciansN + judgesN, + artistsN + graffitiN + tattooN + trainersN + musiciansN + judgesN + + organizersN + barbersN, shops: uniq(shops).filter( (e) => e.tags.some((t) => t[0] === 't' && t[1] === 'skateshop') ).length,