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.
- package/README.md +489 -0
- package/bin/cli.mjs +35 -0
- package/lib/installer.mjs +495 -0
- package/lib/questions.mjs +332 -0
- package/lib/ui.mjs +76 -0
- package/lib/utils.mjs +231 -0
- package/package.json +26 -0
- package/templates/base/CLAUDE.md +34 -0
- package/templates/base/_ai/_guidelines_header.md +70 -0
- package/templates/base/_ai/context/README.md +20 -0
- package/templates/base/_ai/prompts/codereview.prompt.md +324 -0
- package/templates/base/_ai/prompts/duplicate-code-analysis.prompt.md +128 -0
- package/templates/base/_ai/prompts/figma-analysis.prompt.md +155 -0
- package/templates/base/_ai/prompts/security-review.prompt.md +46 -0
- package/templates/base/_ai/skills/README.md +80 -0
- package/templates/base/_ai/skills/TEMPLATE.md +106 -0
- package/templates/base/_ai/skills/babysit-prs/SKILL.md +105 -0
- package/templates/base/_ai/skills/debug/SKILL.md +93 -0
- package/templates/base/_ai/skills/fill-worklogs/SKILL.md +158 -0
- package/templates/base/_ai/skills/hotfix/SKILL.md +52 -0
- package/templates/base/_ai/skills/jira-task/SKILL.md +170 -0
- package/templates/base/_ai/skills/my-prs/SKILL.md +78 -0
- package/templates/base/_ai/skills/pr-dashboard/SKILL.md +43 -0
- package/templates/base/_ai/skills/pr-prepare/SKILL.md +106 -0
- package/templates/base/_ai/skills/refactor/SKILL.md +87 -0
- package/templates/base/_ai/skills/write-tests/SKILL.md +109 -0
- package/templates/base/_claude/settings.local.json +37 -0
- package/templates/base/_cursor/rules/global.mdc +7 -0
- package/templates/base/_editorconfig +18 -0
- package/templates/base/_gemini/settings.json +3 -0
- package/templates/base/_github/copilot-instructions.md +1 -0
- package/templates/base/_github/pull_request_template.md +23 -0
- package/templates/base/_gitignore +22 -0
- package/templates/base/_junie/guidelines.md +1 -0
- package/templates/base/commit-instructions.md +92 -0
- package/templates/packs/docker/_ai/instructions/docker.instructions.md +193 -0
- package/templates/packs/docker/_guidelines.md +10 -0
- package/templates/packs/docker/pack.json +8 -0
- package/templates/packs/laravel/_ai/instructions/api-resource.instructions.md +251 -0
- package/templates/packs/laravel/_ai/instructions/module.instructions.md +133 -0
- package/templates/packs/laravel/_ai/instructions/service-repository.instructions.md +215 -0
- package/templates/packs/laravel/_ai/instructions/testing.instructions.md +278 -0
- package/templates/packs/laravel/_ai/skills/migration/SKILL.md +172 -0
- package/templates/packs/laravel/_ai/skills/new-endpoint/SKILL.md +165 -0
- package/templates/packs/laravel/_ai/skills/new-module/SKILL.md +208 -0
- package/templates/packs/laravel/_ai/skills/queued-job/SKILL.md +248 -0
- package/templates/packs/laravel/_ai/skills/testing-feature/SKILL.md +196 -0
- package/templates/packs/laravel/_ai/skills/testing-manual/SKILL.md +186 -0
- package/templates/packs/laravel/_ai/skills/testing-unit/SKILL.md +200 -0
- package/templates/packs/laravel/_guidelines.md +25 -0
- package/templates/packs/laravel/pack.json +6 -0
- package/templates/packs/playwright/_ai/instructions/playwright.instructions.md +219 -0
- package/templates/packs/playwright/_ai/skills/playwright/README.md +194 -0
- package/templates/packs/playwright/_ai/skills/playwright/SKILL.md +1245 -0
- package/templates/packs/playwright/_ai/skills/playwright-codereview/SKILL.md +642 -0
- package/templates/packs/playwright/_ai/skills/playwright-record/README.md +87 -0
- package/templates/packs/playwright/_ai/skills/playwright-record/SKILL.md +564 -0
- package/templates/packs/playwright/_guidelines.md +12 -0
- package/templates/packs/playwright/pack.json +9 -0
- package/templates/packs/storybook/_ai/instructions/storybook.instructions.md +181 -0
- package/templates/packs/storybook/pack.json +6 -0
- package/templates/packs/vitest/_ai/instructions/vitest.instructions.md +688 -0
- package/templates/packs/vitest/pack.json +6 -0
- package/templates/packs/vue3/_ai/instructions/api.instructions.md +163 -0
- package/templates/packs/vue3/_ai/instructions/coding-conventions.instructions.md +160 -0
- package/templates/packs/vue3/_ai/instructions/composables.instructions.md +218 -0
- package/templates/packs/vue3/_ai/instructions/forms.instructions.md +227 -0
- package/templates/packs/vue3/_ai/instructions/store.instructions.md +504 -0
- package/templates/packs/vue3/_ai/instructions/vue.instructions.md +339 -0
- package/templates/packs/vue3/_ai/skills/api-integration/SKILL.md +195 -0
- package/templates/packs/vue3/_ai/skills/new-component/SKILL.md +133 -0
- package/templates/packs/vue3/_ai/skills/new-module/SKILL.md +177 -0
- package/templates/packs/vue3/_guidelines.md +45 -0
- package/templates/packs/vue3/pack.json +11 -0
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
---
|
|
2
|
+
applyTo: "**/*Form*.vue,**/*form*.ts,**/validation*"
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
# Forms & Validation - Instrukcje
|
|
6
|
+
|
|
7
|
+
## Walidacja - funkcje bazowe
|
|
8
|
+
|
|
9
|
+
Trzymaj funkcje walidacji w `utils/validation.ts`:
|
|
10
|
+
|
|
11
|
+
```typescript
|
|
12
|
+
// utils/validation.ts
|
|
13
|
+
export function isNotEmpty(value: unknown): boolean {
|
|
14
|
+
if (value === null || value === undefined) return false;
|
|
15
|
+
if (typeof value === 'string') return value.trim().length > 0;
|
|
16
|
+
if (Array.isArray(value)) return value.length > 0;
|
|
17
|
+
return true;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export function isValidEmail(value: string): boolean {
|
|
21
|
+
return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export function isValidPhone(value: string): boolean {
|
|
25
|
+
return /^\+?[\d\s-()]{7,15}$/.test(value);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export function isValidUrl(value: string): boolean {
|
|
29
|
+
try {
|
|
30
|
+
new URL(value);
|
|
31
|
+
return true;
|
|
32
|
+
} catch {
|
|
33
|
+
return false;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export function isValidLength(value: string, min: number, max?: number): boolean {
|
|
38
|
+
const len = value.trim().length;
|
|
39
|
+
if (len < min) return false;
|
|
40
|
+
if (max !== undefined && len > max) return false;
|
|
41
|
+
return true;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export function isValidZipCode(value: string): boolean {
|
|
45
|
+
return /^\d{2}-\d{3}$/.test(value);
|
|
46
|
+
}
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
## ValidationBuilder (fluent API)
|
|
50
|
+
|
|
51
|
+
Do walidacji zlozonych obiektow:
|
|
52
|
+
|
|
53
|
+
```typescript
|
|
54
|
+
// utils/validation-builder.ts
|
|
55
|
+
interface IValidationResult {
|
|
56
|
+
isValid: boolean;
|
|
57
|
+
errors: string[];
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
class ValidationBuilder<T> {
|
|
61
|
+
private entity: T;
|
|
62
|
+
private errors: string[] = [];
|
|
63
|
+
|
|
64
|
+
constructor(entity: T) {
|
|
65
|
+
this.entity = entity;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
validateString(getValue: (e: T) => string, fieldName: string, min = 1, max?: number): this {
|
|
69
|
+
const val = getValue(this.entity);
|
|
70
|
+
if (!val || val.trim().length < min) {
|
|
71
|
+
this.errors.push(`${fieldName} jest wymagane (min. ${min} znakow)`);
|
|
72
|
+
} else if (max && val.trim().length > max) {
|
|
73
|
+
this.errors.push(`${fieldName} max ${max} znakow`);
|
|
74
|
+
}
|
|
75
|
+
return this;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
validatePositiveInteger(getValue: (e: T) => number, fieldName: string): this {
|
|
79
|
+
const val = getValue(this.entity);
|
|
80
|
+
if (!Number.isInteger(val) || val <= 0) {
|
|
81
|
+
this.errors.push(`${fieldName} musi byc liczba dodatnia`);
|
|
82
|
+
}
|
|
83
|
+
return this;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
validateEnum<E>(getValue: (e: T) => E, allowed: E[], fieldName: string): this {
|
|
87
|
+
if (!allowed.includes(getValue(this.entity))) {
|
|
88
|
+
this.errors.push(`${fieldName} ma niedozwolona wartosc`);
|
|
89
|
+
}
|
|
90
|
+
return this;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
validateCustom(condition: (e: T) => boolean, errorMessage: string): this {
|
|
94
|
+
if (!condition(this.entity)) {
|
|
95
|
+
this.errors.push(errorMessage);
|
|
96
|
+
}
|
|
97
|
+
return this;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
validateNested(result: IValidationResult): this {
|
|
101
|
+
this.errors.push(...result.errors);
|
|
102
|
+
return this;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
build(): IValidationResult {
|
|
106
|
+
return { isValid: this.errors.length === 0, errors: this.errors };
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
export function createValidator<T>(entity: T): ValidationBuilder<T> {
|
|
111
|
+
return new ValidationBuilder(entity);
|
|
112
|
+
}
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
### Uzycie
|
|
116
|
+
|
|
117
|
+
```typescript
|
|
118
|
+
const result = createValidator(user)
|
|
119
|
+
.validatePositiveInteger(u => u.id, 'ID')
|
|
120
|
+
.validateString(u => u.name, 'Imie', 2, 50)
|
|
121
|
+
.validateString(u => u.email, 'Email', 5, 255)
|
|
122
|
+
.validateCustom(u => isValidEmail(u.email), 'Nieprawidlowy format email')
|
|
123
|
+
.validateEnum(u => u.role, ['admin', 'user', 'guest'], 'Rola')
|
|
124
|
+
.build();
|
|
125
|
+
|
|
126
|
+
if (!result.isValid) {
|
|
127
|
+
// result.errors: ['Email: Nieprawidlowy format email', ...]
|
|
128
|
+
}
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
## Walidacja w komponencie Vue
|
|
132
|
+
|
|
133
|
+
```vue
|
|
134
|
+
<script setup lang="ts">
|
|
135
|
+
import { reactive, computed } from 'vue';
|
|
136
|
+
import { isNotEmpty, isValidEmail, isValidLength } from '@/utils/validation';
|
|
137
|
+
|
|
138
|
+
interface IFormData {
|
|
139
|
+
name: string;
|
|
140
|
+
email: string;
|
|
141
|
+
description: string;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
const form = reactive<IFormData>({
|
|
145
|
+
name: '',
|
|
146
|
+
email: '',
|
|
147
|
+
description: '',
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
const validationErrors = computed(() => {
|
|
151
|
+
const errors: Record<string, string> = {};
|
|
152
|
+
|
|
153
|
+
if (!isNotEmpty(form.name)) errors.name = 'Nazwa jest wymagana';
|
|
154
|
+
else if (!isValidLength(form.name, 2, 100)) errors.name = 'Nazwa: 2-100 znakow';
|
|
155
|
+
|
|
156
|
+
if (!isNotEmpty(form.email)) errors.email = 'Email jest wymagany';
|
|
157
|
+
else if (!isValidEmail(form.email)) errors.email = 'Nieprawidlowy email';
|
|
158
|
+
|
|
159
|
+
if (form.description && !isValidLength(form.description, 0, 1000)) {
|
|
160
|
+
errors.description = 'Opis: max 1000 znakow';
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
return errors;
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
const isFormValid = computed(() => Object.keys(validationErrors.value).length === 0);
|
|
167
|
+
</script>
|
|
168
|
+
|
|
169
|
+
<template>
|
|
170
|
+
<form @submit.prevent="onSubmit">
|
|
171
|
+
<div class="form-field" :class="{ 'has-error': validationErrors.name }">
|
|
172
|
+
<label>Nazwa</label>
|
|
173
|
+
<input v-model="form.name" data-testid="formNameInput" />
|
|
174
|
+
<span v-if="validationErrors.name" class="error">{{ validationErrors.name }}</span>
|
|
175
|
+
</div>
|
|
176
|
+
|
|
177
|
+
<div class="form-field" :class="{ 'has-error': validationErrors.email }">
|
|
178
|
+
<label>Email</label>
|
|
179
|
+
<input v-model="form.email" type="email" data-testid="formEmailInput" />
|
|
180
|
+
<span v-if="validationErrors.email" class="error">{{ validationErrors.email }}</span>
|
|
181
|
+
</div>
|
|
182
|
+
|
|
183
|
+
<button type="submit" :disabled="!isFormValid" data-testid="formSubmitButton">
|
|
184
|
+
Zapisz
|
|
185
|
+
</button>
|
|
186
|
+
</form>
|
|
187
|
+
</template>
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
## Obsluga bledow z API (422)
|
|
191
|
+
|
|
192
|
+
```typescript
|
|
193
|
+
const serverErrors = ref<Record<string, string[]>>({});
|
|
194
|
+
|
|
195
|
+
async function onSubmit() {
|
|
196
|
+
serverErrors.value = {};
|
|
197
|
+
try {
|
|
198
|
+
await FeatureService.create(form);
|
|
199
|
+
notify('success', 'Zapisano');
|
|
200
|
+
} catch (error) {
|
|
201
|
+
if (error instanceof AxiosError && error.response?.status === 422) {
|
|
202
|
+
serverErrors.value = error.response.data.errors;
|
|
203
|
+
} else {
|
|
204
|
+
printError(error);
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
## Composable do formularzy
|
|
211
|
+
|
|
212
|
+
Wydziel logike formularza do composable (patrz: `composables.instructions.md`):
|
|
213
|
+
|
|
214
|
+
```typescript
|
|
215
|
+
const { form, errors, submitting, submitForm, resetForm } = useFeatureForm(itemId);
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
## Zasady
|
|
219
|
+
|
|
220
|
+
1. **Walidacja client-side** - computed `validationErrors`, nie metody
|
|
221
|
+
2. **Walidacja server-side** - obsluz 422 i pokaz bledy przy polach
|
|
222
|
+
3. **Funkcje walidacji** - reuzywalne, w `utils/validation.ts`
|
|
223
|
+
4. **ValidationBuilder** - dla zlozonych obiektow (nie dla formularzy UI)
|
|
224
|
+
5. **`data-testid`** - na kazdym input i button formularza
|
|
225
|
+
6. **Disable submit** - gdy `!isFormValid` lub `submitting`
|
|
226
|
+
7. **Reset** - czysc formularz po sukcesie lub przy anulowaniu
|
|
227
|
+
8. **Nie waliduj pustych opcjonalnych pol** - sprawdzaj tylko jesli wartosc istnieje
|