feat: add guidelines for prompt-writer and run-prompt skills
This commit is contained in:
@@ -0,0 +1,118 @@
|
|||||||
|
# راهنمای مشترک `prompt-writer` و `run-prompt`
|
||||||
|
|
||||||
|
این فایل مرجع کیفیت هر دو اسکیل است. هر دو اسکیل باید در ابتدای بدنهٔ خود این خط را داشته باشند:
|
||||||
|
|
||||||
|
> قبل از شروع، فایل `.claude/guidelines.md` را بخوان و همهٔ بخشهای آن را اعمال کن.
|
||||||
|
|
||||||
|
قواعد stack و مسیر فایلها در خود اسکیلها میماند؛ این فایل فقط **چگونه کار کردن** را تعیین میکند. اگر بین این فایل و متن پرامپت تناقضی بود، همین فایل برنده است — مگر اینکه کاربر صریحاً خلافش را بگوید.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## ۱. درک مسئله — قبل از هر خط کد
|
||||||
|
|
||||||
|
هیچ چیز گرانتر از پیادهسازیِ درستِ مسئلهٔ اشتباه نیست. به همین دلیل:
|
||||||
|
|
||||||
|
- **پرامپت/درخواست را کامل بخوان**، بعد کد واقعیِ مرتبط را بخوان. هرگز از حافظه دربارهٔ کد پروژه فرض نکن — فایل را باز کن.
|
||||||
|
- مسئله را **با کلمات خودت بازگو کن**: «مشکل این است که X؛ رفتار درست Y است؛ محدودهٔ تغییر فایلهای Z». اگر این سه جمله را نمیتوانی بنویسی، هنوز مسئله را نفهمیدهای.
|
||||||
|
- برای هر قابلیت **معیار پذیرش (acceptance criteria)** تعریف کن: چه چیزی باید درست کار کند تا «تمام» حساب شود؟ شامل حالت موفق، حالت خطا، و حداقل یک حالت مرزی.
|
||||||
|
- **ابهام = توقف.** سوال را همان اول بپرس، نه وسط پیادهسازی. حدس زدن وسط کار یعنی دوبارهکاری.
|
||||||
|
- در `prompt-writer`: قالب پرامپت باید بخش **«معیار پذیرش»** داشته باشد (بعد از «مشکل / هدف»). پرامپتی که معیار پذیرش ندارد ناقص است.
|
||||||
|
- در `run-prompt`: مرحلهٔ «تحلیل» با بازگویی مسئله و معیار پذیرش به کاربر تمام میشود، نه فقط با لیست قابلیتها.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## ۲. Todo — ستون فقرات اجرا
|
||||||
|
|
||||||
|
Todo فقط نمایش پیشرفت نیست؛ قرارداد بین تو و کاربر است که هیچ مرحلهای بیصدا رد نشود.
|
||||||
|
|
||||||
|
- در ابتدای اجرا با `TodoWrite` لیست کامل بساز — **یک آیتم به ازای هر قابلیت**، همه `pending`.
|
||||||
|
- هر آیتم باید **قابل تست** باشد: خروجی مشخص داشته باشد («endpoint X با response Y کار میکند»)، نه فعل مبهم («بررسی سیستم نوبت»).
|
||||||
|
- در هر لحظه فقط **یک** آیتم `in_progress`.
|
||||||
|
- آیتم فقط وقتی `completed` میشود که **هر سهٔ** اینها انجام شده باشد: پیادهسازی + تست سبز + مستندات بهروز. کد بدون تست یا بدون سند، «تمام» نیست.
|
||||||
|
- اگر وسط کار قابلیت جدیدی کشف شد (باگ جانبی، وابستگی پنهان)، بهجای انجام بیصدا، آیتم جدید به todo اضافه کن و به کاربر بگو.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## ۳. تست — تعریف «تمام شد»
|
||||||
|
|
||||||
|
- **Definition of Done** برای هر قابلیت: syntax/lint/build سبز **و** پوشش سه سناریوی معیار پذیرش (موفق، خطا، مرزی). این همان قاعدهٔ پروژه است: هیچ تسکی بدون تست تمام نیست.
|
||||||
|
- خطا یعنی توقف کامل. هیچ قابلیتی روی خرابهٔ قابلیت قبلی ساخته نمیشود.
|
||||||
|
- دستورهای تست هر پروژه در خود اسکیل `run-prompt` آمده؛ آنها حداقلاند نه سقف. اگر قابلیت منطق دارد (نه فقط UI)، تست واقعی همان منطق را اجرا کن — مثلاً صدا زدن endpoint با داده واقعی، نه فقط `php -l`.
|
||||||
|
- **تغییر cross-repo تست خودکار ندارد.** تغییر قرارداد API در build کلاینتها خطا نمیدهد و در رانتایم میشکند؛ پس بعد از هر تغییر قرارداد، مصرف واقعی را در هر کلاینت متأثر دستی دنبال کن و نتیجه را گزارش بده.
|
||||||
|
- در `prompt-writer`: در بخش «وظایف» یا «نکات مهم» هر پرامپت بنویس این قابلیت **چطور تست میشود** — تا `run-prompt` مجبور نباشد خودش تست را اختراع کند.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## ۴. مستندسازی — بخشی از کار، نه بعد از کار
|
||||||
|
|
||||||
|
- سند در **همان جلسهای** بهروز میشود که کد تغییر کرده. «بعداً مینویسم» یعنی هرگز.
|
||||||
|
- مپینگ `src/*` → `docs/api/*.md` در اسکیل `run-prompt` است؛ الزام محتوایی: endpoint با method/path/permission، بدنهٔ request با نوع همهٔ فیلدها، response با **JSON واقعی** (خروجی اجرای واقعی، نه دستساز)، همهٔ status/error codeها.
|
||||||
|
- سند باید با کد **همخوان** باشد: اگر سند موجود با رفتار فعلی فرق دارد، اول سند را اصلاح کن، بعد قابلیت جدید را اضافه کن.
|
||||||
|
- خود فایل پرامپت هم سند است: در `prompt-writer` بخش «وضعیت فعلی» باید **کپی از کد واقعی** باشد؛ کد بازنویسیشده از حافظه ممنوع.
|
||||||
|
- کامنت بدیهی ننویس؛ مستندات کوتاه و دقیق فقط روی چیزهای عمومی (public API، قرارداد، تصمیم معماری).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## ۵. کدنویسی — SOLID و الگوها در بستر این پروژه
|
||||||
|
|
||||||
|
الگو ابزار حل مسئله است، نه مدال. اول مسئله، بعد الگو.
|
||||||
|
|
||||||
|
### SOLID — یعنی در این پروژه:
|
||||||
|
|
||||||
|
- **S (تکمسئولیتی):** Controller نازک — فقط دریافت request، صدا زدن Service، برگرداندن response با `$this->success()/paginated()/error()`. منطق دامنه در Service؛ کوئری در Repository. در فرانت: کامپوننتی که هم fetch میکند، هم state پیچیده دارد، هم render سنگین — بشکن.
|
||||||
|
- **O (باز/بسته):** رفتار جدید = کلاس/استراتژی جدید، نه `if/switch` جدید روی نوع. نمونههای واقعی این workspace: درگاه پرداخت، ارسالکنندهٔ SMS، نوع نوبت — برای اینها interface + پیادهسازی جدا، نه شاخهشاخه کردن یک Service.
|
||||||
|
- **L (جایگزینی):** پیادهسازی جدیدِ یک interface باید همان قرارداد را کامل رعایت کند — همان شکل response، همان exceptionها. کلاسی که «تقریباً» قرارداد را رعایت میکند، cross-repo bug تولید میکند.
|
||||||
|
- **I (تفکیک interface):** interfaceهای کوچک و متمرکز. اگر پیادهسازیای مجبور است متدی را خالی بگذارد، interface بزرگ است.
|
||||||
|
- **D (وارونگی وابستگی):** Service به interface وابسته باشد، نه کلاس concrete؛ تزریق با DI خود Symfony (constructor injection + autowiring). `new` کردن سرویس داخل سرویس ممنوع.
|
||||||
|
|
||||||
|
### الگوهای پذیرفتهشدهٔ این workspace:
|
||||||
|
|
||||||
|
| الگو | کجا | چرا |
|
||||||
|
|------|-----|-----|
|
||||||
|
| Repository | همهٔ دسترسی داده (Doctrine و لایهٔ repo در tauri) | جداسازی کوئری از منطق |
|
||||||
|
| Service Layer | منطق دامنه در `src/*/Service` | Controller نازک، منطق قابل تست |
|
||||||
|
| Strategy | پرداخت، SMS، هر جای «چند روش برای یک کار» | افزودن روش جدید بدون دست زدن به موجود |
|
||||||
|
| Factory | ساخت objectهای پیچیده/وابسته به نوع | تمرکز منطق ساخت |
|
||||||
|
| Event/Observer | Symfony EventDispatcher برای اثرات جانبی (نوتیف، لاگ) | جدا کردن اثر جانبی از جریان اصلی |
|
||||||
|
| Adapter | کلاینتهای API (`api.ts`، `services/response.js`) | یک نقطهٔ تماس با backend |
|
||||||
|
|
||||||
|
### مرز over-engineering:
|
||||||
|
|
||||||
|
- الگو فقط وقتی که **الان** دو پیادهسازی یا یک نیاز مشخص برای تعویض وجود دارد. برای «شاید در آینده» abstraction نساز.
|
||||||
|
- error handling فقط در boundaryهای واقعی (Controller، فراخوانی API، I/O) — نه try/catch دفاعی دور هر متد.
|
||||||
|
- قبل از ساختن هر چیز جدید: اول بگرد (endpoint، کامپوننت، سرویس موجود)، بعد توسعه بده، در آخر بساز — و دلیلش را بنویس.
|
||||||
|
- در `prompt-writer`: اگر راهحل پیشنهادی الگویی را به کار میبرد، در بخش «نکات مهم» نام الگو و **دلیل انتخابش** را بنویس تا `run-prompt` همان مسیر را برود.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## ۶. رفتار تحلیلگر — نقش agent در هر دو اسکیل
|
||||||
|
|
||||||
|
نقش agent یک تحلیلگر منطقی و منتقد است، نه دستیاری که هدفش راضی کردن کاربر باشد. هدف، پیدا کردن بهترین پاسخ است، نه موافقت.
|
||||||
|
|
||||||
|
- اگر تصمیم کاربر یا راهحل خواستهشده در پرامپت اشتباه، ناقص یا پرریسک است، **قبل از پیادهسازی** صریح و با دلیل بگو و منتظر تعیین تکلیف بمان. اجرای بیچونوچرای یک پرامپت غلط، خطای agent است نه کاربر.
|
||||||
|
- تأیید بدون تحلیل ممنوع — «ایدهٔ خوبی است» و «کاملاً درست است» بدون استدلال ننویس. هر نتیجهگیری بر پایهٔ منطق، شواهد، کد واقعی پروژه یا مستندات رسمی باشد.
|
||||||
|
- بین **واقعیت** (کد/سند موجود)، **فرضیه**، **نظر** و **حدس** تفاوت بگذار و در گزارشها برچسبش را مشخص کن. اگر داده کافی نیست، همین را بگو؛ حدس را قطعی جا نزن.
|
||||||
|
- برای هر پیشنهاد طراحی: مزایا، معایب، ریسکها و حداقل یک گزینهٔ جایگزین. اگر چند راهحل وجود دارد، مقایسه کن و دلیل انتخاب را بنویس. (جای طبیعی این کار مرحلهٔ «طراحی» در `run-prompt` و بخش «وظایف» در `prompt-writer` است.)
|
||||||
|
- در تصمیمهای مهم و موضوعات پیچیده، اول قدمبهقدم تحلیل کن و **ساختار/طرح پیشنهادی را قبل از خروجی نهایی ارائه بده و تأیید بگیر** — نه اینکه مستقیم سراغ کد یا متن نهایی بروی.
|
||||||
|
- مرجع ادعاهای فنی: مستندات رسمی فریمورکها و کد خود پروژه، نه حافظه.
|
||||||
|
|
||||||
|
و در تعامل:
|
||||||
|
|
||||||
|
- سبک کد جدید = سبک فایلهای مشابه موجود در همان پروژه؛ کد واقعی پروژه، نمونهٔ سبک است.
|
||||||
|
- وقتی کاربر بازخورد میدهد، **همان خروجی موجود را اصلاح کن**؛ بازتولید از صفر فقط اگر خودش بخواهد.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## ۷. چکلیست پایان هر قابلیت (برای `run-prompt`)
|
||||||
|
|
||||||
|
قبل از `completed` کردن آیتم todo، همهٔ اینها باید تیک بخورد:
|
||||||
|
|
||||||
|
- [ ] مسئله و معیار پذیرش در ابتدای کار بازگو و تأیید شده بود
|
||||||
|
- [ ] اگر راهحل پرامپت ایراد یا ریسک داشت، قبل از اجرا اعلام و تعیین تکلیف شده بود
|
||||||
|
- [ ] Controller نازک است؛ منطق در Service/Repository است
|
||||||
|
- [ ] رفتار جدید با extension اضافه شده، نه با if روی نوع
|
||||||
|
- [ ] وابستگیها تزریق شدهاند، نه `new` شده
|
||||||
|
- [ ] تستها سبزند: موفق + خطا + مرزی
|
||||||
|
- [ ] اگر قرارداد API تغییر کرده: کلاینتهای متأثر دستی بررسی و گزارش شدهاند
|
||||||
|
- [ ] `docs/api/` (یا سند مرتبط) در همین جلسه بهروز شده و با رفتار واقعی همخوان است
|
||||||
|
- [ ] هیچ کامنت بدیهی و هیچ abstraction بدون مصرف اضافه نشده
|
||||||
@@ -3,9 +3,11 @@ name: prompt-writer
|
|||||||
description: تولید فایل پرامپت .md برای یک قابلیت یا باگفیکس در پروژه ClinicPro. استفاده کن وقتی کاربر میگوید «یک پرامپت بنویس»، «پرامپت بساز»، «write a prompt»، «برام پرامپت بنویس برای X». این skill پروژه را تحلیل میکند، سپس یک فایل .md کامل در .claude/prompt/ میسازد و دستور اجرا را نمایش میدهد.
|
description: تولید فایل پرامپت .md برای یک قابلیت یا باگفیکس در پروژه ClinicPro. استفاده کن وقتی کاربر میگوید «یک پرامپت بنویس»، «پرامپت بساز»، «write a prompt»، «برام پرامپت بنویس برای X». این skill پروژه را تحلیل میکند، سپس یک فایل .md کامل در .claude/prompt/ میسازد و دستور اجرا را نمایش میدهد.
|
||||||
---
|
---
|
||||||
|
|
||||||
|
> **قبل از شروع، فایل `.claude/guidelines.md` را بخوان و همهٔ بخشهای آن را اعمال کن** (اگر روت جلسه workspace است: `clinicpro/.claude/guidelines.md`). آن فایل «چگونه کار کردن» (درک مسئله، معیار پذیرش، تست، مستندات، SOLID، رفتار تحلیلگر) را تعیین میکند؛ این skill فقط قواعد stack و مسیرها و قالب خروجی را دارد. در تناقض، guidelines برنده است.
|
||||||
|
|
||||||
## نحوه دریافت ورودی
|
## نحوه دریافت ورودی
|
||||||
|
|
||||||
کاربر موضوع یا مشکل را توضیح میدهد. اگر توضیح کافی نبود، یک سوال کوتاه بپرس.
|
کاربر موضوع یا مشکل را توضیح میدهد. **ابهام = توقف**: اگر توضیح کافی نبود، همان اول یک سوال کوتاه بپرس و ادامه نده — نه وسط تحلیل.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -20,8 +22,13 @@ description: تولید فایل پرامپت .md برای یک قابلیت ی
|
|||||||
- Backend: `src/` — Controllers، Services، Entities، Repositories
|
- Backend: `src/` — Controllers، Services، Entities، Repositories
|
||||||
- Frontend: `assets/admin/pages/`، `assets/admin/components/`
|
- Frontend: `assets/admin/pages/`، `assets/admin/components/`
|
||||||
- مستندات: `docs/api/`
|
- مستندات: `docs/api/`
|
||||||
3. **APIهای موجود را بررسی کن**: `ddev exec php bin/console debug:router | grep api`
|
3. **APIهای موجود را بررسی کن**: `ddev exec php bin/console debug:router | grep api` — API جدید فقط وقتی هیچ اندپوینت موجودی، حتی با توسعه، کافی نباشد؛ دلیلش را در پرامپت بنویس
|
||||||
4. **کد مرتبط را بخوان**: فقط فایلهایی که مستقیم به موضوع مربوطند
|
4. **کد مرتبط را بخوان**: فقط فایلهایی که مستقیم به موضوع مربوطند — کد واقعی، نه حافظه
|
||||||
|
|
||||||
|
بعد از خواندن، **مسئله را با کلمات خودت بازگو کن** و به کاربر نشان بده:
|
||||||
|
«مشکل این است که X؛ رفتار درست Y است؛ محدودهٔ تغییر فایلهای Z». اگر این سه جمله را نمیتوانی بنویسی، هنوز مسئله را نفهمیدهای — سوال بپرس.
|
||||||
|
|
||||||
|
**نقش تحلیلگر (guidelines §۶):** اگر راهحلی که کاربر خواسته اشتباه، ناقص یا پرریسک است، همینجا صریح و با دلیل بگو و منتظر تعیین تکلیف بمان — پرامپتِ یک راهحل غلط را ننویس. اگر چند راهحل جدی وجود دارد، کوتاه مقایسه کن (مزایا/معایب/ریسک) و دلیل انتخاب را در پرامپت ثبت کن.
|
||||||
|
|
||||||
### مرحله ۲ — نامگذاری فایل
|
### مرحله ۲ — نامگذاری فایل
|
||||||
|
|
||||||
@@ -50,6 +57,14 @@ description: تولید فایل پرامپت .md برای یک قابلیت ی
|
|||||||
|
|
||||||
<توضیح دقیق مشکل یا قابلیت مورد نیاز>
|
<توضیح دقیق مشکل یا قابلیت مورد نیاز>
|
||||||
|
|
||||||
|
## معیار پذیرش
|
||||||
|
|
||||||
|
<چه چیزی باید درست کار کند تا «تمام» حساب شود — قابل تست، نه کلی>
|
||||||
|
|
||||||
|
- ✅ موفق: <رفتار درست در مسیر عادی — مثلاً «POST /api/v1/... با توکن ادمین → 200 و رکورد ذخیره میشود»>
|
||||||
|
- ❌ خطا: <رفتار درست در مسیر خطا — مثلاً «بدون permission → 403 با envelope خطا و کد از ErrorCodes»>
|
||||||
|
- ⚠️ مرزی: <حداقل یک حالت مرزی — لیست خالی، صفحه آخر pagination، تاریخ مرزی شمسی، …>
|
||||||
|
|
||||||
## فایلهای مرتبط
|
## فایلهای مرتبط
|
||||||
|
|
||||||
| فایل | نقش |
|
| فایل | نقش |
|
||||||
@@ -59,7 +74,7 @@ description: تولید فایل پرامپت .md برای یک قابلیت ی
|
|||||||
|
|
||||||
## وضعیت فعلی
|
## وضعیت فعلی
|
||||||
|
|
||||||
<کد یا رفتار فعلی — فقط بخش مرتبط>
|
<کد یا رفتار فعلی — کپی از کد واقعی پروژه، نه بازنویسی از حافظه؛ فقط بخش مرتبط>
|
||||||
|
|
||||||
```php / tsx
|
```php / tsx
|
||||||
// کد فعلی مشکلدار یا ناقص
|
// کد فعلی مشکلدار یا ناقص
|
||||||
@@ -75,6 +90,8 @@ description: تولید فایل پرامپت .md برای یک قابلیت ی
|
|||||||
// نمونه کد یا pseudocode
|
// نمونه کد یا pseudocode
|
||||||
```
|
```
|
||||||
|
|
||||||
|
**نحوه تست:** <دقیقاً چطور این وظیفه تست میشود — دستور، curl با داده واقعی، سناریوی UI — تا run-prompt مجبور نباشد تست را اختراع کند>
|
||||||
|
|
||||||
### ۲. <دومین وظیفه>
|
### ۲. <دومین وظیفه>
|
||||||
|
|
||||||
...
|
...
|
||||||
@@ -83,7 +100,7 @@ description: تولید فایل پرامپت .md برای یک قابلیت ی
|
|||||||
|
|
||||||
- <نکته معماری یا محدودیت مهم>
|
- <نکته معماری یا محدودیت مهم>
|
||||||
- <edge case که باید پوشش داده شود>
|
- <edge case که باید پوشش داده شود>
|
||||||
- <الگوی پروژه که باید رعایت شود>
|
- <الگوی پروژه که باید رعایت شود؛ اگر راهحل از الگویی (Strategy، Factory، Event، …) استفاده میکند: نام الگو + دلیل انتخابش>
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
@@ -92,24 +109,29 @@ description: تولید فایل پرامپت .md برای یک قابلیت ی
|
|||||||
|
|
||||||
**باید داشته باشد:**
|
**باید داشته باشد:**
|
||||||
- مسیر دقیق فایلهایی که باید تغییر کنند
|
- مسیر دقیق فایلهایی که باید تغییر کنند
|
||||||
- وضعیت فعلی کد (کپی از کد واقعی، نه توصیف کلی)
|
- بخش «معیار پذیرش» با هر سه سناریو (موفق/خطا/مرزی) — **پرامپت بدون معیار پذیرش ناقص است**
|
||||||
- راهحل پیشنهادی با نمونه کد
|
- وضعیت فعلی کد (کپی از کد واقعی، نه توصیف کلی) — خود فایل پرامپت سند است
|
||||||
- edge caseها
|
- راهحل پیشنهادی با نمونه کد؛ اگر الگو دارد، نام + دلیل
|
||||||
- الگوهای پروژه که باید رعایت شوند (BaseController، TanStack Query، Zod، ...)
|
- «نحوه تست» برای هر وظیفهای که منطق دارد
|
||||||
|
- edge caseها و الگوهای پروژه که باید رعایت شوند (BaseController، TanStack Query، Zod، ...)
|
||||||
|
|
||||||
**نباید داشته باشد:**
|
**نباید داشته باشد:**
|
||||||
- توضیحات کلی و بدیهی
|
- توضیحات کلی و بدیهی
|
||||||
- کدی که از پروژه واقعی کپی نشده
|
- کدی که از پروژه واقعی کپی نشده
|
||||||
- وظایف نامشخص مثل «بررسی کن» بدون مشخص کردن چه چیزی
|
- وظایف نامشخص مثل «بررسی کن» بدون مشخص کردن چه چیزی
|
||||||
|
- abstraction «برای آینده» — الگو فقط وقتی الان نیازش هست (guidelines §۵)
|
||||||
|
|
||||||
**قوانین خاص پروژه که باید در پرامپت منعکس شود:**
|
**قوانین خاص پروژه که باید در پرامپت منعکس شود:**
|
||||||
- Backend: همه controllerها از `BaseController` ارث میبرند؛ پاسخها با `$this->success()` / `$this->paginated()` / `$this->error()`
|
- Backend: همه controllerها از `BaseController` ارث میبرند؛ پاسخها با `$this->success()` / `$this->paginated()` / `$this->error()`
|
||||||
|
- Controller نازک: منطق در Service، کوئری در Repository؛ وابستگیها با constructor injection (نه `new`)
|
||||||
|
- خطاها با `AppException(ErrorCodes::ERR_XXX)` و پیام فارسی در `src/Shared/Constant/ErrorCodes.php`
|
||||||
- Frontend paginated: items از `data?.data`، total از `data?.meta?.totalRecords`
|
- Frontend paginated: items از `data?.data`، total از `data?.meta?.totalRecords`
|
||||||
- Frontend single: از `data?.data` (ممکن است double-nested باشد)
|
- Frontend single: از `data?.data` (ممکن است double-nested باشد)
|
||||||
- Category API: triple-nested → `data?.data?.data ?? []`
|
- Category API: triple-nested → `data?.data?.data ?? []`
|
||||||
- تاریخها: Unix timestamp صحیح؛ نمایش با `formatDate()` شمسی
|
- تاریخها: Unix timestamp صحیح؛ نمایش با `formatDate()` شمسی
|
||||||
- اگر Entity تغییر کرد: migration لازم است
|
- اگر Entity تغییر کرد: migration لازم است
|
||||||
- مستندات: بعد از هر تغییر API، فایل مربوطه در `docs/api/` باید بهروز شود
|
- مستندات: بعد از هر تغییر API، فایل مربوطه در `docs/api/` باید بهروز شود
|
||||||
|
- اگر endpoint توسط سایت عمومی (`nobat724_front`) هم مصرف میشود، در پرامپت ذکر کن — تغییر قرارداد در build کلاینت خطا نمیدهد
|
||||||
|
|
||||||
### مرحله ۴ — خروجی نهایی
|
### مرحله ۴ — خروجی نهایی
|
||||||
|
|
||||||
@@ -135,7 +157,10 @@ description: تولید فایل پرامپت .md برای یک قابلیت ی
|
|||||||
- متد accept() فقط status تغییر میدهد، clinic->getDoctors()->add() صدا نمیزند
|
- متد accept() فقط status تغییر میدهد، clinic->getDoctors()->add() صدا نمیزند
|
||||||
- Clinic entity دارای ManyToMany $doctors است
|
- Clinic entity دارای ManyToMany $doctors است
|
||||||
|
|
||||||
|
→ بازگویی: مشکل — accept() رابطه clinic_doctors را نمیسازد؛ رفتار درست — پزشک بعد از accept در لیست پزشکان کلینیک باشد؛ محدوده — ClinicInvitationService.php
|
||||||
|
|
||||||
→ فایل .claude/prompt/fix-invitation-accept.md ساخته شد
|
→ فایل .claude/prompt/fix-invitation-accept.md ساخته شد
|
||||||
|
(شامل معیار پذیرش: ✅ accept → پزشک در لیست / ❌ دعوت منقضی → خطا / ⚠️ accept تکراری → بدون رکورد تکراری)
|
||||||
|
|
||||||
توضیح: رفع باگ پذیرش دعوت — پزشک باید پس از accept به clinic_doctors اضافه شود.
|
توضیح: رفع باگ پذیرش دعوت — پزشک باید پس از accept به clinic_doctors اضافه شود.
|
||||||
فایل پرامپت: .claude/prompt/fix-invitation-accept.md
|
فایل پرامپت: .claude/prompt/fix-invitation-accept.md
|
||||||
|
|||||||
@@ -3,6 +3,8 @@ name: run-prompt
|
|||||||
description: اجرای یک فایل پرامپت .md به صورت گامبهگام و ایمن. هر قابلیت را جداگانه پیادهسازی، تست و مستند میکند. استفاده کن وقتی کاربر میگوید "اجرای پرامپت"، "پرامپت را اجرا کن"، "run prompt"، یا مسیر یک فایل .md میدهد.
|
description: اجرای یک فایل پرامپت .md به صورت گامبهگام و ایمن. هر قابلیت را جداگانه پیادهسازی، تست و مستند میکند. استفاده کن وقتی کاربر میگوید "اجرای پرامپت"، "پرامپت را اجرا کن"، "run prompt"، یا مسیر یک فایل .md میدهد.
|
||||||
---
|
---
|
||||||
|
|
||||||
|
> **قبل از شروع، فایل `.claude/guidelines.md` را بخوان و همهٔ بخشهای آن را اعمال کن** (اگر روت جلسه workspace است: `clinicpro/.claude/guidelines.md`). آن فایل «چگونه کار کردن» (درک مسئله، todo، تعریف «تمام شد»، مستندات، SOLID، رفتار تحلیلگر، چکلیست پایانی §۷) را تعیین میکند؛ این skill فقط قواعد stack، دستورهای تست و مسیرها را دارد. در تناقض با متن پرامپت، guidelines برنده است — مگر کاربر صریحاً خلافش را بگوید.
|
||||||
|
|
||||||
## نحوه دریافت ورودی
|
## نحوه دریافت ورودی
|
||||||
|
|
||||||
اگر کاربر مسیر فایل داد → آن را بخوان.
|
اگر کاربر مسیر فایل داد → آن را بخوان.
|
||||||
@@ -12,18 +14,20 @@ description: اجرای یک فایل پرامپت .md به صورت گامب
|
|||||||
|
|
||||||
## قبل از شروع — تحلیل پرامپت و ساخت Todo
|
## قبل از شروع — تحلیل پرامپت و ساخت Todo
|
||||||
|
|
||||||
۱. فایل پرامپت را کامل بخوان
|
۱. فایل پرامپت را **کامل** بخوان
|
||||||
۲. مستندات موجود را بررسی کن: `docs/api/`
|
۲. مستندات موجود را بررسی کن: `docs/api/`
|
||||||
۳. کد مرتبط در `src/` و `assets/admin/` را مرور کن
|
۳. کد واقعیِ مرتبط در `src/` و `assets/admin/` را بخوان — هیچ فرضی از حافظه
|
||||||
۴. لیست قابلیتها را از پرامپت استخراج کن
|
۴. **نقد پرامپت (guidelines §۶):** اگر راهحل پرامپت اشتباه، ناقص یا پرریسک است، همینجا صریح و با دلیل بگو و منتظر تعیین تکلیف بمان. اجرای بیچونوچرای پرامپت غلط، خطای agent است.
|
||||||
۵. **با `TodoWrite` یک todo کامل بساز** — یک آیتم به ازای هر قابلیت
|
۵. لیست قابلیتها را استخراج کن؛ اگر پرامپت «معیار پذیرش» ندارد، خودت برای هر قابلیت بساز (موفق + خطا + مرزی) و به کاربر نشان بده
|
||||||
۶. به کاربر نمایش بده:
|
۶. **با `TodoWrite` todo کامل بساز** — یک آیتم قابلتست به ازای هر قابلیت
|
||||||
|
۷. به کاربر نمایش بده — تحلیل با **بازگویی مسئله و معیار پذیرش** تمام میشود، نه فقط لیست قابلیتها:
|
||||||
|
|
||||||
```
|
```
|
||||||
📋 قابلیتهای شناساییشده:
|
🧭 بازگویی مسئله: مشکل X است؛ رفتار درست Y؛ محدودهٔ تغییر فایلهای Z.
|
||||||
۱. [نام قابلیت اول]
|
📋 قابلیتها + معیار پذیرش:
|
||||||
۲. [نام قابلیت دوم]
|
۱. [نام] — ✅ [موفق] / ❌ [خطا] / ⚠️ [مرزی]
|
||||||
...
|
۲. ...
|
||||||
|
⚠️ ایرادهای پرامپت (اگر هست): ...
|
||||||
|
|
||||||
🚀 شروع با قابلیت ۱ — آیا ادامه دهم؟
|
🚀 شروع با قابلیت ۱ — آیا ادامه دهم؟
|
||||||
```
|
```
|
||||||
@@ -32,48 +36,40 @@ description: اجرای یک فایل پرامپت .md به صورت گامب
|
|||||||
|
|
||||||
## قوانین اجرا (اجباری — هیچ استثنایی ندارد)
|
## قوانین اجرا (اجباری — هیچ استثنایی ندارد)
|
||||||
|
|
||||||
### ۱. Todo — ستون فقرات اجرا
|
### ۱. Todo — ستون فقرات اجرا (guidelines §۲)
|
||||||
|
|
||||||
**در ابتدای هر اجرا** یک todo کامل با `TodoWrite` بساز:
|
**در ابتدای هر اجرا** یک todo کامل با `TodoWrite` بساز:
|
||||||
- یک آیتم به ازای هر قابلیت با وضعیت `pending`
|
- یک آیتم به ازای هر قابلیت با وضعیت `pending`
|
||||||
|
- هر آیتم **قابل تست** با خروجی مشخص («endpoint X با response Y کار میکند»)، نه فعل مبهم («بررسی سیستم نوبت»)
|
||||||
- آیتمهای زیر-مجموعه اضافه کن اگر قابلیت چند بخش مستقل دارد
|
- آیتمهای زیر-مجموعه اضافه کن اگر قابلیت چند بخش مستقل دارد
|
||||||
|
|
||||||
**در طول اجرا:**
|
**در طول اجرا:**
|
||||||
- هر قابلیت را که شروع میکنی → وضعیتش را `in_progress` کن
|
- هر قابلیت را که شروع میکنی → `in_progress`؛ در هر لحظه فقط یک آیتم `in_progress`
|
||||||
- هر قابلیت را که تست و تأیید شد → فوری `completed` کن
|
- آیتم فقط وقتی `completed` میشود که هر سه انجام شده باشد: **پیادهسازی + تست سبز + مستندات بهروز**
|
||||||
- هرگز دو آیتم را همزمان `in_progress` نگذار
|
|
||||||
- اگر خطا داری که قابلیت را block کرده → یک آیتم `in_progress` باقی بماند تا رفع شود
|
- اگر خطا داری که قابلیت را block کرده → یک آیتم `in_progress` باقی بماند تا رفع شود
|
||||||
|
- کشف کار جدید وسط اجرا (باگ جانبی، وابستگی پنهان) → آیتم جدید به todo + اطلاع به کاربر؛ انجام بیصدا ممنوع
|
||||||
**نمونه todo اولیه:**
|
|
||||||
```
|
|
||||||
[ ] قابلیت ۱: Stats Bar backend endpoint
|
|
||||||
[ ] قابلیت ۲: Stats Bar frontend component
|
|
||||||
[ ] قابلیت ۳: PersianCalendar popup
|
|
||||||
[ ] قابلیت ۴: AppointmentStatusDropdown
|
|
||||||
[ ] قابلیت ۵: بازطراحی AppointmentsPage
|
|
||||||
```
|
|
||||||
|
|
||||||
### ۲. یک قابلیت در هر مرحله
|
### ۲. یک قابلیت در هر مرحله
|
||||||
- هرگز دو قابلیت را همزمان پیادهسازی نکن
|
- هرگز دو قابلیت را همزمان پیادهسازی نکن
|
||||||
- هرگز بدون تأیید موفقیت مرحله قبل به مرحله بعد نرو
|
- هرگز بدون تأیید موفقیت مرحله قبل به مرحله بعد نرو — خطا یعنی توقف کامل
|
||||||
|
|
||||||
### ۳. ترتیب اجرای هر قابلیت
|
### ۳. ترتیب اجرای هر قابلیت
|
||||||
|
|
||||||
```
|
```
|
||||||
① تحلیل ② طراحی ③ پیادهسازی ④ تست ⑤ رفع خطا ⑥ مستندسازی ⑦ Todo + گزارش
|
① تحلیل ② طراحی ③ پیادهسازی ④ تست ⑤ رفع خطا ⑥ مستندسازی ⑦ چکلیست + Todo + گزارش
|
||||||
```
|
```
|
||||||
|
|
||||||
### ۴. سازگاری با پروژه
|
### ۴. سازگاری با پروژه
|
||||||
- همه تغییرات باید با Symfony 7 + React 19 سازگار باشند
|
- همه تغییرات باید با Symfony 7 + React 19 سازگار باشند
|
||||||
- از الگوهای موجود پروژه پیروی کن (BaseController، TanStack Query، Zod، ...)
|
- از الگوهای موجود پروژه پیروی کن (BaseController، TanStack Query، Zod، ...)
|
||||||
- اگر Entity تغییر کرد: `doctrine:migrations:diff` و `doctrine:migrations:migrate` اجرا کن
|
- اگر Entity تغییر کرد: `doctrine:migrations:diff` و `doctrine:migrations:migrate` اجرا کن
|
||||||
- اگر API تغییر کرد: همه بخشهای وابسته (frontend types، api calls، ...) را اصلاح کن
|
- اگر قرارداد API تغییر کرد: همه کلاینتهای وابسته را اصلاح کن — admin frontend (types، api calls) **و** اگر endpoint عمومی است، مصرف آن در `nobat724_front/services/` را دستی دنبال کن؛ این تغییرات در build خطا نمیدهند و در رانتایم میشکنند. نتیجه بررسی را گزارش بده.
|
||||||
|
|
||||||
### ۵. اصول کدنویسی
|
### ۵. اصول کدنویسی (تفصیل در guidelines §۵)
|
||||||
- SOLID و Clean Code
|
- SOLID: Controller نازک، منطق در Service، کوئری در Repository؛ رفتار جدید با extension نه if روی نوع؛ وابستگی با constructor injection نه `new`
|
||||||
- هیچ کامنت غیرضروری اضافه نکن
|
- سبک کد جدید = سبک فایلهای مشابه موجود در پروژه
|
||||||
- نامگذاری معنادار
|
- هیچ کامنت بدیهی؛ نامگذاری معنادار؛ هیچ abstraction بدون مصرفِ الان
|
||||||
- error handling فقط در boundaries واقعی (نه defensive programming اضافی)
|
- error handling فقط در boundaries واقعی (Controller، فراخوانی API، I/O)
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -83,9 +79,9 @@ description: اجرای یک فایل پرامپت .md به صورت گامب
|
|||||||
|
|
||||||
قبل از هر چیز:
|
قبل از هر چیز:
|
||||||
- فایلهای مرتبط را بخوان
|
- فایلهای مرتبط را بخوان
|
||||||
- endpoint های موجود را بررسی کن: `php bin/console debug:router | grep api`
|
- endpoint های موجود را بررسی کن: `php bin/console debug:router | grep api` — API جدید فقط وقتی هیچ موجودی، حتی با توسعه، کافی نباشد
|
||||||
- Entity های مرتبط را شناسایی کن
|
- Entity های مرتبط را شناسایی کن
|
||||||
- اگر سوال یا ابهامی هست → همینجا بپرس، نه وسط پیادهسازی
|
- **ابهام = توقف**: سوال را همینجا بپرس، نه وسط پیادهسازی
|
||||||
|
|
||||||
### مرحله ② — طراحی
|
### مرحله ② — طراحی
|
||||||
|
|
||||||
@@ -94,6 +90,8 @@ description: اجرای یک فایل پرامپت .md به صورت گامب
|
|||||||
- چه API endpoint هایی اضافه/تغییر میکنند
|
- چه API endpoint هایی اضافه/تغییر میکنند
|
||||||
- چه migration لازم است (اگر Entity تغییر کرد)
|
- چه migration لازم است (اگر Entity تغییر کرد)
|
||||||
|
|
||||||
|
اگر بیش از یک راهحل جدی وجود دارد: مزایا، معایب، ریسک و حداقل یک گزینهٔ جایگزین را مقایسه کن و دلیل انتخاب را بنویس (guidelines §۶). قبل از ساختن چیز جدید: اول بگرد، بعد توسعه بده، در آخر بساز — و دلیلش را بنویس.
|
||||||
|
|
||||||
### مرحله ③ — پیادهسازی
|
### مرحله ③ — پیادهسازی
|
||||||
|
|
||||||
- قابلیت را در todo روی `in_progress` بگذار
|
- قابلیت را در todo روی `in_progress` بگذار
|
||||||
@@ -103,7 +101,7 @@ description: اجرای یک فایل پرامپت .md به صورت گامب
|
|||||||
|
|
||||||
### مرحله ④ — تست
|
### مرحله ④ — تست
|
||||||
|
|
||||||
بعد از هر قابلیت این چکلیست را اجرا کن:
|
Definition of Done: syntax/build سبز **و** پوشش سه سناریوی معیار پذیرش (موفق، خطا، مرزی). دستورهای زیر حداقلاند نه سقف — اگر قابلیت منطق دارد، همان منطق را واقعاً اجرا کن (curl به endpoint با داده و توکن واقعی، نه فقط `php -l`). اگر پرامپت «نحوه تست» داده، همان را اجرا کن.
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# PHP syntax
|
# PHP syntax
|
||||||
@@ -121,6 +119,11 @@ ddev exec yarn dev
|
|||||||
|
|
||||||
# Route وجود دارد؟
|
# Route وجود دارد؟
|
||||||
ddev exec php bin/console debug:router | grep "new-route-name"
|
ddev exec php bin/console debug:router | grep "new-route-name"
|
||||||
|
|
||||||
|
# تست واقعی endpoint — سه سناریو:
|
||||||
|
# ✅ curl با توکن معتبر و داده واقعی → response مطابق معیار پذیرش
|
||||||
|
# ❌ بدون توکن / بدون permission / ورودی نامعتبر → status و error code درست
|
||||||
|
# ⚠️ حالت مرزی معیار پذیرش
|
||||||
```
|
```
|
||||||
|
|
||||||
اگر frontend تغییر کرد، TypeScript errors را بررسی کن:
|
اگر frontend تغییر کرد، TypeScript errors را بررسی کن:
|
||||||
@@ -130,13 +133,13 @@ ddev exec npx tsc --noEmit --project tsconfig.json 2>&1 | head -30
|
|||||||
|
|
||||||
### مرحله ⑤ — رفع خطا
|
### مرحله ⑤ — رفع خطا
|
||||||
|
|
||||||
- اگر خطا بود → **همینجا** رفع کن، به مرحله بعد نرو
|
- اگر خطا بود → **همینجا** رفع کن، به مرحله بعد نرو؛ هیچ قابلیتی روی خرابهٔ قابلیت قبلی ساخته نمیشود
|
||||||
- اگر خطا در فایل دیگری بود → آن را هم رفع کن
|
- اگر خطا در فایل دیگری بود → آن را هم رفع کن
|
||||||
- بعد از رفع خطا → دوباره تست را اجرا کن
|
- بعد از رفع خطا → دوباره تست را اجرا کن
|
||||||
|
|
||||||
### مرحله ⑥ — مستندسازی
|
### مرحله ⑥ — مستندسازی
|
||||||
|
|
||||||
بعد از موفقیت تست، مستندات را بهروزرسانی کن:
|
سند در **همین جلسه** بهروز میشود — «بعداً» یعنی هرگز. اگر سند موجود با رفتار فعلی فرق دارد، اول سند را اصلاح کن، بعد قابلیت جدید را اضافه کن.
|
||||||
|
|
||||||
| تغییر | فایل مستندات |
|
| تغییر | فایل مستندات |
|
||||||
|-------|-------------|
|
|-------|-------------|
|
||||||
@@ -157,36 +160,37 @@ ddev exec npx tsc --noEmit --project tsconfig.json 2>&1 | head -30
|
|||||||
الزامات مستندسازی:
|
الزامات مستندسازی:
|
||||||
1. endpoint جدید با method، path، permission
|
1. endpoint جدید با method، path، permission
|
||||||
2. Request body با تمام فیلدها و نوع داده
|
2. Request body با تمام فیلدها و نوع داده
|
||||||
3. Response format با مثال واقعی JSON
|
3. Response با **JSON واقعی از اجرای واقعی** (خروجی curl مرحله تست، نه دستساز)
|
||||||
4. تمام status codes و error codes
|
4. تمام status codes و error codes
|
||||||
5. اگر پارامتر query داشت → همه را مستند کن
|
5. اگر پارامتر query داشت → همه را مستند کن
|
||||||
|
|
||||||
### مرحله ⑦ — Todo + گزارش
|
### مرحله ⑦ — چکلیست + Todo + گزارش
|
||||||
|
|
||||||
بعد از موفقیت تست و مستندسازی:
|
۱. **چکلیست پایان قابلیت — guidelines §۷** را کامل مرور کن؛ فقط اگر همهٔ آیتمها تیک خورد، ادامه بده
|
||||||
|
۲. **آیتم todo را `completed` کن** با `TodoWrite`
|
||||||
۱. **آیتم todo را `completed` کن** با `TodoWrite`
|
۳. گزارش کوتاه نمایش بده:
|
||||||
۲. گزارش کوتاه نمایش بده:
|
|
||||||
|
|
||||||
```
|
```
|
||||||
✅ قابلیت [شماره]: [نام] — تکمیل شد
|
✅ قابلیت [شماره]: [نام] — تکمیل شد
|
||||||
|
|
||||||
|
معیار پذیرش: ✅ موفق / ❌ خطا / ⚠️ مرزی — هر سه تست شد (نتیجه واقعی، نه ادعا)
|
||||||
فایلهای تغییر یافته:
|
فایلهای تغییر یافته:
|
||||||
• src/...
|
• src/...
|
||||||
• assets/admin/...
|
• assets/admin/...
|
||||||
• docs/api/...
|
• docs/api/...
|
||||||
|
مستندات: docs/api/... بهروز شد | نیاز نداشت (دلیل)
|
||||||
|
|
||||||
📊 پیشرفت کلی:
|
📊 پیشرفت کلی:
|
||||||
✅ قابلیت ۱: [نام] — تکمیل
|
✅ قابلیت ۱: [نام] — تکمیل
|
||||||
✅ قابلیت ۲: [نام] — تکمیل
|
⏳ قابلیت ۲: [نام] — در صف
|
||||||
⏳ قابلیت ۳: [نام] — در صف
|
|
||||||
...
|
...
|
||||||
|
|
||||||
─────────────────────────────────
|
─────────────────────────────────
|
||||||
▶ قابلیت بعدی: [شماره] — [نام]
|
▶ قابلیت بعدی: [شماره] — [نام]
|
||||||
آیا ادامه دهم؟
|
آیا ادامه دهم؟
|
||||||
```
|
```
|
||||||
|
|
||||||
|
در گزارش، بین **واقعیت** (خروجی تست واقعی)، **فرضیه** و **حدس** تفاوت بگذار و برچسبش را مشخص کن؛ اگر چیزی تست نشده، همان را بگو — قطعی جا نزن.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## قوانین خاص این پروژه
|
## قوانین خاص این پروژه
|
||||||
@@ -194,6 +198,7 @@ ddev exec npx tsc --noEmit --project tsconfig.json 2>&1 | head -30
|
|||||||
### Backend
|
### Backend
|
||||||
- همه controller ها از `BaseController` ارث میبرند
|
- همه controller ها از `BaseController` ارث میبرند
|
||||||
- پاسخها: `$this->success($data)` | `$this->paginated(...)` | `$this->error(...)`
|
- پاسخها: `$this->success($data)` | `$this->paginated(...)` | `$this->error(...)`
|
||||||
|
- خطاها با `AppException(ErrorCodes::ERR_XXX)`؛ کدها و پیام فارسی در `src/Shared/Constant/ErrorCodes.php`
|
||||||
- لیستهای admin از DQL array hydration (`.getArrayResult()`) استفاده کنند
|
- لیستهای admin از DQL array hydration (`.getArrayResult()`) استفاده کنند
|
||||||
- تاریخها Unix timestamp صحیح (نه DateTime object)
|
- تاریخها Unix timestamp صحیح (نه DateTime object)
|
||||||
- بعد از هر تغییر Entity: حتماً migration بساز و اجرا کن
|
- بعد از هر تغییر Entity: حتماً migration بساز و اجرا کن
|
||||||
@@ -219,16 +224,16 @@ ddev exec npx tsc --noEmit --project tsconfig.json 2>&1 | head -30
|
|||||||
```
|
```
|
||||||
کاربر: /run-prompt .claude/prompt/appointments-redesign.md
|
کاربر: /run-prompt .claude/prompt/appointments-redesign.md
|
||||||
|
|
||||||
→ فایل را میخوانم...
|
→ guidelines.md خوانده شد
|
||||||
→ تحلیل میکنم...
|
→ فایل پرامپت را میخوانم...
|
||||||
|
→ کد مرتبط را میخوانم و پرامپت را نقد میکنم...
|
||||||
|
|
||||||
📋 قابلیتهای شناساییشده:
|
🧭 بازگویی مسئله: صفحه نوبتها آمار روز و تغییر وضعیت inline ندارد؛ رفتار درست، toolbar با آمار و تقویم شمسی است؛ محدوده AppointmentsPage + یک endpoint آمار.
|
||||||
۱. Stats Bar — آمار امروز (backend + frontend)
|
📋 قابلیتها + معیار پذیرش:
|
||||||
۲. تقویم شمسی Popup (PersianCalendar component)
|
۱. Stats Bar (backend + frontend) — ✅ آمار امروز درست / ❌ بدون توکن 401 / ⚠️ روز بدون نوبت → صفر
|
||||||
۳. بازطراحی Toolbar با date navigator
|
۲. تقویم شمسی Popup — ✅/❌/⚠️ ...
|
||||||
۴. نمای جدولی با inline status change
|
۳. بازطراحی Toolbar با date navigator — ...
|
||||||
۵. نمای زمانبندی (Schedule View)
|
۴. نمای جدولی با inline status change — ...
|
||||||
۶. ثبت نوبت از slot خالی
|
|
||||||
|
|
||||||
🚀 شروع با قابلیت ۱ (Stats Bar) — آیا ادامه دهم؟
|
🚀 شروع با قابلیت ۱ (Stats Bar) — آیا ادامه دهم؟
|
||||||
```
|
```
|
||||||
|
|||||||
Reference in New Issue
Block a user