frontend-implementation
npx machina-cli add skill itssungho17/ssdam/frontend-implementation --openclawFrontend-Implementation AgentSkill
Overview
frontend-implementation is the seventh and final step in the SSDAM execution pipeline. It reads the frontend-design.TSK-NNN.md specification and directly implements all frontend code into the project root (Svelte components, stores, API client, routes, tests).
This skill is designed for autonomous execution by Cursor AI agents — it requires no human intervention and produces working, tested code.
Skill Chain Context
frontend-design.TSK-NNN.md (required)
+ backend-design.TSK-NNN.md (optional)
↓
[frontend-implementation] ← YOU ARE HERE
↓
Code in project_root/:
src/routes/ ← SvelteKit pages
src/lib/
components/ ← Svelte components
stores/ ← Svelte stores
api/ ← API client functions
types/ ← TypeScript interfaces
tests/ ← Component and store tests
Trigger and I/O
| Aspect | Details |
|---|---|
| Trigger | /frontend-implementation <task-spec-path> |
| Input | task-spec.TSK-NNN.yaml + frontend-design.TSK-NNN.md (required) + backend-design.TSK-NNN.md (optional) |
| Output | Code written directly to project_root/ (no design document) |
| Framework | Svelte 5 (or from task-spec.tech_stack.frontend) |
Execution Procedure
Step 1: Load Inputs and Create Implementation Plan
-
Parse task-spec.TSK-NNN.yaml:
- Extract
execution_plan.tech_stack.frontend(framework: Svelte 5 by default) - Extract
execution_plan.tech_stack.project_root(where to write code) - Extract
acceptance_criteria(requirements for the implementation) - Extract
requirement_ids(for traceability)
- Extract
-
Read frontend-design.TSK-NNN.md fully:
- Extract
pageslist (routes and page components) - Extract
componentslist (all components to implement) - Extract
storeslist (Svelte stores to create) - Extract
api_integration(API client functions) - Extract
file_structure(directory/file plan) - Extract
test_strategy(what to test)
- Extract
-
If backend-design.TSK-NNN.md exists:
- Read
api_endpointsfor precise URL, method, schema details - Read
schemasfor exact TypeScript interface definitions - Use these for API client implementation (override frontend-design if more precise)
- Read
-
Create ordered implementation plan:
Phase 1: TypeScript types └─ Create src/lib/types/{domain}.ts files with all interfaces Phase 2: Svelte stores └─ Create src/lib/stores/{storeName}.ts files Phase 3: API client └─ Create src/lib/api/{domain}.ts files with fetch functions Phase 4: Atomic components └─ Create src/lib/components/atomic/{Component}.svelte (bottom-up) Phase 5: Feature components └─ Create src/lib/components/features/{Feature}/{Component}.svelte Phase 6: Layout components └─ Create src/lib/components/layout/{Layout}.svelte Phase 7: Page components └─ Create src/routes/{route}/+page.svelte Phase 8: Tests └─ Create tests/ directory structure and test files Phase 9: Verification └─ Run npm run test, verify all tests pass └─ Verify acceptance_criteria are met
Rationale for bottom-up order: Atomic components don't depend on others; feature components depend on atomic; page components depend on features. This ensures dependencies are met as we build.
Step 2: Implement TypeScript Interfaces
For each type mentioned in frontend-design.api_integration and frontend-design.stores:
-
Identify all types needed:
- Response types (from API endpoints)
- Request types (for POST/PUT bodies)
- Store state interfaces
- Component-specific interfaces (if needed)
-
Create type files in
src/lib/types/:- Group related types by domain (e.g.,
src/lib/types/media.ts,src/lib/types/auth.ts) - If backend-design exists, copy exact field names and types from its schemas
- Group related types by domain (e.g.,
-
Example implementation:
// src/lib/types/media.ts export interface MediaFile { id: string; filename: string; size: number; mimetype: string; uploadedAt: Date; ownerId: string; } export interface UploadFileRequest { filename: string; mimetype: string; size: number; } export interface MediaFilesState { items: MediaFile[]; loading: boolean; error: string | null; selectedIds: string[]; } -
Ensure all types are TypeScript interfaces, not
any. -
Export all types from a barrel file (optional but recommended):
// src/lib/types/index.ts export * from './media'; export * from './auth';
Step 3: Implement Svelte Stores
For each store in frontend-design.stores:
-
Create store file at
src/lib/stores/{storeName}.ts: -
Implement writable store with initial state:
import { writable, derived } from 'svelte/store'; import type { MediaFilesState, MediaFile } from '../types'; const initialState: MediaFilesState = { items: [], loading: false, error: null, selectedIds: [] }; export const mediaFilesStore = writable<MediaFilesState>(initialState); -
Implement all actions from frontend-design:
export async function fetchFiles() { mediaFilesStore.update(state => ({ ...state, loading: true, error: null })); try { const files = await api.fetchMediaFiles(); mediaFilesStore.update(state => ({ ...state, items: files, loading: false })); } catch (err) { mediaFilesStore.update(state => ({ ...state, error: err instanceof Error ? err.message : 'Unknown error', loading: false })); } } export function selectFile(id: string) { mediaFilesStore.update(state => ({ ...state, selectedIds: [...state.selectedIds, id] })); } export function deselectFile(id: string) { mediaFilesStore.update(state => ({ ...state, selectedIds: state.selectedIds.filter(sid => sid !== id) })); } -
Implement derived stores (if defined in frontend-design):
export const selectedCount$ = derived(mediaFilesStore, $store => $store.selectedIds.length); -
Export all store functions and derived stores.
Key points:
- Do NOT call API directly in stores; stores call API client functions (defined in Step 4)
- Always set
loading: truebefore async operation,falsein finally - Always catch errors and update
errorfield - Never use
anytype; use typed state interface
Step 4: Implement API Client
For each endpoint in frontend-design.api_integration:
-
Create or update
src/lib/api/{domain}.ts: -
Import types and utility functions:
import type { MediaFile, UploadFileRequest } from '../types'; -
Implement async fetch functions:
const API_BASE = process.env.VITE_API_BASE || 'http://localhost:3000'; export async function fetchMediaFiles(): Promise<MediaFile[]> { const res = await fetch(`${API_BASE}/api/media/files`, { method: 'GET', headers: { 'Content-Type': 'application/json' } }); if (!res.ok) throw new Error(`Failed to fetch files: ${res.statusText}`); return res.json(); } export async function uploadFile(file: File): Promise<MediaFile> { const formData = new FormData(); formData.append('file', file); const res = await fetch(`${API_BASE}/api/uploads`, { method: 'POST', body: formData }); if (!res.ok) throw new Error(`Upload failed: ${res.statusText}`); return res.json(); } export async function deleteFile(id: string): Promise<void> { const res = await fetch(`${API_BASE}/api/media/files/${id}`, { method: 'DELETE', headers: { 'Content-Type': 'application/json' } }); if (!res.ok) throw new Error(`Delete failed: ${res.statusText}`); } -
Add Authorization header for auth_required endpoints:
import { get } from 'svelte/store'; import { authStore } from '../stores'; export async function fetchPrivateFiles(): Promise<MediaFile[]> { const $auth = get(authStore); const res = await fetch(`${API_BASE}/api/media/files`, { method: 'GET', headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${$auth.token}` } }); if (!res.ok) throw new Error(`Failed to fetch: ${res.statusText}`); return res.json(); } -
Error handling:
- Always throw descriptive Error objects (not raw strings)
- Include HTTP status or message
- Do NOT catch and suppress errors — let caller handle them
-
Do NOT make HTTP calls directly in stores or components — always use API client.
Step 5: Implement Atomic Components
Implement reusable, presentation-only components in src/lib/components/atomic/:
-
For each atomic component in frontend-design:
-
Create
.sveltefile with TypeScript:<script lang="ts"> interface Props { label: string; type?: 'button' | 'submit' | 'reset'; disabled?: boolean; size?: 'sm' | 'md' | 'lg'; variant?: 'primary' | 'secondary' | 'danger'; } let { label, type = 'button', disabled = false, size = 'md', variant = 'primary' }: Props = $props(); </script> <button {type} {disabled} class:disabled class:sm={size === 'sm'} class:md={size === 'md'} class:lg={size === 'lg'} class:primary={variant === 'primary'} class:secondary={variant === 'secondary'} class:danger={variant === 'danger'} on:click > {label} </button> <style module> button { @apply px-4 py-2 rounded font-medium transition; } .primary { @apply bg-blue-600 text-white hover:bg-blue-700; } .secondary { @apply bg-gray-200 text-gray-800 hover:bg-gray-300; } .danger { @apply bg-red-600 text-white hover:bg-red-700; } .sm { @apply text-sm px-2 py-1; } .lg { @apply text-lg px-6 py-3; } .disabled { @apply opacity-50 cursor-not-allowed; } </style> -
Key rules:
- All props must be typed (no
any) - No API calls, no store mutations
- Use TailwindCSS for styling
- Emit events if needed:
on:click, custom events viadispatch() - Props should be simple types or interfaces, not functions
- All props must be typed (no
-
Implement all atomic components before proceeding to feature components.
Step 6: Implement Feature and Page Components
Implement feature components in src/lib/components/features/:
-
Feature components (medium-complexity, domain-specific):
<script lang="ts"> import { onMount } from 'svelte'; import { mediaFilesStore } from '../../stores/mediaFiles'; import * as api from '../../api/mediaFiles'; import Button from '../atomic/Button.svelte'; import MediaGrid from './MediaGallery/Grid.svelte'; let loading = false; let error: string | null = null; async function handleUpload() { loading = true; error = null; try { const input = document.createElement('input'); input.type = 'file'; input.onchange = async (e) => { const file = (e.target as HTMLInputElement).files?.[0]; if (file) { const result = await api.uploadFile(file); mediaFilesStore.addFile(result); } }; input.click(); } catch (err) { error = err instanceof Error ? err.message : 'Upload failed'; } finally { loading = false; } } onMount(async () => { try { await mediaFilesStore.fetchFiles(); } catch (err) { error = err instanceof Error ? err.message : 'Failed to load files'; } }); </script> <div class="space-y-4"> {#if error} <div class="p-4 bg-red-100 text-red-800 rounded"> {error} </div> {/if} <Button label="Upload File" on:click={handleUpload} disabled={loading} /> {#if loading} <div class="text-center py-8">Loading...</div> {:else} <MediaGrid items={$mediaFilesStore.items} /> {/if} </div> -
Page components in
src/routes/{route}/+page.svelte:<script lang="ts"> import AppLayout from '../../lib/components/layout/AppLayout.svelte'; import MediaUpload from '../../lib/components/features/MediaUpload.svelte'; </script> <AppLayout> <div class="container mx-auto"> <h1 class="text-2xl font-bold mb-6">Media Library</h1> <MediaUpload /> </div> </AppLayout> -
Key principles:
- Feature components call API functions and update stores
- Page components orchestrate layout and feature components
- All async operations must have loading + error states
- Use
onMountto load page data - Use
$storesyntax to read store values reactively
Step 7: Implement Loading and Error States
For every API call in every component:
-
Create reactive state variables:
let loading = false; let error: string | null = null; -
Wrap API calls with try-catch-finally:
async function fetchData() { loading = true; error = null; try { const data = await api.fetchData(); store.update(s => ({ ...s, data })); } catch (err) { error = err instanceof Error ? err.message : 'An error occurred'; } finally { loading = false; } } -
Show loading UI while loading:
{#if loading} <Skeleton /> {:else if error} <ErrorMessage message={error} /> {:else} <Content /> {/if} -
Disable form submissions while loading:
<Button label="Submit" on:click={handleSubmit} disabled={loading} />
Step 8: Write Tests and Verify
-
Create test files using Vitest + Svelte Testing Library:
// tests/components/Button.test.ts import { describe, it, expect } from 'vitest'; import { render, screen } from '@testing-library/svelte'; import userEvent from '@testing-library/user-event'; import Button from '../../src/lib/components/atomic/Button.svelte'; describe('Button', () => { it('renders with label', () => { render(Button, { props: { label: 'Click me' } }); expect(screen.getByText('Click me')).toBeInTheDocument(); }); it('dispatches click event', async () => { const { component } = render(Button, { props: { label: 'Click' } }); let clicked = false; component.$on('click', () => { clicked = true; }); await userEvent.click(screen.getByRole('button')); expect(clicked).toBe(true); }); it('disables when disabled prop is true', () => { render(Button, { props: { label: 'Click', disabled: true } }); expect(screen.getByRole('button')).toBeDisabled(); }); }); -
Test store actions:
// tests/stores/mediaFiles.test.ts import { describe, it, expect } from 'vitest'; import { mediaFilesStore, selectFile } from '../../src/lib/stores/mediaFiles'; describe('mediaFilesStore', () => { it('initializes with empty items', () => { let state; mediaFilesStore.subscribe(s => { state = s; }); expect(state.items).toEqual([]); }); it('selectFile adds ID to selectedIds', () => { selectFile('file-123'); let state; mediaFilesStore.subscribe(s => { state = s; }); expect(state.selectedIds).toContain('file-123'); }); }); -
Run tests and verify they pass:
npm run test -
Verify acceptance criteria:
- For each criterion in task-spec, verify the implementation satisfies it
- Manual testing: navigate the app, test key workflows
- Check for console errors or warnings
Step 9: Final Verification
-
Run full test suite:
npm run test- All tests must pass.
- No errors, no warnings.
-
Verify file structure matches frontend-design.file_structure:
- All specified directories exist
- All specified files exist
- No unexpected files
-
Verify acceptance criteria are met:
- For each criterion in task-spec.acceptance_criteria, verify the implementation satisfies it
- Document any unmet criteria (should be none)
-
Check for TypeScript errors:
npm run check- No TypeScript errors or warnings
-
Check for build errors:
npm run build- Build succeeds without errors
Post-Execution
On successful completion:
✓ Frontend implementation complete for TSK-NNN.
Files created/modified:
✓ src/lib/types/media.ts (3 interfaces)
✓ src/lib/types/auth.ts (2 interfaces)
✓ src/lib/stores/mediaFiles.ts
✓ src/lib/stores/auth.ts
✓ src/lib/api/mediaFiles.ts (5 functions)
✓ src/lib/api/auth.ts (3 functions)
✓ src/lib/components/atomic/Button.svelte
✓ src/lib/components/atomic/TextField.svelte
✓ src/lib/components/layout/AppLayout.svelte
✓ src/lib/components/features/MediaGallery/Grid.svelte
✓ src/routes/+page.svelte
✓ src/routes/library/+page.svelte
✓ tests/ (N test files)
Tests:
npm run test result: 24 passed, 0 failed
Build:
npm run build: SUCCESS
Acceptance criteria verified:
✓ Users can upload media files
✓ Users can browse media gallery
✓ Users can delete media files
✓ All API calls include auth headers
✓ All async operations show loading/error states
Next: Deploy to production or proceed to next task phase
Error Handling
| Error | Action |
|---|---|
frontend-design.TSK-NNN.md not found | Stop. Instruct agent: "Run /frontend-design first." |
| TypeScript compilation errors | Stop and report: "[list errors]". Agent must fix. |
| Test failures | Stop and report: "[list failed tests]". Agent must fix. |
| Missing prop typing | Stop and report: "[component] has untyped props". Agent must fix. |
| API call without auth header | Stop and report: "[function] missing auth header". Agent must fix. |
| Inline API calls in components | Stop and report: "[component] makes fetch directly". Refactor to API client. |
References
- input.template.yaml: Schema for frontend-design input, task-spec, backend-design
- output.template.yaml: Contract showing what was implemented
- rules.md: Mandatory conventions (must enforce during implementation)
Compatibility Notes
- Framework: Read
task-spec.execution_plan.tech_stack.frontendto determine framework. This skill is optimized for Svelte 5 but patterns apply to other frameworks (React, Vue, etc.). - Project setup: Assumes SvelteKit (v2+) with TypeScript, Tailwind CSS, Vitest pre-configured.
- API base: Reads
VITE_API_BASEenvironment variable; defaults tohttp://localhost:3000. - Auth: Assumes JWT token in
authStore.token; adjust for other auth methods.
Source
git clone https://github.com/itssungho17/ssdam/blob/main/templetes/frontend-implementation/SKILL.mdView on GitHub Overview
frontend-implementation is the final SSDAM step that reads frontend-design specifications and writes working, tested Svelte frontends directly into the project root. It enables Cursor AI to autonomously produce concrete UI code, including components, stores, API clients, routes, and tests. This accelerates delivery and enforces design-to-code fidelity with automated verification.
How This Skill Works
The agent loads task-spec.yaml to determine the frontend tech stack, project root, and acceptance criteria, then reads frontend-design.md (and optional backend-design.md) to extract pages, components, stores, API integrations, and test strategy. It builds an ordered plan (types, stores, API client, atomic components, features, layout, routes, tests) and writes code into project_root under the structure expected by SvelteKit (src/lib, src/routes, tests). Finally, it runs tests to verify adherence to acceptance criteria before completion.
When to Use It
- You have a complete frontend-design.TSK-NNN.md and a task-spec.yaml that require full code generation in SvelteKit
- Backend design provides precise API endpoints and schemas to align the frontend client and types
- You need a fully wired frontend (components, stores, API client, routes) generated automatically with tests
- Traceability is required, mapping all code to requirement_ids and acceptance criteria
- Autonomous Cursor AI execution is preferred to deliver working frontend code with verification
Quick Start
- Step 1: Load task-spec.yaml and frontend-design.TSK-NNN.md (plus optional backend-design).
- Step 2: Create the phased implementation plan (types, stores, API client, components, routes, tests).
- Step 3: Implement code into project_root and run npm run test to verify acceptance criteria.
Best Practices
- Follow the project_root structure exactly: src/routes, src/lib/components, src/lib/stores, src/lib/api, tests
- Define TypeScript interfaces early from frontend-design.api_integration and schemas in backend-design to ensure type safety
- Build bottom-up: atomic components first, then feature components, then layout and routes
- Include tests for components and stores; wire API calls to ensure end-to-end behavior
- Validate acceptance criteria and maintain clear traceability with requirement_ids
Example Use Cases
- Generated a product catalog UI in a new SvelteKit app from frontend-design, including routes, components, API client, and tests
- Built a user authentication flow: stores for auth state, API client for login/logout, and protected routes with tests
- Created a reusable atomic component set from design specs and composed them into feature components
- Developed an admin panel with CRUD routes and corresponding tests driven by the frontend design
- Compared implemented output against acceptance criteria using npm run test to confirm success