Skip to content
Development
Skill

/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.

From plugin
archestra
4.1k17 skills2 commands
Install
$ npx -y skills add archestra-ai/archestra --skill archestra-dev-frontend --agent claude-code

How 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.md
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
Read more
Ships witharchestra

Enterprise AI Platform with guardrails, MCP registry, gateway & orchestrator

Get the whole plugin