Skip to main content

prototype

Load for the /prototype flow: generate the prototype (prototype.json + prototype.tsx) of every web-application a finished design declares, as a clickable, checked app the reviewer can use before anything is built.

Informações da origem

Repositório
wso2/labs-agentic-engineer
Última atividade na origem
4 de outubro de 2026 às 16:49
Idioma detectado do SKILL.md
inglês
Estrelas
25
Forks
24

Opções de instalação

Por padrão, está selecionado o prompt que primeiro revisa a origem. Você pode mudar para um comando direto ou baixar uma cópia local.

Revise os arquivos de origem

Leia o SKILL.md e os arquivos complementares exibidos pelo SkillsMP antes de decidir se vai instalar.

Exibindo SKILL.md

SKILL.md
Instruções da origem · Visualização somente leitura
name
prototype
description
Load for the /prototype flow: generate the prototype (prototype.json + prototype.tsx) of every web-application a finished design declares, as a clickable, checked app the reviewer can use before anything is built.
metadata
{"aep":{"kind":"platform","audience":["design"]}}
# Prototype A prototype is the reviewable picture of a `web-application` before it is built: its screens, navigation, roles, flows and display states, filled with mock data, running in a sandbox inside the review page. It is **derived from the design**, so this flow runs after `/design` and asks nothing the design already answers. It writes two files per web-application and nothing else. The screens are a React module written against a fixed kit of components (`@wso2/prototype-kit`). You compose screens from the kit; you do not build components, style anything, or reach outside the frame. ## Inputs Read these before writing anything: - `specs/design/design.cell`: every `component … web-application` line is one prototype to write. No web-application means there is nothing to do: say so and stop. When the turn names components, write only those. - `specs/design/security.json`: the roles. When the file exists, each prototype role's `id` is a role's `name` from it, **copied verbatim** (same case, same spelling), and its `name` is a short human label for that role. Include exactly the roles that use this application. Without the file, take the roles from the PRD's actors and give them short kebab-case ids. - The API each web-application reads: the `openapi.yaml` of every component listed under `dependencies` (`kind: component`) in its `design.json`, and its own when it has one. Screens show what these operations return and collect what they accept. - `specs/requirements/prd.md`: the numbered **User Stories**, the coverage oracle. Every story gets at least one screen. Set a story aside only when the product gives it no view (sign-in through the platform's SSO, a backend job, a machine-facing endpoint) and name it in your closing. - `specs/design/flows/*.md`: the key flows. Every flow a web-application's users walk becomes a `flows` entry of its prototype, one role's walk through its screens in order. - `specs/design/components/<component>/wireframes.dsl`, when it exists: the earlier sketch of the screens. It is a hint for layout and screen names, not a constraint; the PRD, flows and API decide what the screens hold. - `specs/design/components/<component>/prototype.json` and `prototype.tsx` if they already exist: you are revising them, not starting over (see **Stable ids**). ## Output Two files per web-application, in `specs/design/components/<component>/`, where `<component>` is the web-application's name in the cell: 1. **`prototype.json`, the manifest**: the roles, display states, screens and flows the reviewer picks from. Write it **first**. 2. **`prototype.tsx`, the screens**: one React component per manifest screen, exported with `defineApp`. It is checked against the manifest already written. Write a new file with ONE `addFile` of the whole file. Revise an existing one with `editFile` edits: the file is already in front of you, and an edit costs what it changes. Never write a partial file to finish later. Change **no other file**: not the cell, not `security.json`, not any `design.json`, `openapi.yaml` or requirement. If the design is wrong for the prototype you would write, say what is wrong in your reply and leave the design alone. Every write is checked as it lands: the manifest's shape and references, the source's static rules, then a render of every screen for every role that reaches it in every display state. A refusal lists every finding with its code and where (a JSON path in the manifest, a line in the source, or the screen, role and state that failed to draw). Fix all of them and retry once. The codes are listed at the end of **The kit**. ## The manifest Plain JSON, schema version 3. Every object is closed: a key not shown here is refused. ```json { "schemaVersion": 3, "name": "Expense approvals", "entryScreen": "screen.queue", "roles": [{ "id": "Approver", "name": "Approver" }], "states": [ { "id": "state.default", "name": "Default" }, { "id": "state.empty", "name": "Nothing to show" }, { "id": "state.validation-error", "name": "Validation errors" }, { "id": "state.failed", "name": "Expense feed failed" } ], "screens": [ { "id": "screen.queue", "name": "Approval queue", "roleIds": ["Approver"] }, { "id": "screen.detail", "name": "Expense detail", "roleIds": ["Approver"] }, { "id": "screen.account", "name": "Account", "roleIds": ["Approver"] }, { "id": "screen.settings", "name": "Settings", "roleIds": ["Approver"] }, { "id": "screen.signed-out", "name": "Signed out", "roleIds": ["Approver"] } ], "flows": [ { "id": "flow.approve", "name": "Approve an expense", "roleId": "Approver", "screenIds": ["screen.queue", "screen.detail"] } ] } ``` - `roles`, `states` and `screens` have at least one entry; their ids and the flows' ids share one namespace and are unique together. `entryScreen` is the screen the prototype opens on. `name` is the application's name. - A screen's `roleIds` are the roles that reach it. A flow is one role's walk through screens that role reaches, in order, for one story or key flow. - The app shell's screens, an account screen, a settings screen and a signed-out screen, go to **every** role: every user has them. - `states` always starts with a default state (`state.default`); add one per presentation a reviewer must see: - `state.empty` wherever a list can be empty. - Wherever a screen's operations declare error responses in the API, the states those responses produce. A form whose operation answers `400` or `422` for invalid input shows its validation errors in `state.validation-error`: its Fields' `error` and a ValidationSummary. An operation that can fail (`5xx`, or a dependency it calls) gets `state.failed`: an error Alert saying what failed and what the user can do. A call that can be slow (an external dependency, a long-running operation) gets `state.delayed`: an info or warning Alert saying the result is on its way. A prototype with only default and empty states hides the presentations a reviewer most needs to judge. ## The screens ```tsx import { useState, type ReactNode } from "react"; import { Alert, AppShell, Button, Detail, Dialog, EmptyState, Field, Form, Heading, Screen, Section, Stat, StatGroup, Table, defineApp, useCollection, useDisplayState, useNav, useParams, } from "@wso2/prototype-kit"; interface Expense { id: string; employee: string; amount: string; status: string } const expenses: Expense[] = [ { id: "1042", employee: "Maya Fernando", amount: "$148.20", status: "Awaiting approval" }, { id: "1039", employee: "Ravi Perera", amount: "$62.00", status: "Awaiting approval" }, { id: "1031", employee: "Ana Silva", amount: "$310.00", status: "Approved" }, ]; const user = { name: "Ravi Perera", email: "ravi@acme.example" }; function Shell({ children }: { children: ReactNode }) { return ( <AppShell id="shell" user={user} nav={[{ id: "nav.queue", label: "Approval queue", to: "screen.queue" }]} account="screen.account" settings="screen.settings" signOut="screen.signed-out" > {children} </AppShell> ); } function Queue() { const state = useDisplayState(); const all = useCollection<Expense>("expenses"); const waiting = state === "state.empty" ? [] : all.items.filter((e) => e.status === "Awaiting approval"); const approved = all.items.filter((e) => e.status === "Approved").length; return ( <Shell> <Heading id="heading.queue" text="Approval queue" /> {state === "state.failed" && ( <Alert id="alert.feed-failed" tone="error" title="Expense feed unavailable" text="Amounts may be stale. Refresh in a few minutes." /> )} <StatGroup> <Stat id="stat.waiting" label="Awaiting you" value={String(waiting.length)} hint="claims to decide" icon="Inbox" tone="warning" /> <Stat id="stat.approved" label="Approved" value={String(approved)} hint="this month" icon="CircleCheck" tone="success" /> </StatGroup> <Section id="section.expenses" title="Expenses" count={waiting.length} subtitle="Oldest first. Open a claim to reject it with a reason."> <Table id="table.expenses" columns={["Employee", { label: "Amount", kind: "number" }, { label: "Status", kind: "status" }]} rows={waiting.map((e) => ({ id: `expense.${e.id}`, cells: [e.employee, e.amount], status: { text: e.status, tone: "warning" }, to: "screen.detail", params: { expense: e.id }, actions: [{ id: `expense.${e.id}.approve`, label: "Approve", onPress: () => all.update(e.id, { status: "Approved" }) }], }))} empty={<EmptyState id="empty.queue" title="Nothing to approve" text="New claims appear here." />} /> </Section> </Shell> ); } function ExpenseDetail() { const { expense: id } = useParams(); const state = useDisplayState(); const navigate = useNav(); const all = useCollection<Expense>("expenses"); const [rejecting, setRejecting] = useState(false); const expense = (id ? all.get(id) : undefined) ?? all.items[0]!; const decide = (status: string) => { all.update(expense.id, { status }); navigate.go("screen.queue"); }; return ( <Shell> <Heading id="heading.detail" text={`Expense ${expense.id}`} /> <Detail id="detail.expense" fields={[ { label: "Employee", value: expense.employee }, { label: "Amount", value: expense.amount }, ]} /> <Button id="btn.approve" label="Approve" emphasis="primary" onPress={() => decide("Approved")} /> <Button id="btn.reject" label="Reject" emphasis="danger" onPress={() => setRejecting(true)} /> <Dialog id="dialog.reject" title={`Reject expense ${expense.id}`} open={rejecting} onClose={() => setRejecting(false)}> <Form id="form.reject" onSubmit={() => decide("Rejected")} actions={<Button id="btn.reject.confirm" label="Reject" emphasis="danger" submit />} > <Field id="field.reason" label="Reason" type="textarea" required error={state === "state.validation-error" ? "Give the employee a reason" : undefined} /> </Form> </Dialog> </Shell> ); } function Account() { return ( <Shell> <Heading id="heading.account" text="Account" /> <Detail id="detail.account" fields={[{ label: "Name", value: user.name }, { label: "Email", value: user.email }]} /> </Shell> ); } function Settings() { const navigate = useNav(); return ( <Shell> <Heading id="heading.settings" text="Settings" /> <Form id="form.settings" onSubmit={() => navigate.go("screen.queue")} actions={<Button id="btn.save-settings" label="Save settings" emphasis="primary" submit />} > <Field id="field.digest" label="Email me a daily digest" type="switch" defaultValue="on" /> </Form> </Shell> ); } function SignedOut() { return ( <Screen> <Heading id="heading.signed-out" text="You are signed out" /> <Button id="btn.sign-in" label="Sign in" emphasis="primary" to="screen.queue" /> </Screen> ); } export default defineApp({ screens: { "screen.queue": Queue, "screen.detail": ExpenseDetail, "screen.account": Account, "screen.settings": Settings, "screen.signed-out": SignedOut, }, data: { expenses }, }); ``` - **One component per manifest screen**, keyed by the screen's id in `defineApp({ screens })`: exactly the manifest's screens, no more, no less. - **Every screen sits in the app shell.** Write one `<AppShell>` in a small local component (`Shell` above) and make it every screen's root: it draws the product's header, the signed-in user and the user menu (Account, Settings, Sign out) and the side navigation. Its `account`, `settings` and `signOut` are screens you write: an account screen with the user's details, a settings screen with the preferences the application offers, and a signed-out screen. Only that signed-out screen, outside the product, has a bare `<Screen>` as its root, with a way to sign back in. The shell's `menu` adds the application's own user-menu entries (a profile, billing) between Settings and Sign out. A reviewer points at an entry by
Ver no GitHub
Este SKILL.md e muito grande, entao o SkillsMP mostra aqui apenas a primeira secao. Ver no GitHub