/archestra-dev-frontend
Use when modifying Archestra frontend Next.js/React code, UI components, forms, TanStack Query hooks, generated API client usage, frontend copy, or documentation links.
$ npx -y skills add archestra-ai/archestra --skill archestra-dev-frontend --agent claude-codeHow it fires
How this skill gets triggered: by you, by Claude, or both.
- Fires itselfAuto-invocation. Claude auto-loads it when your prompt matches the work.Auto-invocation is when the right skill fires by itself at the right moment, driven by a FLOW.md router and a hook, instead of you invoking it by name. It is the difference between a skill being installed and a skill actually getting used.Read the full definition →
- You can call itInvoke it directly when you want it.
- Slash command
/archestra-dev-frontend
Context preview
The summary Claude sees to decide when to auto-load this skill.
Use when modifying Archestra frontend Next.js/React code, UI components, forms, TanStack Query hooks, generated API client usage, frontend copy, or documentation links.
SKILL.md
archestra-dev-frontend.SKILL.mdname: archestra-dev-frontend
description: Use when modifying Archestra frontend Next.js/React code, UI components, forms, TanStack Query hooks, generated API client usage, frontend copy, or documentation links.
Archestra Frontend Development
Use this skill before changing files under `platform/frontend/` or frontend-facing shared code.
Commands
Run commands from `platform/` unless specifically instructed otherwise.
pnpm codegen # regenerates the OpenAPI spec and the API client
pnpm type-check
pnpm lint
pnpm test
pnpm knip # flags unused exports; part of frontend check:ci
Data fetching
- Use TanStack Query for data fetching.
- Prefer `useQuery` over `useSuspenseQuery` with explicit loading states.
- Prefer TanStack Query over prop drilling when a component can fetch data by identifier itself.
- Only pass minimal identifiers, such as `catalogId`, needed for child components to fetch or filter their own data.
- TanStack Query caching prevents duplicate requests when multiple components use the same query.
API clients
- Frontend `.query.ts` files should never call the Archestra backend with `fetch()` directly — use the generated SDK. Raw `fetch()` is only for third-party APIs the SDK does not cover (e.g. GitHub, see `lib/github/*.query.ts`).
- Run `pnpm codegen` first to ensure the generated SDK is up to date (`codegen:api-client` alone only exists inside `@archestra/shared` and needs the env var: `CODEGEN=true pnpm --filter @archestra/shared codegen:api-client` — without `CODEGEN=true` it reads a live `localhost:9000` instead of the committed spec).
- Use generated SDK methods instead of manual API calls for type safety and consistency.
- Reuse API types from `@archestra/shared`, especially `archestraApiTypes` types such as `archestraApiTypes.CreateXxxData["body"]` and `archestraApiTypes.GetXxxResponses["200"]`.
- Do not define duplicate frontend API types when generated/shared types already exist.
Query error handling
- Handle toasts in `.query.ts` files, not in components.
- Define mutation success/error toasts in `onSuccess` and `onError` callbacks.
- Queries must fail loud: call `throwOnApiError(error)` after the SDK call so the query enters its error state, then keep the existing success return (`return data ?? []`). Swallowing an error into a default makes an outage indistinguishable from a genuinely empty result, which is how an offline app showed "Add an LLM Provider Key".
- `throwOnApiError(error)` toasts via `handleApiError` by default. Screens that render their own error state (e.g. a `QueryLoadError` retry panel gated on `isLoadingError`) pass `{ toastOnError: false }` to avoid a redundant toast and a fresh toast on every retry. Detail endpoints where a 404 means "does not exist" rather than an outage pass `{ allowNotFound: true }` and keep returning their `null` default for that case.
- Mutations keep `handleApiError(error)` + `throw toApiError(error)` in the `mutationFn`.
- Components should not use `try`/`catch` for API calls; API error handling belongs in `.query.ts` files.
UI components
- Use shadcn/ui components only.
- Add shadcn/ui components with `npx shadcn@latest add <component>`.
- Prefer components from `frontend/src/components/ui` over plain HTML elements when a component exists.
- Use `Button` over raw `<button>`, `Input` over raw `<input>`, and the matching UI component for selects and other controls.
- Keep components small and focused, with extracted business logic where it improves clarity.
- Keep frontend files flat where practical and avoid barrel files.
- Only export what is needed externally.
Text nodes and machine translation
Chrome page-translate re-parents bare text nodes into `<font>` wrappers. React still holds the original nodes, so deleting one — or inserting an element before it — throws `NotFoundError` and crashes the page (facebook/react#11538, no upstream fix). Never let React add, remove, or replace a **bare** text node: wrap conditional text in an element so only elements move.
`biome-plugins/no-conditional-bare-jsx-text.grit` fails the build on the shapes below, and its diagnostics cannot be suppressed with `biome-ignore` — write them wrapped in the first place:
- `{cond ? <Icon /> : "More"}` → `{cond ? <Icon /> : <span>More</span>}`.
- `{cond ? (<><Loader2 />Loading…</>) : ("Load more")}` → wrap both branches; the fragment's own bare text is deleted when the branch flips, so `<span>Loading…</span>` inside it and `<span>Load more</span>` for the string.
- `{saved && "Saved!"}` → `{saved && <span>Saved!</span>}`.
- `{n > 0 ? " and more" : ""}` → `{n > 0 ? <span> and more</span> : null}` — return `null`, never `""`, and keep the padding spaces inside the span.
- `<Button>{pending ? <Loader2 /> : <Icon />} Save</Button>` → wrap the label: `<span>Save</span>`. Same for a label expression: `<span>{agent ? "Update" : "Create"}</span>`.
The rule cannot see these; apply the convention by hand:
- A `ReactNode` prop or variable rendered next to a conditional sibling (`{icon}{label}`) — wrap it: `{icon}<span>{label}</span>` (`app/messaging-channels/layout.tsx`).
- Loading/empty/data branches whose roots are the **same tag** — React reconciles the element and deletes the bare status text in place. Wrap each branch's text (`<span>Loading tools…</span>`) or give the branches distinct `key`s.
- A shared component rendering a `ReactNode` slot inside an element that persists across content changes — key the wrapper by the content, as `components/form-dialog.tsx` and `components/ui/searchable-select.tsx` do.
Safe, do not churn: text→text updates (`{saving ? "Saving…" : "Save"}`), whole-element unmounts, and strings in attributes.
Wrapping splits a string across sibling elements, so Testing Library's default `getByText` stops matching. Scope to a container with `toHaveTextContent`, or use a function matcher constrained by tag — do not unwrap the span to satisfy a test.
Forms
- Prefer `useForm
Read more
name: archestra-dev-frontend description: Use when modifying Archestra frontend Next.js/React code, UI components, forms, TanStack Query hooks, generated API client usage, frontend copy, or documentation links.
Archestra Frontend Development
Use this skill before changing files under `platform/frontend/` or frontend-facing shared code.
Commands
Run commands from `platform/` unless specifically instructed otherwise.
pnpm codegen # regenerates the OpenAPI spec and the API client pnpm type-check pnpm lint pnpm test pnpm knip # flags unused exports; part of frontend check:ci
Data fetching
- Use TanStack Query for data fetching.
- Prefer `useQuery` over `useSuspenseQuery` with explicit loading states.
- Prefer TanStack Query over prop drilling when a component can fetch data by identifier itself.
- Only pass minimal identifiers, such as `catalogId`, needed for child components to fetch or filter their own data.
- TanStack Query caching prevents duplicate requests when multiple components use the same query.
API clients
- Frontend `.query.ts` files should never call the Archestra backend with `fetch()` directly — use the generated SDK. Raw `fetch()` is only for third-party APIs the SDK does not cover (e.g. GitHub, see `lib/github/*.query.ts`).
- Run `pnpm codegen` first to ensure the generated SDK is up to date (`codegen:api-client` alone only exists inside `@archestra/shared` and needs the env var: `CODEGEN=true pnpm --filter @archestra/shared codegen:api-client` — without `CODEGEN=true` it reads a live `localhost:9000` instead of the committed spec).
- Use generated SDK methods instead of manual API calls for type safety and consistency.
- Reuse API types from `@archestra/shared`, especially `archestraApiTypes` types such as `archestraApiTypes.CreateXxxData["body"]` and `archestraApiTypes.GetXxxResponses["200"]`.
- Do not define duplicate frontend API types when generated/shared types already exist.
Query error handling
- Handle toasts in `.query.ts` files, not in components.
- Define mutation success/error toasts in `onSuccess` and `onError` callbacks.
- Queries must fail loud: call `throwOnApiError(error)` after the SDK call so the query enters its error state, then keep the existing success return (`return data ?? []`). Swallowing an error into a default makes an outage indistinguishable from a genuinely empty result, which is how an offline app showed "Add an LLM Provider Key".
- `throwOnApiError(error)` toasts via `handleApiError` by default. Screens that render their own error state (e.g. a `QueryLoadError` retry panel gated on `isLoadingError`) pass `{ toastOnError: false }` to avoid a redundant toast and a fresh toast on every retry. Detail endpoints where a 404 means "does not exist" rather than an outage pass `{ allowNotFound: true }` and keep returning their `null` default for that case.
- Mutations keep `handleApiError(error)` + `throw toApiError(error)` in the `mutationFn`.
- Components should not use `try`/`catch` for API calls; API error handling belongs in `.query.ts` files.
UI components
- Use shadcn/ui components only.
- Add shadcn/ui components with `npx shadcn@latest add <component>`.
- Prefer components from `frontend/src/components/ui` over plain HTML elements when a component exists.
- Use `Button` over raw `<button>`, `Input` over raw `<input>`, and the matching UI component for selects and other controls.
- Keep components small and focused, with extracted business logic where it improves clarity.
- Keep frontend files flat where practical and avoid barrel files.
- Only export what is needed externally.
Text nodes and machine translation
Chrome page-translate re-parents bare text nodes into `<font>` wrappers. React still holds the original nodes, so deleting one — or inserting an element before it — throws `NotFoundError` and crashes the page (facebook/react#11538, no upstream fix). Never let React add, remove, or replace a **bare** text node: wrap conditional text in an element so only elements move.
`biome-plugins/no-conditional-bare-jsx-text.grit` fails the build on the shapes below, and its diagnostics cannot be suppressed with `biome-ignore` — write them wrapped in the first place:
- `{cond ? <Icon /> : "More"}` → `{cond ? <Icon /> : <span>More</span>}`.
- `{cond ? (<><Loader2 />Loading…</>) : ("Load more")}` → wrap both branches; the fragment's own bare text is deleted when the branch flips, so `<span>Loading…</span>` inside it and `<span>Load more</span>` for the string.
- `{saved && "Saved!"}` → `{saved && <span>Saved!</span>}`.
- `{n > 0 ? " and more" : ""}` → `{n > 0 ? <span> and more</span> : null}` — return `null`, never `""`, and keep the padding spaces inside the span.
- `<Button>{pending ? <Loader2 /> : <Icon />} Save</Button>` → wrap the label: `<span>Save</span>`. Same for a label expression: `<span>{agent ? "Update" : "Create"}</span>`.
The rule cannot see these; apply the convention by hand:
- A `ReactNode` prop or variable rendered next to a conditional sibling (`{icon}{label}`) — wrap it: `{icon}<span>{label}</span>` (`app/messaging-channels/layout.tsx`).
- Loading/empty/data branches whose roots are the **same tag** — React reconciles the element and deletes the bare status text in place. Wrap each branch's text (`<span>Loading tools…</span>`) or give the branches distinct `key`s.
- A shared component rendering a `ReactNode` slot inside an element that persists across content changes — key the wrapper by the content, as `components/form-dialog.tsx` and `components/ui/searchable-select.tsx` do.
Safe, do not churn: text→text updates (`{saving ? "Saving…" : "Save"}`), whole-element unmounts, and strings in attributes.
Wrapping splits a string across sibling elements, so Testing Library's default `getByText` stops matching. Scope to a container with `toHaveTextContent`, or use a function matcher constrained by tag — do not unwrap the span to satisfy a test.
Forms
- Prefer `useForm
Enterprise AI Platform with guardrails, MCP registry, gateway & orchestrator
Repo: archestra-ai/archestra
Other skills on archestra.
- /archestra-dev-backend-tests
Use when writing or modifying Archestra backend unit tests (platform/backend/src/**/*.test.ts) — mocking modules, stubbing globals, database fixtures, vitest projects/isolation, or test performance.
Open skill - /archestra-dev-backend
Use when adding or changing Archestra backend routes, models, services, API request/response schemas, endpoint permissions, or OpenAPI/codegen for the generated API client.
Open skill - /archestra-dev-bench-analysis
Map-reduce a finished archestra-bench run into a Tier-1/Tier-2 improvement report using Claude subagents (same analysis as the Rust analyzer, no API key).
Open skill - /archestra-dev-e2e
Use when writing, debugging, or running Archestra Playwright e2e tests, API/UI fixtures, WireMock-backed tests, local/CI e2e setup, or test selectors.
Open skill - /archestra-dev-interactions-migrations
Use BEFORE writing or running any Drizzle migration that touches the `interactions` table (or any other very large, write-hot table). The interactions table is the platform's biggest, append-heavy table — every LLM proxy call writes a row — so a careless migration can take a
Open skill - /archestra-dev-investigate
Use when investigating Archestra bugs or incidents — staging issues, backend 50x errors, Drizzle failed queries, DB connection pressure, deploy regressions, or Kubernetes/runtime symptoms. Orientation only; defers the process to /investigate.
Open skill

