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.

Informations de source

Dépôt
wso2/labs-agentic-engineer
Dernière activité de la source
4 octobre 2026 à 16:49
Langue détectée de SKILL.md
anglais
Étoiles
25
Forks
24

Options d'installation

Le prompt qui vérifie d'abord la source est sélectionné par défaut. Vous pouvez passer à une commande directe ou télécharger une copie locale.

Vérifiez les fichiers source

Lisez SKILL.md et les fichiers associés affichés par SkillsMP avant de décider de l'installer.

Affichage de SKILL.md

SKILL.md
Instructions source · Aperçu en lecture seule
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
Voir sur GitHub
Ce SKILL.md est tres volumineux, SkillsMP affiche donc ici seulement la premiere section. Voir sur GitHub