symfonia-ai-tools 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (74) hide show
  1. package/README.md +489 -0
  2. package/bin/cli.mjs +35 -0
  3. package/lib/installer.mjs +495 -0
  4. package/lib/questions.mjs +332 -0
  5. package/lib/ui.mjs +76 -0
  6. package/lib/utils.mjs +231 -0
  7. package/package.json +26 -0
  8. package/templates/base/CLAUDE.md +34 -0
  9. package/templates/base/_ai/_guidelines_header.md +70 -0
  10. package/templates/base/_ai/context/README.md +20 -0
  11. package/templates/base/_ai/prompts/codereview.prompt.md +324 -0
  12. package/templates/base/_ai/prompts/duplicate-code-analysis.prompt.md +128 -0
  13. package/templates/base/_ai/prompts/figma-analysis.prompt.md +155 -0
  14. package/templates/base/_ai/prompts/security-review.prompt.md +46 -0
  15. package/templates/base/_ai/skills/README.md +80 -0
  16. package/templates/base/_ai/skills/TEMPLATE.md +106 -0
  17. package/templates/base/_ai/skills/babysit-prs/SKILL.md +105 -0
  18. package/templates/base/_ai/skills/debug/SKILL.md +93 -0
  19. package/templates/base/_ai/skills/fill-worklogs/SKILL.md +158 -0
  20. package/templates/base/_ai/skills/hotfix/SKILL.md +52 -0
  21. package/templates/base/_ai/skills/jira-task/SKILL.md +170 -0
  22. package/templates/base/_ai/skills/my-prs/SKILL.md +78 -0
  23. package/templates/base/_ai/skills/pr-dashboard/SKILL.md +43 -0
  24. package/templates/base/_ai/skills/pr-prepare/SKILL.md +106 -0
  25. package/templates/base/_ai/skills/refactor/SKILL.md +87 -0
  26. package/templates/base/_ai/skills/write-tests/SKILL.md +109 -0
  27. package/templates/base/_claude/settings.local.json +37 -0
  28. package/templates/base/_cursor/rules/global.mdc +7 -0
  29. package/templates/base/_editorconfig +18 -0
  30. package/templates/base/_gemini/settings.json +3 -0
  31. package/templates/base/_github/copilot-instructions.md +1 -0
  32. package/templates/base/_github/pull_request_template.md +23 -0
  33. package/templates/base/_gitignore +22 -0
  34. package/templates/base/_junie/guidelines.md +1 -0
  35. package/templates/base/commit-instructions.md +92 -0
  36. package/templates/packs/docker/_ai/instructions/docker.instructions.md +193 -0
  37. package/templates/packs/docker/_guidelines.md +10 -0
  38. package/templates/packs/docker/pack.json +8 -0
  39. package/templates/packs/laravel/_ai/instructions/api-resource.instructions.md +251 -0
  40. package/templates/packs/laravel/_ai/instructions/module.instructions.md +133 -0
  41. package/templates/packs/laravel/_ai/instructions/service-repository.instructions.md +215 -0
  42. package/templates/packs/laravel/_ai/instructions/testing.instructions.md +278 -0
  43. package/templates/packs/laravel/_ai/skills/migration/SKILL.md +172 -0
  44. package/templates/packs/laravel/_ai/skills/new-endpoint/SKILL.md +165 -0
  45. package/templates/packs/laravel/_ai/skills/new-module/SKILL.md +208 -0
  46. package/templates/packs/laravel/_ai/skills/queued-job/SKILL.md +248 -0
  47. package/templates/packs/laravel/_ai/skills/testing-feature/SKILL.md +196 -0
  48. package/templates/packs/laravel/_ai/skills/testing-manual/SKILL.md +186 -0
  49. package/templates/packs/laravel/_ai/skills/testing-unit/SKILL.md +200 -0
  50. package/templates/packs/laravel/_guidelines.md +25 -0
  51. package/templates/packs/laravel/pack.json +6 -0
  52. package/templates/packs/playwright/_ai/instructions/playwright.instructions.md +219 -0
  53. package/templates/packs/playwright/_ai/skills/playwright/README.md +194 -0
  54. package/templates/packs/playwright/_ai/skills/playwright/SKILL.md +1245 -0
  55. package/templates/packs/playwright/_ai/skills/playwright-codereview/SKILL.md +642 -0
  56. package/templates/packs/playwright/_ai/skills/playwright-record/README.md +87 -0
  57. package/templates/packs/playwright/_ai/skills/playwright-record/SKILL.md +564 -0
  58. package/templates/packs/playwright/_guidelines.md +12 -0
  59. package/templates/packs/playwright/pack.json +9 -0
  60. package/templates/packs/storybook/_ai/instructions/storybook.instructions.md +181 -0
  61. package/templates/packs/storybook/pack.json +6 -0
  62. package/templates/packs/vitest/_ai/instructions/vitest.instructions.md +688 -0
  63. package/templates/packs/vitest/pack.json +6 -0
  64. package/templates/packs/vue3/_ai/instructions/api.instructions.md +163 -0
  65. package/templates/packs/vue3/_ai/instructions/coding-conventions.instructions.md +160 -0
  66. package/templates/packs/vue3/_ai/instructions/composables.instructions.md +218 -0
  67. package/templates/packs/vue3/_ai/instructions/forms.instructions.md +227 -0
  68. package/templates/packs/vue3/_ai/instructions/store.instructions.md +504 -0
  69. package/templates/packs/vue3/_ai/instructions/vue.instructions.md +339 -0
  70. package/templates/packs/vue3/_ai/skills/api-integration/SKILL.md +195 -0
  71. package/templates/packs/vue3/_ai/skills/new-component/SKILL.md +133 -0
  72. package/templates/packs/vue3/_ai/skills/new-module/SKILL.md +177 -0
  73. package/templates/packs/vue3/_guidelines.md +45 -0
  74. package/templates/packs/vue3/pack.json +11 -0
@@ -0,0 +1,339 @@
1
+ ---
2
+ applyTo: "**/*.vue"
3
+ ---
4
+
5
+ # Vue Component Instructions
6
+
7
+ ## Core Vue Component Principles
8
+
9
+ ### Architecture Rules
10
+ - **Vue components (.vue files)**: Visual layer ONLY - NO business logic
11
+ - **ALL business logic** must go in composables (`useXxx` pattern)
12
+
13
+ ### Component Structure Template
14
+ ```vue
15
+ <template>
16
+ <!-- Use components from your component library -->
17
+ <Button
18
+ :disabled="loading"
19
+ :data-testid="dataTestId"
20
+ @click="handleClick"
21
+ >
22
+ {{ $t('button.save') }}
23
+ </Button>
24
+
25
+ <!-- Always use translation keys, never hardcoded text -->
26
+ <div>
27
+ {{ $t('component.description') }}
28
+ </div>
29
+ </template>
30
+
31
+ <script setup lang="ts">// Import components
32
+ import { Button } from '{{COMPONENT_LIB_IMPORT}}'
33
+ // 1. Import composables for ALL business logic
34
+ import { useComponentLogic } from '@/composables/useComponentLogic'
35
+
36
+ // 2. Define props with proper TypeScript interfaces (prefix with I)
37
+ interface IComponentProps {
38
+ userId: string
39
+ isEditable: boolean
40
+ dataTestId?: string
41
+ }
42
+
43
+ // 3. Define emits with proper TypeScript interfaces
44
+ interface IComponentEmits {
45
+ save: [data: IFormData]
46
+ cancel: []
47
+ }
48
+
49
+ const props = withDefaults(defineProps<IComponentProps>(), {
50
+ dataTestId: 'component'
51
+ })
52
+
53
+ const emit = defineEmits<IComponentEmits>()
54
+
55
+ // 4. Use composables for ALL business logic - NO logic in component
56
+ const {
57
+ loading,
58
+ handleSave,
59
+ handleCancel,
60
+ buttonVariant,
61
+ textVariant
62
+ } = useComponentLogic(props, emit)
63
+
64
+ // 5. Only simple event handlers that delegate to composables
65
+ const handleClick = () => {
66
+ handleSave()
67
+ }
68
+ </script>
69
+
70
+ <style scoped>
71
+ /* Minimal styles only */
72
+ /* DO NOT interfere with existing CSS */
73
+ </style>
74
+ ```
75
+
76
+ ## Mandatory Requirements
77
+
78
+ ### 1. TypeScript Interfaces
79
+ - **ALL props must have interfaces** prefixed with `I` (e.g., `IComponentProps`)
80
+ - **ALL emits must be typed** with interfaces (e.g., `IComponentEmits`)
81
+ - **NEVER use `any` type** - strict typing required
82
+ - **Import types properly** with `import type { ... }`
83
+
84
+ ### 2. Business Logic Separation
85
+ - **NO business logic in Vue components**
86
+ - **ALL logic goes in composables** (`/src/composables/useXxxLogic.ts`)
87
+ - **Components only handle**: rendering, props, emits, basic event delegation
88
+ - **Use composables for**: API calls, state management, calculations, validations
89
+
90
+ ### 3. Component Library Usage
91
+ - **ALWAYS use existing components** from your component library (Button, InputText, Loader, Alert, Modal, etc.)
92
+ - **Check existing components** before creating new ones
93
+ - **Follow existing patterns** as examples in the codebase
94
+ - **NEVER recreate existing functionality**
95
+ - Import components: `import { Button, InputText, Alert } from '{{COMPONENT_LIB_IMPORT}}'`
96
+
97
+ ### 4. Internationalization (i18n)
98
+ - **ALL user-facing text MUST use `$t('key')`**
99
+ - **NEVER hardcode strings** in templates
100
+ - **Check existing translation keys** first
101
+ - **Use descriptive, hierarchical keys**: `module.component.action`
102
+
103
+ ### 5. Data Test IDs
104
+ - **ALWAYS add `data-testid`** for testing
105
+ - **Pass `dataTestId` as prop** with default value
106
+ - **Use camelCase for `dataTestId` values**: `'bellNotifications'`, `'userForm'`, `'saveButton'`
107
+ - Correct: `dataTestId: 'bellNotifications'`
108
+ - Wrong: `dataTestId: 'bell-notifications'`
109
+
110
+ ### 6. CSS Class Naming
111
+ - **Use single dash `-` as separator** (NOT BEM double underscore `__`)
112
+ - Correct: `.notification-item-content`, `.user-form-header`
113
+ - Wrong: `.notification-item__content`, `.user-form__header`
114
+ - **No CSS comments** - styles should be self-explanatory
115
+
116
+ ### 7. Console and Debugging
117
+ - **NEVER use console methods** in production code
118
+ - Use a centralized error handler utility for errors
119
+ - Remove all debugging statements before committing
120
+
121
+ ## Component Development Guidelines
122
+
123
+ ### Props and Emits Best Practices
124
+ ```vue
125
+ <script setup lang="ts">
126
+ // Good: Proper interface definition
127
+ interface IUserFormProps {
128
+ userId: string
129
+ initialData?: IUserData
130
+ isReadonly: boolean
131
+ dataTestId?: string
132
+ }
133
+
134
+ interface IUserFormEmits {
135
+ save: [userData: IUserData]
136
+ cancel: []
137
+ validation: [isValid: boolean]
138
+ }
139
+
140
+ // Good: Default values with camelCase
141
+ const props = withDefaults(defineProps<IUserFormProps>(), {
142
+ dataTestId: 'userForm',
143
+ isReadonly: false
144
+ })
145
+
146
+ const emit = defineEmits<IUserFormEmits>()
147
+
148
+ // Good: Delegate everything to composables
149
+ const { formData, validation, handleSave } = useUserForm(props, emit)
150
+ </script>
151
+ ```
152
+
153
+ ### Error Handling
154
+ ```vue
155
+ <script setup lang="ts">
156
+ // Good: Error handling in composables
157
+ const { error, loading, clearError } = useUserManagement()
158
+
159
+ // Good: Error display with components
160
+ </script>
161
+
162
+ <template>
163
+ <Alert
164
+ v-if="error"
165
+ type="error"
166
+ :data-testid="`${dataTestId}-error`"
167
+ @close="clearError"
168
+ >
169
+ {{ $t('error.generic') }}
170
+ </Alert>
171
+ </template>
172
+ ```
173
+
174
+ ### Loading States
175
+ ```vue
176
+ <template>
177
+ <!-- Good: Loading states with components -->
178
+ <Loader v-if="loading" :data-testid="`${dataTestId}-loader`" />
179
+
180
+ <Button
181
+ :disabled="loading"
182
+ :loading="loading"
183
+ :data-testid="`${dataTestId}-submit`"
184
+ >
185
+ {{ $t('form.submit') }}
186
+ </Button>
187
+ </template>
188
+ ```
189
+
190
+ ## Common Mistakes to Avoid
191
+
192
+ ### Never Do This
193
+ ```vue
194
+ <script setup lang="ts">
195
+ // BAD: Business logic in component
196
+ const saveUser = async () => {
197
+ try {
198
+ const response = await api.post('/users', userData)
199
+ // Complex business logic here
200
+ } catch (error) {
201
+ // Error handling here
202
+ }
203
+ }
204
+
205
+ // BAD: Hardcoded strings
206
+ const title = 'User Profile'
207
+
208
+ // BAD: Using any type
209
+ const props = defineProps<{ data: any }>()
210
+
211
+ </script>
212
+
213
+ <template>
214
+ <!-- BAD: Hardcoded text -->
215
+ <h1>User Profile</h1>
216
+ </template>
217
+ ```
218
+
219
+ ### Always Do This
220
+ ```vue
221
+ <script setup lang="ts">
222
+ // GOOD: Use composables for business logic
223
+ const { saveUser, loading, error } = useUserManagement(props, emit)
224
+
225
+ // GOOD: Proper TypeScript interfaces
226
+ interface IUserProfileProps {
227
+ userId: string
228
+ dataTestId?: string
229
+ }
230
+
231
+ const props = withDefaults(defineProps<IUserProfileProps>(), {
232
+ dataTestId: 'user-profile'
233
+ })
234
+ </script>
235
+
236
+ <template>
237
+ <!-- GOOD: Use translation keys -->
238
+ <h1 :data-testid="`${dataTestId}-title`">
239
+ {{ $t('user.profile.title') }}
240
+ </h1>
241
+
242
+ <!-- GOOD: Use components from your component library -->
243
+ <Button
244
+ :disabled="loading"
245
+ :data-testid="`${dataTestId}-save`"
246
+ @click="saveUser"
247
+ >
248
+ {{ $t('form.save') }}
249
+ </Button>
250
+ </template>
251
+ ```
252
+
253
+ ## Testing Requirements
254
+
255
+ ### Component Testing with VITEST
256
+ - **Test props and emits behavior**
257
+ - **Test user interactions**
258
+ - **Mock composables, not component internals**
259
+ - **Use `data-testid` for element selection**
260
+
261
+ ```typescript
262
+ // Example component test
263
+ describe('UserForm Component', () => {
264
+ it('should emit save event when form is submitted', async () => {
265
+ const wrapper = mount(UserForm, {
266
+ props: { userId: '123', dataTestId: 'test-form' }
267
+ })
268
+
269
+ await wrapper.find('[data-testid="test-form-save"]').trigger('click')
270
+ expect(wrapper.emitted('save')).toBeTruthy()
271
+ })
272
+ })
273
+ ```
274
+
275
+ ## Performance Optimization
276
+
277
+ ### Vue Performance Best Practices
278
+ ```vue
279
+ <template>
280
+ <!-- Use v-memo for expensive renders -->
281
+ <div v-memo="[expensiveData]">
282
+ <!-- Complex list items -->
283
+ </div>
284
+
285
+ <!-- Proper key attributes in v-for -->
286
+ <div
287
+ v-for="item in items"
288
+ :key="item.id"
289
+ :data-testid="`item-${item.id}`"
290
+ >
291
+ {{ item.name }}
292
+ </div>
293
+ </template>
294
+ ```
295
+
296
+ ## State Management Integration
297
+
298
+ ### Pinia Store Usage
299
+ ```vue
300
+ <script setup lang="ts">
301
+ // Good: Access stores through composables
302
+ const { users, addUser } = useUserStore()
303
+
304
+ // Good: Reactive computed properties
305
+ const filteredUsers = computed(() =>
306
+ users.value.filter(user => user.isActive)
307
+ )
308
+ </script>
309
+ ```
310
+
311
+ ## File Organization
312
+
313
+ ### Component Location Rules
314
+ - **Module-specific components**: `{{MODULE_PATH}}/components/`
315
+ - **Shared components**: `{{COMPONENT_LIB_PATH}}`
316
+ - **Component composables**: `{{MODULE_PATH}}/composables/`
317
+ - **Component types**: `{{MODULE_PATH}}/types/`
318
+
319
+ ### Import Organization
320
+ ```vue
321
+ <script setup lang="ts">
322
+ // 1. Vue imports
323
+ import { computed, ref } from 'vue'
324
+
325
+ // 2. External library imports
326
+ import { useI18n } from 'vue-i18n'
327
+
328
+ // 3. Project components
329
+ import { Button, InputText, Alert, Loader } from '{{COMPONENT_LIB_IMPORT}}'
330
+
331
+ // 4. Internal composables
332
+ import { useUserManagement } from '@/composables/useUserManagement'
333
+
334
+ // 5. Types
335
+ import type { IUser, IUserForm } from '@/types/user'
336
+ </script>
337
+ ```
338
+
339
+ Remember: Vue components are purely visual layers. All business logic, API calls, and complex state management must be handled by composables following the established architecture patterns.
@@ -0,0 +1,195 @@
1
+ # Skill: API Integration (Vue 3 + TypeScript)
2
+
3
+ ## Trigger
4
+ Use when connecting frontend to a new or existing backend API endpoint.
5
+
6
+ ## Input
7
+ - API endpoint (method + URL, e.g. `GET /api/users`)
8
+ - Request payload (if any)
9
+ - Response format (JSON structure)
10
+ - Target module (where to add integration)
11
+
12
+ ## Steps
13
+
14
+ ### 1. Define types from API contract
15
+
16
+ ```typescript
17
+ // types/[module].types.ts
18
+
19
+ // Response type - matches API response exactly
20
+ export interface I[Entity]Response {
21
+ id: number;
22
+ name: string;
23
+ status: T[Entity]Status;
24
+ // ... match API response fields
25
+ created_at: string; // keep API casing in response type
26
+ }
27
+
28
+ // Frontend type - camelCase, parsed dates
29
+ export interface I[Entity] {
30
+ id: number;
31
+ name: string;
32
+ status: T[Entity]Status;
33
+ createdAt: Date;
34
+ }
35
+
36
+ // Request payload type
37
+ export interface I[Entity]CreatePayload {
38
+ name: string;
39
+ status: T[Entity]Status;
40
+ }
41
+
42
+ // Mapper
43
+ export function map[Entity](raw: I[Entity]Response): I[Entity] {
44
+ return {
45
+ id: raw.id,
46
+ name: raw.name,
47
+ status: raw.status,
48
+ createdAt: new Date(raw.created_at),
49
+ };
50
+ }
51
+ ```
52
+
53
+ ### 2. Add service method
54
+
55
+ ```typescript
56
+ // services/[Module].service.ts
57
+ import { useApi } from '@/composables/useApi';
58
+ import type { I[Entity]Response, I[Entity]CreatePayload } from '../types/[module].types';
59
+
60
+ const api = useApi();
61
+ const BASE = '{{API_BASE_URL}}/[endpoint]';
62
+
63
+ const [Module]Service = {
64
+ // ... existing methods
65
+
66
+ // New method
67
+ fetchItems: (params?: Record<string, unknown>) =>
68
+ api.get<I[Entity]Response[]>(BASE, { params }),
69
+
70
+ createItem: (data: I[Entity]CreatePayload) =>
71
+ api.post<I[Entity]Response>(BASE, data),
72
+ };
73
+
74
+ export default [Module]Service;
75
+ ```
76
+
77
+ ### 3. Update store
78
+
79
+ ```typescript
80
+ // [module].store.ts - add to existing store
81
+
82
+ import { map[Entity] } from './types/[module].types';
83
+
84
+ // Inside defineStore:
85
+ async function fetchItems() {
86
+ loading.value = true;
87
+ try {
88
+ const { data } = await [Module]Service.fetchItems();
89
+ items.value = data.map(map[Entity]);
90
+ } finally {
91
+ loading.value = false;
92
+ }
93
+ }
94
+ ```
95
+
96
+ ### 4. Create/update composable
97
+
98
+ If the integration needs form handling or complex logic, create a composable:
99
+
100
+ ```typescript
101
+ // composables/use[Action].ts
102
+ import { ref } from 'vue';
103
+ import { use[Module]Store } from '../[module].store';
104
+ import [Module]Service from '../services/[Module].service';
105
+ import type { I[Entity]CreatePayload } from '../types/[module].types';
106
+
107
+ export function use[Action]() {
108
+ const store = use[Module]Store();
109
+ const submitting = ref(false);
110
+ const error = ref<string | null>(null);
111
+
112
+ async function submit(payload: I[Entity]CreatePayload) {
113
+ submitting.value = true;
114
+ error.value = null;
115
+ try {
116
+ await [Module]Service.createItem(payload);
117
+ await store.fetchItems(); // refresh list
118
+ } catch (e) {
119
+ error.value = 'Operation failed';
120
+ throw e;
121
+ } finally {
122
+ submitting.value = false;
123
+ }
124
+ }
125
+
126
+ return { submitting, error, submit };
127
+ }
128
+ ```
129
+
130
+ ### 5. Connect to view
131
+
132
+ ```vue
133
+ <script setup lang="ts">
134
+ import { use[Module]Store } from '../[module].store';
135
+ import { use[Action] } from '../composables/use[Action]';
136
+
137
+ const store = use[Module]Store();
138
+ const { submitting, error, submit } = use[Action]();
139
+
140
+ // Fetch on mount
141
+ store.fetchItems();
142
+ </script>
143
+ ```
144
+
145
+ ### 6. Handle errors
146
+
147
+ Ensure error states are handled in the UI:
148
+ - Loading state (spinner/skeleton)
149
+ - Empty state (no results)
150
+ - Error state (API failure message)
151
+ - Validation errors (422 response → field-level errors)
152
+
153
+ ### 7. Write tests
154
+
155
+ ```typescript
156
+ // Test service method (mock API)
157
+ describe('[Module]Service', () => {
158
+ it('fetches items from API', async () => {
159
+ // mock useApi().get to return test data
160
+ // verify service calls correct URL
161
+ // verify response is returned
162
+ });
163
+ });
164
+
165
+ // Test store action
166
+ describe('use[Module]Store', () => {
167
+ it('fetchItems maps response and updates state', async () => {
168
+ // mock service
169
+ // call store.fetchItems()
170
+ // verify items are mapped and stored
171
+ });
172
+ });
173
+
174
+ // Test composable
175
+ describe('use[Action]', () => {
176
+ it('submits and refreshes list', async () => {
177
+ // mock service + store
178
+ // call submit()
179
+ // verify service called + store refreshed
180
+ });
181
+ });
182
+ ```
183
+
184
+ ### 8. Verification checklist
185
+
186
+ - [ ] Types match API contract exactly
187
+ - [ ] Mapper converts API casing → frontend casing
188
+ - [ ] Service method uses correct HTTP method + URL
189
+ - [ ] Store action handles loading state
190
+ - [ ] Error states handled in UI (loading, empty, error)
191
+ - [ ] 422 validation errors mapped to form fields
192
+ - [ ] Composable created if logic is non-trivial
193
+ - [ ] Tests cover service, store, composable
194
+ - [ ] No `any` types — everything strictly typed
195
+ - [ ] No hardcoded API URLs (use base URL constant)
@@ -0,0 +1,133 @@
1
+ # Skill: New Component (Vue 3 + TypeScript)
2
+
3
+ ## Trigger
4
+ Use when creating a new reusable Vue component.
5
+
6
+ ## Input
7
+ - Component name (PascalCase, e.g. "UserCard")
8
+ - Props specification
9
+ - Events specification
10
+ - Target directory
11
+
12
+ ## Steps
13
+
14
+ ### 1. Create files
15
+
16
+ ```
17
+ [target-dir]/
18
+ ├── [ComponentName].vue
19
+ ├── [ComponentName].stories.ts
20
+ └── [ComponentName].test.ts
21
+ ```
22
+
23
+ ### 2. Write component
24
+
25
+ ```vue
26
+ <template>
27
+ <div class="[component-name]" :data-testid="dataTestId">
28
+ <!-- component template -->
29
+ </div>
30
+ </template>
31
+
32
+ <script setup lang="ts">
33
+ interface I[ComponentName]Props {
34
+ /** Description of prop */
35
+ dataTestId?: string;
36
+ // ... props
37
+ }
38
+
39
+ interface I[ComponentName]Emits {
40
+ (e: 'update', value: unknown): void;
41
+ // ... events
42
+ }
43
+
44
+ const props = withDefaults(defineProps<I[ComponentName]Props>(), {
45
+ dataTestId: '[componentName]',
46
+ });
47
+
48
+ const emit = defineEmits<I[ComponentName]Emits>();
49
+ </script>
50
+
51
+ <style scoped lang="scss">
52
+ .component-name {
53
+ // single-dash CSS classes only
54
+ }
55
+ </style>
56
+ ```
57
+
58
+ Rules:
59
+ - `<template>` → `<script setup>` → `<style scoped>` order
60
+ - Interface props with `I` prefix
61
+ - `withDefaults` for default values
62
+ - JSDoc on props for Storybook autodocs
63
+ - `data-testid` on root element
64
+ - CSS: single-dash classes, no BEM
65
+
66
+ ### 3. Write Storybook story
67
+
68
+ ```typescript
69
+ import type { Meta, StoryObj } from '@storybook/vue3';
70
+ import [ComponentName] from './[ComponentName].vue';
71
+
72
+ const meta: Meta<typeof [ComponentName]> = {
73
+ title: 'Components/[Category]/[ComponentName]',
74
+ component: [ComponentName],
75
+ argTypes: {
76
+ // control for each prop
77
+ },
78
+ tags: ['autodocs'],
79
+ };
80
+
81
+ export default meta;
82
+ type Story = StoryObj<typeof [ComponentName]>;
83
+
84
+ export const Default: Story = {
85
+ args: {
86
+ // default props
87
+ },
88
+ };
89
+
90
+ // Add variants: Disabled, Loading, Error, etc.
91
+ ```
92
+
93
+ ### 4. Write test
94
+
95
+ ```typescript
96
+ import { mount } from '@vue/test-utils';
97
+ import { describe, expect, it } from 'vitest';
98
+ import [ComponentName] from './[ComponentName].vue';
99
+
100
+ describe('[ComponentName]', () => {
101
+ it('renders with default props', () => {
102
+ const wrapper = mount([ComponentName]);
103
+ expect(wrapper.find('.[component-name]').exists()).toBe(true);
104
+ });
105
+
106
+ it('emits events correctly', async () => {
107
+ const wrapper = mount([ComponentName]);
108
+ // trigger action
109
+ expect(wrapper.emitted('update')).toBeTruthy();
110
+ });
111
+
112
+ it('has correct data-testid', () => {
113
+ const wrapper = mount([ComponentName], {
114
+ props: { dataTestId: 'custom' },
115
+ });
116
+ expect(wrapper.attributes('data-testid')).toBe('custom');
117
+ });
118
+ });
119
+ ```
120
+
121
+ ### 5. Verification checklist
122
+
123
+ - [ ] Component renders correctly
124
+ - [ ] Props typed with interface + `I` prefix
125
+ - [ ] Emits typed with interface
126
+ - [ ] `withDefaults` for optional props
127
+ - [ ] JSDoc comments on props (for Storybook)
128
+ - [ ] `data-testid` on root and interactive elements
129
+ - [ ] Story with `tags: ['autodocs']` and argTypes
130
+ - [ ] At least Default + 2 variant stories
131
+ - [ ] Test covers render, props, events
132
+ - [ ] CSS uses single-dash classes, scoped
133
+ - [ ] No `console.log`