[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"skill:01M6QQBC7H3APRRCFZRHJ1RA98":3,"skill-same:01M6QQBC7H3APRRCFZRHJ1RA98":73},{"skillNo":4,"name":5,"nameZh":6,"description":7,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":16,"categories":17,"keywords":26,"publishedAt":47,"linkOnly":48,"markdownContent":49,"markdownBody":50,"frontMatter":51,"refName":52,"commitSha":53,"skillDirectory":54,"forksCount":55,"repositoryHtmlUrl":56,"repositoryHomepageUrl":6,"ownerHtmlUrl":57,"ownerBlogUrl":6,"ownerDescription":6,"updatedAt":58,"install":59,"resources":72},"01M6QQBC7H3APRRCFZRHJ1RA98","web-app",null,"Use when building or changing an App on the platform's web-app starter — the React, Vite, Tailwind and shadcn\u002Fui frontend with a NestJS and SQLite backend laid in `\u002Fworkspace\u002Fapp`. Covers the dev servers that are already running, adding packages, the build order, the project structure, the frontend-to-backend flow, the starter's rules, and how to verify the App before calling a feature done. Load it before touching the app.","SimaDevelopment\u002Fopsiforce","SimaDevelopment","https:\u002F\u002Favatars.githubusercontent.com\u002Fu\u002F153810655?v=4","01M6QQ054MKBDSS43AND3FNHDK","Opsima","01M6QQ058VFT6EY7HCBVN49G8Z",43,"AGPL-3.0","https:\u002F\u002Fgithub.com\u002FSimaDevelopment\u002Fopsiforce\u002Fblob\u002Fmain\u002Fagent-config\u002Fskills\u002Fweb-app\u002FSKILL.md",[18,23],{"categoryNo":19,"categoryName":20,"taxonomyType":21,"taxonomyTypeDesc":22},"01JBSEEDCAT000000000000009","前端开发","SCENARIO","使用场景",{"categoryNo":24,"categoryName":25,"taxonomyType":21,"taxonomyTypeDesc":22},"01JBSEEDCAT000000000000010","后端与接口",[27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46],"web","web app","app","how to","frontend","feature","platform","project","structure","done","vite","how","sqlite","Tailwind","flow","ui","shadcn","packages","project structure","react","2026-11-10 10:12:30",false,"---\nname: web-app\ndescription: Use when building or changing an App on the platform's web-app starter — the React, Vite, Tailwind and shadcn\u002Fui frontend with a NestJS and SQLite backend laid in `\u002Fworkspace\u002Fapp`. Covers the dev servers that are already running, adding packages, the build order, the project structure, the frontend-to-backend flow, the starter's rules, and how to verify the App before calling a feature done. Load it before touching the app.\n---\n\n# Building on the web-app starter\n\n`\u002Fworkspace\u002Fapp` is the platform's web-app starter: a Vite + React + Tailwind + shadcn\u002Fui frontend, a NestJS backend on SQLite, one `startup.sh` that satisfies the App contract, and a process supervisor. Everything below is true because of that starter. The `platform` skill owns what is true of every App — the contract, the durable workspace, the databases — and nothing here repeats it.\n\n## The starter is already there\n\n`app\u002F` holds the starter — `startup.sh`, `frontend\u002F` and `backend\u002F`. If it does not, stop and tell the user what you found instead of building.\n\n## What the starter fixes and what you change\n\nThe starter is a reference structure, not a finished design: it settles the decisions an App would otherwise make differently every time, and leaves the App itself to you.\n\n**Fixed — platform plumbing, never edited:** `startup.sh` stays the entry point, keeps its production and development branches, and runs every process under `guard` (add to it; follow the `platform` skill's rules for what you add). `frontend\u002Fhmr-toggle.ts` and its entry in `vite.config.ts`, `\u003CAppTitleSync \u002F>` in `main.tsx`, the `\u002Fapi` proxy, and the health and app-meta routes in `app.controller.ts`. The top-level shape — `frontend\u002F`, `backend\u002F`, `data\u002F`, `startup.sh` — stays.\n\n**Yours to extend — the normal work:** pages, components, backend modules, migrations, packages, and more supervised processes in `startup.sh` when the App needs them.\n\n**Replaceable, only on a decision:** a layer of the starter — the database, a backend library, the styling system — changes only when your own instructions or a skill you carry name the replacement, or the user asks for it outright. Fitting a feature into the starter comes before replacing part of it. A replacement is complete: the old layer's code and package go, and this skill's lines and the stack skill about that layer stop applying, so the App never runs two of anything.\n\nWhen instructions disagree, the order is: the `platform` skill, then your own instructions, then a skill you carry that names a starter decision it changes, then this skill.\n\n## The dev servers are already running\n\nThe container entrypoint runs them through `startup.sh` as soon as dependencies are installed (it waits for `.pnp.cjs`), so on a fresh Project they come up a few seconds after your first `yarn install`.\n\n- **Frontend** (Vite): `http:\u002F\u002Flocalhost:3000` — hot-reloads on file save\n- **Backend** (NestJS): `http:\u002F\u002Flocalhost:3100` — auto-restarts on file change\n- Vite proxies `\u002Fapi\u002F*` to the backend automatically\n- The supervisor restarts a crashed service on its own\n\n**Never start, stop or restart them yourself.** No `yarn dev`, no `node backend\u002Fsrc\u002Fmain.ts`, no command that starts a server. They pick up your changes as you save. Check the backend with `curl http:\u002F\u002Flocalhost:3100\u002Fapi\u002Fhealth` when you need to.\n\n## Adding a package\n\n`cd \u002Fworkspace\u002Fapp && yarn add \u003Cpkg>`, then **wait a few seconds**: the platform bounces Vite once so it sees the new dependency. Import it after the reload.\n\nResolve errors right after `yarn add` are transient — wait for the reload and re-check. Never fix them in `vite.config.ts`: `optimizeDeps.exclude`\u002F`include` forces extra restarts and makes things worse.\n\nIf an error **persists** — `X tried to access Y, but it isn't declared in its dependencies`, or `Could not resolve 'Y'` inside `.yarn\u002F__virtual__\u002F…` — it is a Yarn PnP gap: a library using an undeclared dependency. Declare it in `\u002Fworkspace\u002Fapp\u002F.yarnrc.yml`, then `yarn install`:\n\n```yaml\npackageExtensions:\n  \"\u003Cpackage>@*\":\n    dependencies:\n      \"\u003Cmissing-dep>\": \"*\"\n```\n\n## App configuration\n\nThe `platform` skill owns the `app\u002Fopsiforce.env.json` contract. In this starter, read it in the backend with a helper loaded once at boot:\n\n```ts\nimport fs from \"node:fs\"\nimport path from \"node:path\"\n\nconst file = path.resolve(process.cwd(), \"opsiforce.env.json\")\nconst values: Record\u003Cstring, string> = fs.existsSync(file)\n  ? JSON.parse(fs.readFileSync(file, \"utf8\"))\n  : {}\n\nexport const appConfig = (key: string): string | undefined => values[key]\n```\n\nThen `appConfig(\"STRIPE_API_KEY\")` — never `.env`, never `process.env` for the app's own config.\n\n## The build order\n\n1. **Before writing any code**, `cd \u002Fworkspace\u002Fapp && yarn install`. Not optional: nothing runs without installed dependencies.\n2. **MANDATORY: load the `frontend-design` skill before writing any UI code** — call `skill({ name: \"frontend-design\" })`. Do not skip this. It holds the design system, the palettes and the anti-patterns. Every App gets a brand colour and a theme of its own, never generic gray defaults; update the variables in `index.css`. If you do not carry `frontend-design`, say so — the author can add it.\n3. **Check for an existing App** — `cat \u002Fworkspace\u002Fapp\u002Fapp.meta.json 2>\u002Fdev\u002Fnull`. If it exists, modify what is there.\n4. **Build, in this order:**\n   - **`pages\u002Fhome.tsx` is the App.** Rewrite it with the real functionality. It is never a landing page that links to the real content.\n   - **Layout** in `App.tsx` — nav links, branding, colours. It is pre-configured with BrowserRouter, Layout and routes: build on it, do not rewrite it.\n   - **Database** — migration files in `backend\u002Fsrc\u002Fmigrations\u002F` (load `sqlite` when you carry it, unless the database was replaced — §What the starter fixes).\n   - **Backend API** — a NestJS module per feature (load `nestjs-api` when you carry it). **Register every module in `app.module.ts`** — the single most common mistake.\n   - **Frontend** — shadcn + Tailwind for the UI (load `ui` when you carry it), TanStack Query for every request (load `data-fetching` when you carry it).\n   - **More pages only when the App genuinely has several views** — a file in `pages\u002F`, a `\u003CRoute>` and a `\u003CNavLink>` in `App.tsx`. A simple App is one page.\n5. **The two cheap gates before go-live:** `cd \u002Fworkspace\u002Fapp && yarn check` (type errors do not crash the boot, so nothing else catches them), then confirm the dev servers booted (§Verifying the App runs).\n6. **Go live: write `app\u002Fapp.meta.json`** as soon as the first feature is built and step 5 passes, per the `platform` skill's go-live rules; feature testing in step 7 runs with the App already live.\n   - The tab title is read from that file at runtime — never hardcode the App's name in `index.html`.\n   - **Create the favicon at the same moment:** overwrite `frontend\u002Fpublic\u002Ffavicon.svg` with a flat SVG on the brand colour that shows what the App does — a few basic shapes, or the Lucide icon you chose for the UI. No clear glyph: the App name's initial as a letter mark. No image generation for a favicon unless asked.\n7. **Walk the real user flow in the browser before saying a feature is done** (the `platform` skill's browser walk). Go-live already happened, so a problem found here is fixed forward with the App up.\n\n## Frontend to backend\n\n**Every request goes through TanStack Query** — never raw `fetch()` or `axios` in a component. `data-fetching`, when you carry it, has `useQuery`\u002F`useMutation`, query keys, cache invalidation and optimistic updates.\n\n```\nUser clicks \"Save\" → React form (ui, rules\u002Fforms.md)\n  → useMutation calls fetch(\"\u002Fapi\u002F...\", { method: \"POST\", body: ... })\n    → NestJS Controller receives the request (nestjs-api)\n      → Service inserts into SQLite (sqlite)\n    → useMutation.onSuccess invalidates cache\n  → useQuery refetches → UI updates automatically\n```\n\nThe frontend talks to the backend at `\u002Fapi\u002F...`; Vite proxies it.\n\n## Project structure\n\n```\napp\u002F\n  app.meta.json              — YOU CREATE THIS after the gates pass (go-live; the tab title reads its name at runtime)\n  startup.sh                 — the App contract, already satisfied; add anything else the App runs here\n  frontend\u002Fpublic\u002F\n    favicon.svg               — neutral placeholder; overwrite with a brand-coloured SVG at go-live\n  frontend\u002Fsrc\u002F\n    App.tsx                   — ROOT COMPONENT (BrowserRouter + Layout + routes pre-configured)\n    main.tsx                  — entry point (QueryClientProvider + tab-title sync from app.meta.json — keep both intact)\n    index.css                 — Tailwind theme + CSS variables\n    pages\u002Fhome.tsx            — placeholder; becomes the App on the first request\n    pages\u002F                    — further page components\n    components\u002Fui\u002F            — shadcn components (~21 pre-installed: button, card, input, badge, dialog, alert-dialog, sheet, dropdown-menu, select, command, popover, tooltip, tabs, label, textarea, switch, checkbox, separator, skeleton, alert, avatar)\n    lib\u002Futils.ts              — cn() helper\n  backend\u002Fsrc\u002F\n    main.ts                   — NestJS bootstrap\n    app.module.ts             — root module — REGISTER ALL NEW MODULES HERE\n    app.controller.ts         — health + app-meta endpoints\n    database\u002F                 — DatabaseService (global, inject anywhere)\n    external-services\u002F        — catch-all doorbell stub (501); add a route per inbound service you implement\n    items\u002F                    — example CRUD module (replace with your own)\n    migrations\u002F               — SQL migration files (auto-run on startup)\n  data\u002F\n    app.db                    — the App's data (tables you create via migrations)\ndata\u002F\n  database.db                 — platform request log: every HTTP request to the App (read-only)\n  logs\u002F                       — platform process logs: output and lifecycle events, read with `process-logs`\n```\n\nNew pages, components, modules and migrations go inside these directories — that is what they are for. Do not add top-level directories or files beside them, and do not move what is there.\n\n## Skills for this stack\n\nEach of these is a separate skill. Load it when the App needs it **and you carry it**; when you do not carry one the App needs, say so — the author can add it.\n\n| Skill | When |\n|---|---|\n| `frontend-design` | **First**, before any UI — brand colours, typography, layout, polish |\n| `ui` | Any frontend work — shadcn\u002Fui, react-hook-form + zod, the app shell, routing and URL-synced lists, icons, theme tokens, dark mode, command palette, accessibility |\n| `data-fetching` | Every request — useQuery, useMutation, invalidation, optimistic updates |\n| `nestjs-api` | Endpoints, modules, services |\n| `sqlite` | Tables, migrations, queries, transactions, FTS, JSON |\n| `agent-browser` | Walking the App in a headless browser to verify a feature |\n| `react-table` | Sortable, filterable, paginated tables |\n| `charts` | Charts and dashboards (Recharts) |\n| `state-management` | Shared client state — Zustand |\n| `animations` | Transitions, gestures, reveals |\n| `dates` | Formatting, parsing, timezones, pickers |\n| `react-dropzone` | Drag-and-drop uploads |\n| `dnd-kit` | Drag and drop, kanban, reorderable lists |\n| `data-export` | CSV, JSON, print, clipboard |\n| `virtual-list` | Lists of 100+ items |\n| `websockets` | Live updates, chat, presence |\n| `document-generation` | Word, PowerPoint, Excel, PDF deliverables |\n| `llm-api` | AI features through the gateway — chat, structured output, transcription, speech, images |\n\n## The starter's rules\n\n1. **No landing pages.** The first page a user sees does the job; \"Welcome to X\" with a link is never it.\n2. **No placeholders.** Every file holds complete, working code. No `\u002F\u002F TODO`, no \"implement later\".\n3. **Data lives in the App's database through the backend API**, never in `localStorage`. Auth tokens are the one exception.\n4. **No monolithic files.** Split large components; each NestJS feature has its own module folder.\n5. **Mobile first.** Design at phone width and scale up with responsive Tailwind classes.\n6. **Complete files.** When you edit a file, write the whole updated file.\n\n## Verifying the App runs\n\nAfter any round of edits, and again before telling the user a feature is done: `cd \u002Fworkspace\u002Fapp && yarn check` first, then confirm the dev servers booted — no crash or error lines in the last couple of minutes, and `curl http:\u002F\u002Flocalhost:3100\u002Fapi\u002Fhealth` answers 200. The crash and error queries are in the `platform` skill (§Verify the App booted).\n\nRight after `yarn add`, **wait for the frontend to restart** before checking: `app-frontend` restarts once (one stop\u002F`started` pair and a fresh `VITE ... ready` line). That single restart is the dependency reload. Only a *repeating* restart loop (`restart_count` climbing) or an error that outlives the reload means something is wrong.\n\nCheck `app_requests` only when you changed frontend-to-backend communication (a new endpoint, a changed request or response shape).\n","# Building on the web-app starter\n\n`\u002Fworkspace\u002Fapp` is the platform's web-app starter: a Vite + React + Tailwind + shadcn\u002Fui frontend, a NestJS backend on SQLite, one `startup.sh` that satisfies the App contract, and a process supervisor. Everything below is true because of that starter. The `platform` skill owns what is true of every App — the contract, the durable workspace, the databases — and nothing here repeats it.\n\n## The starter is already there\n\n`app\u002F` holds the starter — `startup.sh`, `frontend\u002F` and `backend\u002F`. If it does not, stop and tell the user what you found instead of building.\n\n## What the starter fixes and what you change\n\nThe starter is a reference structure, not a finished design: it settles the decisions an App would otherwise make differently every time, and leaves the App itself to you.\n\n**Fixed — platform plumbing, never edited:** `startup.sh` stays the entry point, keeps its production and development branches, and runs every process under `guard` (add to it; follow the `platform` skill's rules for what you add). `frontend\u002Fhmr-toggle.ts` and its entry in `vite.config.ts`, `\u003CAppTitleSync \u002F>` in `main.tsx`, the `\u002Fapi` proxy, and the health and app-meta routes in `app.controller.ts`. The top-level shape — `frontend\u002F`, `backend\u002F`, `data\u002F`, `startup.sh` — stays.\n\n**Yours to extend — the normal work:** pages, components, backend modules, migrations, packages, and more supervised processes in `startup.sh` when the App needs them.\n\n**Replaceable, only on a decision:** a layer of the starter — the database, a backend library, the styling system — changes only when your own instructions or a skill you carry name the replacement, or the user asks for it outright. Fitting a feature into the starter comes before replacing part of it. A replacement is complete: the old layer's code and package go, and this skill's lines and the stack skill about that layer stop applying, so the App never runs two of anything.\n\nWhen instructions disagree, the order is: the `platform` skill, then your own instructions, then a skill you carry that names a starter decision it changes, then this skill.\n\n## The dev servers are already running\n\nThe container entrypoint runs them through `startup.sh` as soon as dependencies are installed (it waits for `.pnp.cjs`), so on a fresh Project they come up a few seconds after your first `yarn install`.\n\n- **Frontend** (Vite): `http:\u002F\u002Flocalhost:3000` — hot-reloads on file save\n- **Backend** (NestJS): `http:\u002F\u002Flocalhost:3100` — auto-restarts on file change\n- Vite proxies `\u002Fapi\u002F*` to the backend automatically\n- The supervisor restarts a crashed service on its own\n\n**Never start, stop or restart them yourself.** No `yarn dev`, no `node backend\u002Fsrc\u002Fmain.ts`, no command that starts a server. They pick up your changes as you save. Check the backend with `curl http:\u002F\u002Flocalhost:3100\u002Fapi\u002Fhealth` when you need to.\n\n## Adding a package\n\n`cd \u002Fworkspace\u002Fapp && yarn add \u003Cpkg>`, then **wait a few seconds**: the platform bounces Vite once so it sees the new dependency. Import it after the reload.\n\nResolve errors right after `yarn add` are transient — wait for the reload and re-check. Never fix them in `vite.config.ts`: `optimizeDeps.exclude`\u002F`include` forces extra restarts and makes things worse.\n\nIf an error **persists** — `X tried to access Y, but it isn't declared in its dependencies`, or `Could not resolve 'Y'` inside `.yarn\u002F__virtual__\u002F…` — it is a Yarn PnP gap: a library using an undeclared dependency. Declare it in `\u002Fworkspace\u002Fapp\u002F.yarnrc.yml`, then `yarn install`:\n\n```yaml\npackageExtensions:\n  \"\u003Cpackage>@*\":\n    dependencies:\n      \"\u003Cmissing-dep>\": \"*\"\n```\n\n## App configuration\n\nThe `platform` skill owns the `app\u002Fopsiforce.env.json` contract. In this starter, read it in the backend with a helper loaded once at boot:\n\n```ts\nimport fs from \"node:fs\"\nimport path from \"node:path\"\n\nconst file = path.resolve(process.cwd(), \"opsiforce.env.json\")\nconst values: Record\u003Cstring, string> = fs.existsSync(file)\n  ? JSON.parse(fs.readFileSync(file, \"utf8\"))\n  : {}\n\nexport const appConfig = (key: string): string | undefined => values[key]\n```\n\nThen `appConfig(\"STRIPE_API_KEY\")` — never `.env`, never `process.env` for the app's own config.\n\n## The build order\n\n1. **Before writing any code**, `cd \u002Fworkspace\u002Fapp && yarn install`. Not optional: nothing runs without installed dependencies.\n2. **MANDATORY: load the `frontend-design` skill before writing any UI code** — call `skill({ name: \"frontend-design\" })`. Do not skip this. It holds the design system, the palettes and the anti-patterns. Every App gets a brand colour and a theme of its own, never generic gray defaults; update the variables in `index.css`. If you do not carry `frontend-design`, say so — the author can add it.\n3. **Check for an existing App** — `cat \u002Fworkspace\u002Fapp\u002Fapp.meta.json 2>\u002Fdev\u002Fnull`. If it exists, modify what is there.\n4. **Build, in this order:**\n   - **`pages\u002Fhome.tsx` is the App.** Rewrite it with the real functionality. It is never a landing page that links to the real content.\n   - **Layout** in `App.tsx` — nav links, branding, colours. It is pre-configured with BrowserRouter, Layout and routes: build on it, do not rewrite it.\n   - **Database** — migration files in `backend\u002Fsrc\u002Fmigrations\u002F` (load `sqlite` when you carry it, unless the database was replaced — §What the starter fixes).\n   - **Backend API** — a NestJS module per feature (load `nestjs-api` when you carry it). **Register every module in `app.module.ts`** — the single most common mistake.\n   - **Frontend** — shadcn + Tailwind for the UI (load `ui` when you carry it), TanStack Query for every request (load `data-fetching` when you carry it).\n   - **More pages only when the App genuinely has several views** — a file in `pages\u002F`, a `\u003CRoute>` and a `\u003CNavLink>` in `App.tsx`. A simple App is one page.\n5. **The two cheap gates before go-live:** `cd \u002Fworkspace\u002Fapp && yarn check` (type errors do not crash the boot, so nothing else catches them), then confirm the dev servers booted (§Verifying the App runs).\n6. **Go live: write `app\u002Fapp.meta.json`** as soon as the first feature is built and step 5 passes, per the `platform` skill's go-live rules; feature testing in step 7 runs with the App already live.\n   - The tab title is read from that file at runtime — never hardcode the App's name in `index.html`.\n   - **Create the favicon at the same moment:** overwrite `frontend\u002Fpublic\u002Ffavicon.svg` with a flat SVG on the brand colour that shows what the App does — a few basic shapes, or the Lucide icon you chose for the UI. No clear glyph: the App name's initial as a letter mark. No image generation for a favicon unless asked.\n7. **Walk the real user flow in the browser before saying a feature is done** (the `platform` skill's browser walk). Go-live already happened, so a problem found here is fixed forward with the App up.\n\n## Frontend to backend\n\n**Every request goes through TanStack Query** — never raw `fetch()` or `axios` in a component. `data-fetching`, when you carry it, has `useQuery`\u002F`useMutation`, query keys, cache invalidation and optimistic updates.\n\n```\nUser clicks \"Save\" → React form (ui, rules\u002Fforms.md)\n  → useMutation calls fetch(\"\u002Fapi\u002F...\", { method: \"POST\", body: ... })\n    → NestJS Controller receives the request (nestjs-api)\n      → Service inserts into SQLite (sqlite)\n    → useMutation.onSuccess invalidates cache\n  → useQuery refetches → UI updates automatically\n```\n\nThe frontend talks to the backend at `\u002Fapi\u002F...`; Vite proxies it.\n\n## Project structure\n\n```\napp\u002F\n  app.meta.json              — YOU CREATE THIS after the gates pass (go-live; the tab title reads its name at runtime)\n  startup.sh                 — the App contract, already satisfied; add anything else the App runs here\n  frontend\u002Fpublic\u002F\n    favicon.svg               — neutral placeholder; overwrite with a brand-coloured SVG at go-live\n  frontend\u002Fsrc\u002F\n    App.tsx                   — ROOT COMPONENT (BrowserRouter + Layout + routes pre-configured)\n    main.tsx                  — entry point (QueryClientProvider + tab-title sync from app.meta.json — keep both intact)\n    index.css                 — Tailwind theme + CSS variables\n    pages\u002Fhome.tsx            — placeholder; becomes the App on the first request\n    pages\u002F                    — further page components\n    components\u002Fui\u002F            — shadcn components (~21 pre-installed: button, card, input, badge, dialog, alert-dialog, sheet, dropdown-menu, select, command, popover, tooltip, tabs, label, textarea, switch, checkbox, separator, skeleton, alert, avatar)\n    lib\u002Futils.ts              — cn() helper\n  backend\u002Fsrc\u002F\n    main.ts                   — NestJS bootstrap\n    app.module.ts             — root module — REGISTER ALL NEW MODULES HERE\n    app.controller.ts         — health + app-meta endpoints\n    database\u002F                 — DatabaseService (global, inject anywhere)\n    external-services\u002F        — catch-all doorbell stub (501); add a route per inbound service you implement\n    items\u002F                    — example CRUD module (replace with your own)\n    migrations\u002F               — SQL migration files (auto-run on startup)\n  data\u002F\n    app.db                    — the App's data (tables you create via migrations)\ndata\u002F\n  database.db                 — platform request log: every HTTP request to the App (read-only)\n  logs\u002F                       — platform process logs: output and lifecycle events, read with `process-logs`\n```\n\nNew pages, components, modules and migrations go inside these directories — that is what they are for. Do not add top-level directories or files beside them, and do not move what is there.\n\n## Skills for this stack\n\nEach of these is a separate skill. Load it when the App needs it **and you carry it**; when you do not carry one the App needs, say so — the author can add it.\n\n| Skill | When |\n|---|---|\n| `frontend-design` | **First**, before any UI — brand colours, typography, layout, polish |\n| `ui` | Any frontend work — shadcn\u002Fui, react-hook-form + zod, the app shell, routing and URL-synced lists, icons, theme tokens, dark mode, command palette, accessibility |\n| `data-fetching` | Every request — useQuery, useMutation, invalidation, optimistic updates |\n| `nestjs-api` | Endpoints, modules, services |\n| `sqlite` | Tables, migrations, queries, transactions, FTS, JSON |\n| `agent-browser` | Walking the App in a headless browser to verify a feature |\n| `react-table` | Sortable, filterable, paginated tables |\n| `charts` | Charts and dashboards (Recharts) |\n| `state-management` | Shared client state — Zustand |\n| `animations` | Transitions, gestures, reveals |\n| `dates` | Formatting, parsing, timezones, pickers |\n| `react-dropzone` | Drag-and-drop uploads |\n| `dnd-kit` | Drag and drop, kanban, reorderable lists |\n| `data-export` | CSV, JSON, print, clipboard |\n| `virtual-list` | Lists of 100+ items |\n| `websockets` | Live updates, chat, presence |\n| `document-generation` | Word, PowerPoint, Excel, PDF deliverables |\n| `llm-api` | AI features through the gateway — chat, structured output, transcription, speech, images |\n\n## The starter's rules\n\n1. **No landing pages.** The first page a user sees does the job; \"Welcome to X\" with a link is never it.\n2. **No placeholders.** Every file holds complete, working code. No `\u002F\u002F TODO`, no \"implement later\".\n3. **Data lives in the App's database through the backend API**, never in `localStorage`. Auth tokens are the one exception.\n4. **No monolithic files.** Split large components; each NestJS feature has its own module folder.\n5. **Mobile first.** Design at phone width and scale up with responsive Tailwind classes.\n6. **Complete files.** When you edit a file, write the whole updated file.\n\n## Verifying the App runs\n\nAfter any round of edits, and again before telling the user a feature is done: `cd \u002Fworkspace\u002Fapp && yarn check` first, then confirm the dev servers booted — no crash or error lines in the last couple of minutes, and `curl http:\u002F\u002Flocalhost:3100\u002Fapi\u002Fhealth` answers 200. The crash and error queries are in the `platform` skill (§Verify the App booted).\n\nRight after `yarn add`, **wait for the frontend to restart** before checking: `app-frontend` restarts once (one stop\u002F`started` pair and a fresh `VITE ... ready` line). That single restart is the dependency reload. Only a *repeating* restart loop (`restart_count` climbing) or an error that outlives the reload means something is wrong.\n\nCheck `app_requests` only when you changed frontend-to-backend communication (a new endpoint, a changed request or response shape).\n",{"name":5,"description":7},"main","c54d8e35f50ac35b3b2c7ba8d4fca2205a740cb9","agent-config\u002Fskills\u002Fweb-app",9,"https:\u002F\u002Fgithub.com\u002FSimaDevelopment\u002Fopsiforce","https:\u002F\u002Fgithub.com\u002FSimaDevelopment","2026-11-05 06:13:10",{"source":8,"skillName":5,"commands":60,"prompt":70,"zipAvailable":71},[61,64,67],{"tool":62,"command":63},"npx","npx skills add SimaDevelopment\u002Fopsiforce --skill web-app",{"tool":65,"command":66},"bunx","bunx skills add SimaDevelopment\u002Fopsiforce --skill web-app",{"tool":68,"command":69},"pnpm","pnpm dlx skills add SimaDevelopment\u002Fopsiforce --skill web-app","安装技能，技能地址：https:\u002F\u002Fgithub.com\u002FSimaDevelopment\u002Fopsiforce\u002Fblob\u002Fmain\u002Fagent-config\u002Fskills\u002Fweb-app\u002FSKILL.md",true,[],[74,111,131,152,162,188],{"skillNo":75,"name":76,"nameZh":6,"description":77,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":78,"categories":79,"keywords":91,"publishedAt":47},"01M6QQKHZFGFB6A111RW2A9WC0","figma-use","**MANDATORY prerequisite** — you MUST invoke this skill BEFORE every `use_figma` tool call. NEVER call `use_figma` directly without loading this skill first. Skipping it causes common, hard-to-debug failures. Trigger whenever the user wants to perform a write action or a unique read action that requires JavaScript execution in the Figma file context — e.g. create\u002Fedit\u002Fdelete nodes, set up variables or tokens, build components and variants, modify auto-layout or fills, bind variables to properties, or inspect file structure programmatically.","https:\u002F\u002Fgithub.com\u002FSimaDevelopment\u002Fopsiforce\u002Fblob\u002Fmain\u002Ffrontend\u002Fopencode\u002Fpackages\u002Fplugins\u002Ffigma\u002Fskills\u002Ffigma-use\u002FSKILL.md",[80,83,88],{"categoryNo":81,"categoryName":82,"taxonomyType":21,"taxonomyTypeDesc":22},"01JBSEEDCAT000000000000023","Skill与智能体开发",{"categoryNo":84,"categoryName":85,"taxonomyType":86,"taxonomyTypeDesc":87},"01JBSEEDCAT000000000000030","开发工程师","PROFESSION","适用职业",{"categoryNo":89,"categoryName":90,"taxonomyType":86,"taxonomyTypeDesc":87},"01JBSEEDCAT000000000000032","设计师",[92,93,94,95,96,97,98,99,100,101,102,103,35,104,105,106,107,108,109,110],"figma use","figma","use","components","create","delete","tool","read","write","edit","context","auto","tokens","bind","Variables","you","inspect","variants","invoke",{"skillNo":112,"name":113,"nameZh":6,"description":114,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":115,"categories":116,"keywords":122,"publishedAt":47},"01M6QQK1AMA90K6DFAVANYQ9X3","figma-use-slides","This skill helps agents use Figma's use_figma MCP tool in the Slides context. Can be used alongside figma-use which has foundational context for using the use_figma tool.","https:\u002F\u002Fgithub.com\u002FSimaDevelopment\u002Fopsiforce\u002Fblob\u002Fmain\u002Ffrontend\u002Fopencode\u002Fpackages\u002Fplugins\u002Ffigma\u002Fskills\u002Ffigma-use-slides\u002FSKILL.md",[117,120,121],{"categoryNo":118,"categoryName":119,"taxonomyType":21,"taxonomyTypeDesc":22},"01JBSEEDCAT000000000000003","演示文稿",{"categoryNo":81,"categoryName":82,"taxonomyType":21,"taxonomyTypeDesc":22},{"categoryNo":89,"categoryName":90,"taxonomyType":86,"taxonomyTypeDesc":87},[92,93,123,124,94,125,98,102,126,127,128,129,130],"figma use slides","slides","MCP","false","which","can","for","figma mcp",{"skillNo":132,"name":133,"nameZh":6,"description":134,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":135,"categories":136,"keywords":139,"publishedAt":47},"01M6QQJTR9QYK297WQ1X792D63","figma-use-motion","Motion \u002F animation context for the `use_figma` MCP tool — animating Figma nodes via manual keyframes, animation styles, easing, and timeline duration. Load alongside figma-use whenever a task involves adding, editing, or inspecting animation on a node.","https:\u002F\u002Fgithub.com\u002FSimaDevelopment\u002Fopsiforce\u002Fblob\u002Fmain\u002Ffrontend\u002Fopencode\u002Fpackages\u002Fplugins\u002Ffigma\u002Fskills\u002Ffigma-use-motion\u002FSKILL.md",[137,138],{"categoryNo":81,"categoryName":82,"taxonomyType":21,"taxonomyTypeDesc":22},{"categoryNo":89,"categoryName":90,"taxonomyType":86,"taxonomyTypeDesc":87},[92,93,140,94,141,125,142,98,143,144,102,145,146,147,126,148,149,150,151,129],"figma use motion","motion","Animation","node","editing","Nodes","task","keyframes","via","styles","manual","load",{"skillNo":153,"name":154,"nameZh":6,"description":155,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":156,"categories":157,"keywords":160,"publishedAt":47},"01M6QQJNZ067ZRXZ6A0QB9M6SB","figma-use-figjam","This skill helps agents use Figma's use_figma MCP tool in the FigJam context. Can be used alongside figma-use which has foundational context for using the use_figma tool.","https:\u002F\u002Fgithub.com\u002FSimaDevelopment\u002Fopsiforce\u002Fblob\u002Fmain\u002Ffrontend\u002Fopencode\u002Fpackages\u002Fplugins\u002Ffigma\u002Fskills\u002Ffigma-use-figjam\u002FSKILL.md",[158,159],{"categoryNo":81,"categoryName":82,"taxonomyType":21,"taxonomyTypeDesc":22},{"categoryNo":89,"categoryName":90,"taxonomyType":86,"taxonomyTypeDesc":87},[92,93,161,94,125,98,102,126,127,128,129,130],"figma use figjam",{"skillNo":163,"name":164,"nameZh":6,"description":165,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":166,"categories":167,"keywords":169,"publishedAt":47},"01M6QQJ7NEHXPE33VRVHYYM7YE","figma-swiftui","SwiftUI ↔ Figma translation. Use whenever the user mentions Swift, SwiftUI, iOS, iPhone, or iPad — in EITHER direction — translating a Figma design into SwiftUI (design → code), or pushing SwiftUI views \u002F screens \u002F tokens back into a Figma file (code → design). Triggers on phrases like 'implement this Figma design in SwiftUI', 'build this screen in Swift', 'push this SwiftUI view to Figma', 'mirror my Swift code in a Figma file', or whenever a Figma URL appears alongside `.swift` files \u002F an `.xcodeproj`. Routes to a direction-specific reference doc; loads alongside `figma-use` for the code → design path.","https:\u002F\u002Fgithub.com\u002FSimaDevelopment\u002Fopsiforce\u002Fblob\u002Fmain\u002Ffrontend\u002Fopencode\u002Fpackages\u002Fplugins\u002Ffigma\u002Fskills\u002Ffigma-swiftui\u002FSKILL.md",[168],{"categoryNo":89,"categoryName":90,"taxonomyType":86,"taxonomyTypeDesc":87},[93,170,171,172,173,174,175,176,177,178,179,180,181,182,126,183,184,185,186,187],"figma swiftui","swiftui","design","swift","code","push","doc","files","translation","back","either","iphone","ipad","screen","direction","URL","my","path",{"skillNo":189,"name":190,"nameZh":6,"description":191,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":192,"categories":193,"keywords":195,"publishedAt":47},"01M6QQJ3WZ3M90BZQ5TD305PKN","figma-shaders","**MANDATORY prerequisite** — load this skill before calling `create_shader` or `update_shader`. Use when the user asks to create, author, change, fix, or iterate on a shader effect, shader fill, custom effect, custom fill, or procedural shader in Figma.","https:\u002F\u002Fgithub.com\u002FSimaDevelopment\u002Fopsiforce\u002Fblob\u002Fmain\u002Ffrontend\u002Fopencode\u002Fpackages\u002Fplugins\u002Ffigma\u002Fskills\u002Ffigma-shaders\u002FSKILL.md",[194],{"categoryNo":89,"categoryName":90,"taxonomyType":86,"taxonomyTypeDesc":87},[196,93,197,198,96,199,200,201,202,94,203,204,126,205,151,206,207,208,209],"shaders","figma shaders","change","update","effect","user","fix","shader","calling","procedural","fill","iterate","custom","author"]