@michaelbel/cuckcoder-mcp 1.6.11

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 (92) hide show
  1. package/assets/rules/architecture.md +15 -0
  2. package/assets/rules/bottom-sheet.md +20 -0
  3. package/assets/rules/compose-color.md +22 -0
  4. package/assets/rules/compose-constraintlayout.md +18 -0
  5. package/assets/rules/compose-screen.md +33 -0
  6. package/assets/rules/compose-spacing.md +39 -0
  7. package/assets/rules/compose.md +54 -0
  8. package/assets/rules/dialog.md +33 -0
  9. package/assets/rules/domain.md +36 -0
  10. package/assets/rules/filesystem.md +3 -0
  11. package/assets/rules/git.md +11 -0
  12. package/assets/rules/github-readme.md +97 -0
  13. package/assets/rules/github-repo.md +65 -0
  14. package/assets/rules/kmp.md +14 -0
  15. package/assets/rules/kotlin.md +48 -0
  16. package/assets/rules/lazylist.md +36 -0
  17. package/assets/rules/mvi-error-handling.md +19 -0
  18. package/assets/rules/mvi-state.md +31 -0
  19. package/assets/rules/mvi.md +39 -0
  20. package/assets/rules/navigation.md +20 -0
  21. package/assets/rules/network.md +36 -0
  22. package/assets/rules/preview.md +31 -0
  23. package/assets/rules/realtime.md +39 -0
  24. package/assets/rules/resource.md +18 -0
  25. package/assets/rules/room.md +33 -0
  26. package/assets/rules/scaffold.md +25 -0
  27. package/assets/rules/shimmer.md +34 -0
  28. package/assets/rules/textfield.md +31 -0
  29. package/assets/rules/typography.md +32 -0
  30. package/assets/rules/usecase.md +60 -0
  31. package/assets/rules/workflow.md +17 -0
  32. package/assets/rules/workmanager.md +44 -0
  33. package/assets/skills/create-data-layer/SKILL.md +60 -0
  34. package/assets/skills/create-datastore-preference/SKILL.md +80 -0
  35. package/assets/skills/create-domain-mapper/SKILL.md +30 -0
  36. package/assets/skills/create-feature-alert-dialog/SKILL.md +549 -0
  37. package/assets/skills/create-feature-bottom-sheet/SKILL.md +300 -0
  38. package/assets/skills/create-feature-scaffold-screen/SKILL.md +74 -0
  39. package/assets/skills/create-feature-scaffold-screen/references/form-screen.md +20 -0
  40. package/assets/skills/create-feature-scaffold-screen/references/network-only-screen.md +20 -0
  41. package/assets/skills/create-feature-scaffold-screen/references/paginated-screen.md +29 -0
  42. package/assets/skills/create-feature-scaffold-screen/references/room-backed-screen.md +23 -0
  43. package/assets/skills/create-feature-scaffold-screen/references/static-screen.md +13 -0
  44. package/assets/skills/create-guide/SKILL.md +169 -0
  45. package/assets/skills/create-guide/references/android-project.md +120 -0
  46. package/assets/skills/create-guide/references/androidx.md +49 -0
  47. package/assets/skills/create-guide/references/existing-guides.yaml +60 -0
  48. package/assets/skills/create-guide/references/github.md +71 -0
  49. package/assets/skills/create-guide/references/guide-defaults.yaml +51 -0
  50. package/assets/skills/create-guide/references/notion.md +121 -0
  51. package/assets/skills/create-guide/references/output-contract.md +79 -0
  52. package/assets/skills/create-guide/references/research.md +64 -0
  53. package/assets/skills/create-guide/references/validation.md +59 -0
  54. package/assets/skills/create-guide/scripts/validate_guide.py +167 -0
  55. package/assets/skills/create-guide/templates/guide-manifest.yaml +67 -0
  56. package/assets/skills/create-guide/templates/implementation-plan.md +30 -0
  57. package/assets/skills/create-guide/templates/notion-page.md +46 -0
  58. package/assets/skills/create-guide/templates/repository/AGENTS.md +7 -0
  59. package/assets/skills/create-guide/templates/repository/README.md +32 -0
  60. package/assets/skills/create-guide/templates/repository/SOURCES.md +32 -0
  61. package/assets/skills/create-guide/templates/validation-report.md +30 -0
  62. package/assets/skills/create-guide/tests/test_validate_guide.py +97 -0
  63. package/assets/skills/create-ktor-endpoint/SKILL.md +33 -0
  64. package/assets/skills/create-notification-flow/SKILL.md +103 -0
  65. package/assets/skills/create-offline-outbox/SKILL.md +110 -0
  66. package/assets/skills/create-paging-flow/SKILL.md +96 -0
  67. package/assets/skills/create-project-from-template/SKILL.md +91 -0
  68. package/assets/skills/create-project-from-template/assets/icons/android.svg +12 -0
  69. package/assets/skills/create-project-from-template/assets/icons/compose.svg +34 -0
  70. package/assets/skills/create-project-from-template/assets/icons/jetpack.svg +10 -0
  71. package/assets/skills/create-project-from-template/references/myapplication-checklist.md +92 -0
  72. package/assets/skills/create-room-storage/SKILL.md +39 -0
  73. package/assets/skills/create-shared-component/SKILL.md +160 -0
  74. package/assets/skills/create-signalr-channel/SKILL.md +39 -0
  75. package/assets/skills/create-usecase/SKILL.md +142 -0
  76. package/assets/skills/create-workmanager-task/SKILL.md +41 -0
  77. package/assets/skills/github-repo-settings/SKILL.md +152 -0
  78. package/assets/skills/interview-me/SKILL.md +150 -0
  79. package/dist/errors.js +49 -0
  80. package/dist/frontmatter.js +44 -0
  81. package/dist/github.js +23 -0
  82. package/dist/index.js +6 -0
  83. package/dist/server.js +131 -0
  84. package/dist/source/bundled.js +78 -0
  85. package/dist/source/cache.js +61 -0
  86. package/dist/source/github-source.js +68 -0
  87. package/dist/source/github.js +185 -0
  88. package/dist/source/index.js +36 -0
  89. package/dist/source/types.js +1 -0
  90. package/dist/validation.js +33 -0
  91. package/dist/version.js +26 -0
  92. package/package.json +38 -0
@@ -0,0 +1,15 @@
1
+ ---
2
+ paths:
3
+ - "**/*.gradle.kts"
4
+ - "**/*.gradle"
5
+ - "**/AndroidManifest.xml"
6
+ - "**/*.kt"
7
+ ---
8
+
9
+ # Правила архитектуры
10
+
11
+ - Помещай логику маппинга в KTX-файлы мапперов.
12
+ - Для каждой модели создавай отдельный файл; не объявляй несколько классов моделей в одном файле.
13
+ - Для проверок уровня Android API сравнивай `Build.VERSION.SDK_INT` с числовыми уровнями API; не
14
+ используй буквенные константы `Build.VERSION_CODES`, например используй
15
+ `Build.VERSION.SDK_INT >= 31` вместо `Build.VERSION.SDK_INT >= Build.VERSION_CODES.S`.
@@ -0,0 +1,20 @@
1
+ ---
2
+ paths:
3
+ - "**/*.gradle.kts"
4
+ - "**/*.gradle"
5
+ - "**/AndroidManifest.xml"
6
+ - "**/*.kt"
7
+ ---
8
+
9
+ # Правила Bottom Sheet
10
+
11
+ - Пакеты bottom sheet фич в `features` используют постфикс `_sheet`, например
12
+ `features/profile_sheet`.
13
+ - Используй file-level `@file:OptIn(ExperimentalMaterial3Api::class)`, когда файл использует
14
+ Material3 API для bottom sheet.
15
+ - Создавай `sheetState` через `rememberModalBottomSheetState(skipPartiallyExpanded = true)`.
16
+ - Когда кнопка закрывает sheet, вызывай `sheetState.hide()` внутри `scope.launch` перед диспатчем
17
+ intent подтверждения или закрытия.
18
+ - Делай preview для composable, который сам рендерит `SharedModalBottomSheet` или
19
+ `ModalBottomSheet`; оборачивай его в многострочно оформленный `Box` с
20
+ `modifier = Modifier.fillMaxSize()`, иначе preview bottom sheet может не отрендериться.
@@ -0,0 +1,22 @@
1
+ ---
2
+ paths:
3
+ - "**/*.gradle.kts"
4
+ - "**/*.gradle"
5
+ - "**/AndroidManifest.xml"
6
+ - "**/*.kt"
7
+ ---
8
+
9
+ # Правила цвета Compose
10
+
11
+ - Используй цвета через `MaterialTheme.colorScheme`; не используй сырые константы `Color` для
12
+ тематических цветов внутри компонентов.
13
+ - Добавляй новые UI-цвета в файл Colors UI kit и предоставляй их через `MaterialTheme.colorScheme`;
14
+ не создавай локальные константы `private val ... = Color(...)` в файлах фич или компонентов.
15
+ - Называй новые расширения цветов `ColorScheme` по HTML/CSS-имени hex-цвета, соответствующему
16
+ hex-значению, например `val ColorScheme.midnightBlue: Color get() = Color(0xFF251052)`; не называй
17
+ их по назначению или использованию.
18
+ - В Material top app bar (`TopAppBar`, `CenterAlignedTopAppBar`, `LargeTopAppBar` и подобных)
19
+ устанавливай цвета контейнера, иконки навигации, заголовка/подзаголовка и иконок действий через
20
+ параметр `colors` с `TopAppBarDefaults.*topAppBarColors(...)`, когда API предоставляет эти цвета;
21
+ не хардкодь цвета `Icon` или `Text` внутри слотов app bar, когда они могут наследоваться от
22
+ `colors`.
@@ -0,0 +1,18 @@
1
+ ---
2
+ paths:
3
+ - "**/*.gradle.kts"
4
+ - "**/*.gradle"
5
+ - "**/AndroidManifest.xml"
6
+ - "**/*.kt"
7
+ ---
8
+
9
+ # Правила ConstraintLayout
10
+
11
+ - В блоке `constrainAs` упорядочивай присваивания ограничений так: `width`, `height`, `start`,
12
+ `top`, `end`, `bottom`.
13
+ - Когда composable позиционируется через `constrainAs`, объявляй его размер внутри блока
14
+ `constrainAs`, используя API `Dimension` (`Dimension.value(...)`, `Dimension.fillToConstraints`,
15
+ `Dimension.wrapContent`), вместо модификаторов размера во внешней цепочке modifier.
16
+ - Называй refs ConstraintLayout, созданные через `createRef` или `createRefs`, с постфиксом `Ref`;
17
+ это применимо как к одиночным refs, так и к каждому компоненту деструктурированного объявления
18
+ `createRefs()`.
@@ -0,0 +1,33 @@
1
+ ---
2
+ paths:
3
+ - "**/*.gradle.kts"
4
+ - "**/*.gradle"
5
+ - "**/AndroidManifest.xml"
6
+ - "**/*.kt"
7
+ ---
8
+
9
+ # Правила Compose-экранов
10
+
11
+ - Публичный `{Feature}Screen(viewModel = hiltViewModel())` собирает state, создаёт
12
+ remembered-хелперы событий, наблюдает события и делегирует UI приватному `{Feature}ScreenContent`.
13
+ - `*ScreenContent` получает `state`, `dispatch` и любые remembered UI-хелперы, нужные для preview.
14
+ - `*ScreenContent` и дочерние компоненты не должны содержать бизнес-логику или domain-решения;
15
+ предоставляй производные UI-флаги/текст/действия из модели ViewModel и рендери их напрямую.
16
+ - Держи ветки состояния экрана, такие как loading, content, error и empty, inline в блоке `when`
17
+ внутри `*ScreenContent`; не выделяй их в отдельные composable/функции `Loading`, `Content`,
18
+ `Error` или `Empty`, если только они не являются переиспользуемыми реальными компонентами.
19
+ - Собирай state через `collectAsStateWithLifecycle()`.
20
+ - Наблюдай одноразовые события через `ObserveAsEvents`.
21
+ - Snackbar используют `SnackbarHostState`; закрывай `currentSnackbarData` перед показом нового
22
+ snackbar.
23
+ - Используй `SnackbarMessage` для обычных информационных сообщений и `SnackbarErrorMessage` для
24
+ сообщений об ошибках.
25
+ - Рендери диалоги и bottom sheet на основе явного свойства `is...Visible` в `Model` экрана; не
26
+ используй проверки nullable payload как условие видимости. Передавай nullable данные payload в
27
+ модель диалога/sheet отдельно.
28
+ - Размещай каждый диалог и bottom sheet в отдельном одиночном `if (state.is...Visible)`; не
29
+ используй `when` для выбора между диалогами и bottom sheet.
30
+ - Объявляй простые shape inline, например `RoundedCornerShape(8.dp)`, вместо выделения их в
31
+ переменные `private val` уровня фичи.
32
+ - Добавляй новые цвета в цвета UI kit проекта и используй их через `MaterialTheme.colorScheme`; не
33
+ держи сырые константы `Color(0x...)` в файлах фич.
@@ -0,0 +1,39 @@
1
+ ---
2
+ paths:
3
+ - "**/*.gradle.kts"
4
+ - "**/*.gradle"
5
+ - "**/AndroidManifest.xml"
6
+ - "**/*.kt"
7
+ ---
8
+
9
+ # Правила отступов и размеров Compose
10
+
11
+ - Для `padding(...)` и `PaddingValues(...)` упорядочивай именованные параметры согласно сигнатуре
12
+ метода: `start`, `top`, `end`, `bottom`; либо при использовании сокращённой формы — `horizontal`,
13
+ `vertical`.
14
+ - Опускай именованные аргументы `padding(...)` или `PaddingValues(...)`, равные `0.dp`; неуказанные
15
+ измерения padding по умолчанию равны `0.dp`.
16
+ - Когда padding симметричен, используй `all`, `horizontal` или `vertical` вместо повторения
17
+ одинаковых значений для противоположных сторон.
18
+ - Используй `PaddingValues()` вместо `PaddingValues(0.dp)` — все измерения по умолчанию равны
19
+ `0.dp`.
20
+ - Когда первый и последний дочерние элементы `Row` или `Column` имеют одинаковый крайний padding,
21
+ переноси его в родительский контейнер через `padding(horizontal = ...)` или
22
+ `padding(vertical = ...)`.
23
+ - Для `Modifier.offset(...)`, зависящего от state, используй lambda-перегрузку:
24
+ `Modifier.offset { ... }`.
25
+ - Для `floatingActionButton` размещай кнопку прямо в слоте и применяй padding к modifier кнопки; не
26
+ оборачивай её в `Box` только ради полной ширины, отступа под навигационную панель или
27
+ горизонтального padding.
28
+ - Используй `Modifier.size(width = ..., height = ...)` вместо цепочки
29
+ `Modifier.width(...).height(...)`.
30
+ - Когда по дизайну текст должен быть вертикально центрирован в области фиксированной высоты,
31
+ применяй модификаторы ширины, высоты и padding напрямую к `Text`, затем используй
32
+ `Modifier.wrapContentHeight(align = Alignment.CenterVertically)`; не оборачивай `Text` в `Box`
33
+ фиксированной высоты только ради вертикального выравнивания.
34
+ - Заменяй одинаковые по размеру элементы `Spacer` между дочерними элементами `Row` или `Column` на
35
+ `horizontalArrangement = Arrangement.spacedBy(...)` или
36
+ `verticalArrangement = Arrangement.spacedBy(...)`.
37
+ - Когда центрированному `Column` нужны равномерные отступы между дочерними элементами, используй
38
+ `verticalArrangement = Arrangement.spacedBy(..., Alignment.CenterVertically)` вместо добавления
39
+ верхнего padding отдельным дочерним элементам.
@@ -0,0 +1,54 @@
1
+ ---
2
+ paths:
3
+ - "**/*.gradle.kts"
4
+ - "**/*.gradle"
5
+ - "**/AndroidManifest.xml"
6
+ - "**/*.kt"
7
+ ---
8
+
9
+ # Правила Compose
10
+
11
+ - При создании composable-функции всегда создавай для неё preview.
12
+ - Параметр `modifier` должен быть первым опциональным параметром в сигнатуре composable-функции.
13
+ - Когда composable-компоненту нужно больше одного поля данных или callback, перенеси эти поля в
14
+ `{Component}State` data class, объявленный в том же файле, и передавай его через параметр `state`
15
+ вместо отдельных параметров.
16
+ - Не объявляй локальный `val` в теле composable, чтобы вывести условие или boolean из полей `state`;
17
+ вместо этого предоставляй его как вычисляемое свойство с явным типом и геттером на самом
18
+ `{Component}State` data class, и читай это свойство напрямую в composable вместо пересчёта условия
19
+ inline.
20
+ - Composable-экраны и компоненты не должны содержать бизнес-логику, ветвящиеся функции принятия
21
+ решений или `if`/`else`, определяющие domain-поведение; переноси эту логику в ViewModel и рендери
22
+ уже подготовленное UI-состояние.
23
+ - Не создавай дополнительные локальные `private @Composable` функции для частей разметки фичи и не
24
+ вводи локальные абстракции, вспомогательные переменные или модели ради сокращения UI-кода.
25
+ Размещай разметку непосредственно в каждом месте использования и допускай дублирование, даже если
26
+ один UI-фрагмент повторяется несколько раз. Выноси его в отдельный переиспользуемый компонент
27
+ только по явному запросу. Это правило не отменяет структурные composable-функции, прямо требуемые
28
+ другими правилами, например `*ScreenContent`.
29
+ - Используй обёртку компонента `Shared*` проекта вместо прямого компонента фреймворка, когда такая
30
+ обёртка существует в проекте.
31
+ - В composable используй `val resources = LocalResources.current`; не используй
32
+ `val resources = LocalContext.current.resources`.
33
+ - Вызовы composable с именованными аргументами всегда форматируй многострочно: имя composable,
34
+ каждый аргумент и закрывающая скобка должны находиться на отдельных строках. Не используй
35
+ однострочную запись `Box(modifier = Modifier.fillMaxSize()) {`; оформляй её так:
36
+
37
+ ```kotlin
38
+ Box(
39
+ modifier = Modifier.fillMaxSize()
40
+ ) {
41
+ ```
42
+ - Для вызовов composable-функций упорядочивай аргументы так же, как они объявлены в сигнатуре
43
+ SDK/компонента.
44
+ - Объявляй значения shape inline в месте вызова, например `RoundedCornerShape(8.dp)`; не выделяй
45
+ простые shape в переменные `private val`.
46
+ - В Compose-контейнерах разделяй соседние вызовы composable пустой строкой. Внутри контейнеров вроде
47
+ `Column`, `Row`, `Box`, `ConstraintLayout` добавляй пустую строку между соседними дочерними
48
+ composable; не пиши `Text(...)` сразу за которым следует другой `Text(...)` или вызов компонента
49
+ без пустой строки.
50
+ - В файлах векторных иконок используй именованные параметры для всех булевых аргументов.
51
+ - Для каждого общего UI-компонента создавай отдельный файл; не объявляй несколько composable
52
+ компонентов в одном файле.
53
+ - Создавай новые общие UI-компоненты в `shared/ui/components`; при необходимости группируй их там в
54
+ отдельные подпапки.
@@ -0,0 +1,33 @@
1
+ ---
2
+ paths:
3
+ - "**/*.gradle.kts"
4
+ - "**/*.gradle"
5
+ - "**/AndroidManifest.xml"
6
+ - "**/*.kt"
7
+ ---
8
+
9
+ # Правила Dialog
10
+
11
+ - Используй `AlertDialog`, когда в диалоге есть кнопки действий; используй `BasicAlertDialog`, когда
12
+ кнопок действий нет.
13
+ - Для `AlertDialog` передавай слот `icon`, когда нужна иконка; устанавливай
14
+ `modifier = Modifier.size(...)` на `Icon`.
15
+ - Для `AlertDialog` передавай слот `title`, когда у диалога есть заголовок.
16
+ - Для `AlertDialog` передавай тело диалога через слот `text`.
17
+ - Для `AlertDialog` устанавливай `iconContentColor`, `titleContentColor` и `textContentColor`, когда
18
+ нужны кастомные цвета содержимого.
19
+ - Для `BasicAlertDialog` применяй `clip` и `background` прямо на modifier.
20
+ - Всегда устанавливай
21
+ `modifier = Modifier.fillMaxWidth().wrapContentHeight().clip(RoundedCornerShape(16.dp)).background(MaterialTheme.colorScheme.surfaceContainerHigh)`
22
+ на `BasicAlertDialog`.
23
+ - Делай preview composable-диалогов внутри многострочно оформленного `Box` с
24
+ `modifier = Modifier.fillMaxSize()`; иначе preview `AlertDialog`/`BasicAlertDialog` может не
25
+ отрендериться.
26
+ - Оборачивай прокручиваемое содержимое в `Column` с `weight(1F, fill = false)` и
27
+ `verticalScroll(rememberScrollState())`, чтобы обрабатывать длинный текст без обрезки.
28
+ - Размещай кнопки действий в `FlowRow` с
29
+ `horizontalArrangement = Arrangement.spacedBy(8.dp, Alignment.End)` и
30
+ `verticalArrangement = Arrangement.spacedBy(8.dp)`.
31
+ - В лямбде кнопки подтверждения вызывай `onConfirmRequest()` перед `onDismissRequest()`.
32
+ - Внешний `Column` использует `verticalArrangement = Arrangement.spacedBy(40.dp)` между колонкой
33
+ содержимого и рядом кнопок.
@@ -0,0 +1,36 @@
1
+ ---
2
+ paths:
3
+ - "**/*.gradle.kts"
4
+ - "**/*.gradle"
5
+ - "**/AndroidManifest.xml"
6
+ - "**/*.kt"
7
+ ---
8
+
9
+ # Правила Domain
10
+
11
+ - Операции domain-слоя находятся в конкретных классах `UseCase` и `FlowUseCase` в
12
+ `shared/domain/usecase`.
13
+ - Зависимости Room, Ktor, DataStore и бизнес-логики внедряются напрямую в конструктор use case.
14
+ - Сетевые ответы в use case обрабатываются через `handleResponse` или
15
+ `handleResponseResult(...).getOrThrow()`. Когда generic network failure нужно классифицировать как
16
+ endpoint-specific exception, используй `getOrElse`, пробрось `CancellationException` и выбрось
17
+ конкретное исключение; логика маппинга находится в `shared/domain/mapper/*Ktx.kt`.
18
+ - Функции-расширения мапперов Response→Entity должны называться `entity`, например
19
+ `fun ItemResponse.entity(...): ItemEntity`.
20
+ - Функции-расширения мапперов список Response→список Entity должны называться `entities`, например
21
+ `fun List<ItemResponse>.entities(...): List<ItemEntity>`.
22
+ - В мапперах Response→Entity размещай параметры функции маппера и аргументы конструктора целевой
23
+ entity на отдельных строках.
24
+ - В мапперах Response→Entity используй значения по умолчанию `orEmpty` вместо Elvis-операторов:
25
+ `String?.orEmpty()` для строк и nullable примитивные свойства-расширения `orEmpty` для примитивных
26
+ значений.
27
+ - Мапперы Response→Entity должны возвращать non-null значения entity; не возвращай nullable entity и
28
+ не используй `return null` / `mapNotNull` для отсутствующих полей ответа. Заполняй nullable поля
29
+ ответа значениями по умолчанию `orEmpty` проекта.
30
+ - Держи хелперы значений по умолчанию для nullable примитивов в
31
+ `shared/domain/mapper/PrimitiveKtx.kt`; создавай этот файл, если он отсутствует, и добавляй туда
32
+ новые nullable примитивные свойства `orEmpty`.
33
+ - Базовый класс `UseCase` / `FlowUseCase` сам отвечает за переключение диспетчеров; не добавляй
34
+ `withContext` или `flowOn` внутри реализаций.
35
+ - ViewModel-и внедряют конкретные use case и вызывают одноразовые use case с `.getOrThrow()` внутри
36
+ `launch { ... }`.
@@ -0,0 +1,3 @@
1
+ # Правила проекта
2
+
3
+ - При удалении файлов также удаляй все директории, которые в результате становятся пустыми.
@@ -0,0 +1,11 @@
1
+ # Правила Git
2
+
3
+ - Никогда не коммить несвязанные файлы вместе; добавляй файлы в индекс выборочно и делайте один
4
+ целевой коммит на каждое логическое изменение с понятным, конкретным сообщением.
5
+ - Когда пользователь просит закоммитить, выбери подходящее сообщение коммита и закоммить все текущие
6
+ изменённые файлы одним или несколькими целевыми коммитами; не оставляй файлы в изменённом
7
+ состоянии. Если пользователь сам что-то изменил или удалил, коммить только текущее состояние
8
+ рабочего дерева и не восстанавливай изменения из своего контекста.
9
+ - Обновления зависимостей коммить по одному на коммит в формате
10
+ `Bump <dependency> from <old version> to <new version>` — даже если несколько зависимостей
11
+ находятся в одном файле, применяй и коммитьте каждое изменение по отдельности.
@@ -0,0 +1,97 @@
1
+ # Правила GitHub README
2
+
3
+ Каждый README следует этой фиксированной структуре в этом порядке:
4
+
5
+ ## 1. Бейдж last-commit (всегда первый)
6
+
7
+ ```markdown
8
+ [![last-commit](https://img.shields.io/github/last-commit/<owner>/<repo>?style=for-the-badge&logo=github&labelColor=3F464F)](https://github.com/<owner>/<repo>/commits)
9
+ ```
10
+
11
+ ## 2. Описание проекта
12
+
13
+ Максимум два предложения. Обычный текст, без заголовков, описывает, что делает проект.
14
+
15
+ ## 3. Скриншот
16
+
17
+ ```markdown
18
+ <div align="left">
19
+ <img src="cover.png" alt="Cover">
20
+ </div>
21
+ ```
22
+
23
+ ## 4. Технологии
24
+
25
+ Заголовок `## Technologies`, за которым следуют shield-бейджи, по одному на технологию, все
26
+ ссылаются на корень репозитория.
27
+
28
+ ```markdown
29
+ ## Technologies
30
+ [![<name>](https://img.shields.io/badge/<label>-<color>.svg?style=for-the-badge&logo=<logo>&logoColor=<color>)](https://github.com/<owner>/<repo>)
31
+ ```
32
+
33
+ ### Правила бейджей
34
+
35
+ Когда пользователь просит добавить бейджи, создавай бейджи shields.io в стиле `for-the-badge`.
36
+
37
+ - Бейджи технологий размещаются под `## Technologies`.
38
+ - Бейджи контактов, профиля, соцсетей, донатов и ресурсов размещаются в отдельном разделе,
39
+ выбранном исходя из контекста README, например `## Contacts`, `## Links` или `## Resources`.
40
+ - Используй один Markdown-бейдж на строку.
41
+ - Используй официальные slug-и логотипов, когда shields.io их поддерживает.
42
+ - Используй inline-логотипы `data:image/svg+xml;base64,...`, когда у сервиса нет подходящего
43
+ логотипа в shields.io или когда пользователь предоставляет кастомный логотип.
44
+ - Сохраняй целевые ссылки, предоставленные пользователем.
45
+ - Держи подписи короткими; используй `_` вместо пробелов внутри подписей бейджей shields.io.
46
+ - Если пользователь просит бейджи как в примерах ниже, используй те же цвета, логотипы и стиль
47
+ ссылок, если он не попросит изменений.
48
+
49
+ Примеры бейджей технологий:
50
+
51
+ ```markdown
52
+ [![Android](https://img.shields.io/badge/Android-50AE55?style=for-the-badge&logo=android&logoColor=F6F6F6)](https://d.android.com)
53
+ [![Kotlin](https://img.shields.io/badge/kotlin-7f52ff.svg?style=for-the-badge&logo=kotlin&logoColor=white)](https://d.android.com/kotlin)
54
+ [![KMP](https://img.shields.io/badge/KMP-7F52FF?&style=for-the-badge&logo=data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iODAiIGhlaWdodD0iODAiIHZpZXdCb3g9IjAgMCA4MCA4MCIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZD0iTTAgMzcuNjA0OFYwLjEzOTIxOUwzNy40NjU3IDM3LjYwNDhIMFpNMCA0Mi4zOTUzVjgwSDAuMDk1NDI4TDM3LjcwMDIgNDIuMzk1M0gwWk00My4zMTc3IDM2LjY4MjNMODAgMEg2LjYzNTQ3TDQzLjMxNzcgMzYuNjgyM1pNNDMuMzY1NSA0My41MDQ3TDYuODcwMTcgODBINzkuODYwN0w0My4zNjU1IDQzLjUwNDdaIiBmaWxsPSJ3aGl0ZSIvPgo8L3N2Zz4K&logoColor=white)](https://www.jetbrains.com/kotlin-multiplatform)
55
+ [![Compose](https://img.shields.io/badge/compose-blue.svg?style=for-the-badge&logo=jetpackcompose&logoColor=white)](https://d.android.com/jetpack/compose)
56
+ [![Material](https://img.shields.io/badge/Material-004A76?&style=for-the-badge&logo=data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI4MDBweCIgaGVpZ2h0PSI4MDBweCIgdmlld0JveD0iMCAwIDI0IDI0Ij4KICA8dGl0bGU+bWF0ZXJpYWxfZGVzaWduPC90aXRsZT4KICA8cmVjdCB3aWR0aD0iMjQiIGhlaWdodD0iMjQiIGZpbGw9Im5vbmUiLz4KICA8cGF0aCBkPSJNMjEsMTJhOSw5LDAsMCwwLTItNS42MlYxNy42M0E4Ljc4LDguNzgsMCwwLDAsMjEsMTJtLTMuMzcsN0g2LjM4YTkuNSw5LjUsMCwwLDAsMi42NywxLjQxQTguOTEsOC45MSwwLDAsMCwxMiwyMSw4Ljg2LDguODYsMCwwLDAsMTUsMjAuNDEsOS43Miw5LjcyLDAsMCwwLDE3LjYzLDE5TTExLDE3LDcsOXY4aDRtNi04LTQsOGg0VjltLTUsNS41M0wxNS43NSw3SDguMjVMMTIsMTQuNTNNMTcuNjMsNUE4LjkxLDguOTEsMCwwLDAsNi4zOCw1SDE3LjYzTTUsMTcuNjNWNi4zOEE5LDksMCwwLDAsMywxMmE4Ljc4LDguNzgsMCwwLDAsMiw1LjYzTTIzLDEyYTEwLjU3LDEwLjU3LDAsMCwxLTMuMjIsNy43OEExMC41NywxMC41NywwLDAsMSwxMiwyM2ExMC41OSwxMC41OSwwLDAsMS03Ljc4LTMuMjJBMTAuNTcsMTAuNTcsMCwwLDEsMSwxMiwxMC41OSwxMC41OSwwLDAsMSw0LjIyLDQuMjIsMTAuNTksMTAuNTksMCwwLDEsMTIsMWExMC41NywxMC41NywwLDAsMSw3Ljc4LDMuMjJBMTAuNTksMTAuNTksMCwwLDEsMjMsMTJaIiBmaWxsPSIjZmZmZmZmIi8+Cjwvc3ZnPg==&logoColor=white)](https://m3.material.io)
57
+ [![Git](https://img.shields.io/badge/GIT-E44C30?style=for-the-badge&logo=git&logoColor=white)](https://git-scm.com)
58
+ [![Codex](https://img.shields.io/badge/Codex-75Ab9E?&style=for-the-badge&logo=data:image/svg+xml;base64,<codex-svg-base64>&logoColor=white)](https://openai.com/codex)
59
+ [![Claude](https://img.shields.io/badge/Claude-D97757?&style=for-the-badge&logo=data:image/svg+xml;base64,<claude-svg-base64>&logoColor=white)](https://claude.ai)
60
+ ```
61
+
62
+ Примеры бейджей контактов/профиля:
63
+
64
+ ```markdown
65
+ [![](https://img.shields.io/badge/Telegram-24A1DE?&style=for-the-badge&logo=data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNDciIGhlaWdodD0iMzkiIHZpZXdCb3g9IjAgMCA0NyAzOSIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZmlsbC1ydWxlPSJldmVub2RkIiBjbGlwLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik0zLjE3MjkyIDE2LjQ2NjRDMTUuNTYyNSAxMS4wNjg0IDIzLjgyNDIgNy41MDk3NSAyNy45NTggNS43OTAzOEMzOS43NjA3IDAuODgxMjMxIDQyLjIxMzIgMC4wMjg0NTkzIDQzLjgxMTcgMC4wMDAzMDAyMzhDNDQuMTYzMiAtMC4wMDU4OTMwMyA0NC45NDkzIDAuMDgxMjM3OSA0NS40NTg1IDAuNDk0NDJDNDUuODg4NSAwLjg0MzMwMyA0Ni4wMDY4IDEuMzE0NTkgNDYuMDYzNCAxLjY0NTM3QzQ2LjEyIDEuOTc2MTUgNDYuMTkwNSAyLjcyOTY4IDQ2LjEzNDUgMy4zMTg0NkM0NS40OTQ5IDEwLjAzODcgNDIuNzI3NCAyNi4zNDcgNDEuMzE5NCAzMy44NzM4QzQwLjcyMzcgMzcuMDU4NyAzOS41NTA2IDM4LjEyNjUgMzguNDE1IDM4LjIzMUMzNS45NDY5IDM4LjQ1ODIgMzQuMDcyOCAzNi42IDMxLjY4MjMgMzUuMDMzQzI3Ljk0MTggMzIuNTgxIDI1LjgyODYgMzEuMDU0NyAyMi4xOTc4IDI4LjY2MkMxOC4wMDE3IDI1Ljg5NjggMjAuNzIxOCAyNC4zNzcxIDIzLjExMzIgMjEuODkzM0MyMy43MzkgMjEuMjQzMyAzNC42MTMzIDExLjM1MjMgMzQuODIzOCAxMC40NTVDMzQuODUwMSAxMC4zNDI4IDM0Ljg3NDUgOS45MjQ1MSAzNC42MjYgOS43MDM2NEMzNC4zNzc1IDkuNDgyNzYgMzQuMDEwNyA5LjU1ODI5IDMzLjc0NjEgOS42MTgzNkMzMy4zNzA5IDkuNzAzNTEgMjcuMzk1MyAxMy42NTMxIDE1LjgxOTMgMjEuNDY3M0MxNC4xMjMyIDIyLjYzMiAxMi41ODY4IDIzLjE5OTUgMTEuMjEwNCAyMy4xNjk3QzkuNjkyODkgMjMuMTM2OSA2Ljc3Mzg4IDIyLjMxMTcgNC42MDM5MSAyMS42MDY0QzEuOTQyMzQgMjAuNzQxMiAtMC4xNzMwMTkgMjAuMjgzOCAwLjAxMTE4MTUgMTguODE0NEMwLjEwNzEyNCAxOC4wNDkxIDEuMTYxMDQgMTcuMjY2NCAzLjE3MjkyIDE2LjQ2NjRaIiBmaWxsPSJ3aGl0ZSIvPgo8L3N2Zz4K)](https://t.me/michaelbel)
66
+ [![](https://img.shields.io/badge/LinkedIn-0077B5?&style=for-the-badge&logo=data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNzUiIGhlaWdodD0iNzUiIHZpZXdCb3g9IjAgMCA3NSA3NSIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZmlsbC1ydWxlPSJldmVub2RkIiBjbGlwLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik03NSA3NUg1OS41ODk4VjQ4Ljc1MzFDNTkuNTg5OCA0MS41NTY5IDU2Ljg1NTUgMzcuNTM1NCA1MS4xNTk3IDM3LjUzNTRDNDQuOTYzNCAzNy41MzU0IDQxLjcyNjEgNDEuNzIwNCA0MS43MjYxIDQ4Ljc1MzFWNzVIMjYuODc1VjI1SDQxLjcyNjFWMzEuNzM1QzQxLjcyNjEgMzEuNzM1IDQ2LjE5MTQgMjMuNDcyNCA1Ni44MDE4IDIzLjQ3MjRDNjcuNDA3MiAyMy40NzI0IDc1IDI5Ljk0ODggNzUgNDMuMzQzMVY3NVpNOS4xNTc3MSAxOC40NTI5QzQuMDk5MTIgMTguNDUyOSAwIDE0LjMyMTYgMCA5LjIyNjQ1QzAgNC4xMzEyOCA0LjA5OTEyIDAgOS4xNTc3MSAwQzE0LjIxNjMgMCAxOC4zMTMgNC4xMzEyOCAxOC4zMTMgOS4yMjY0NUMxOC4zMTMgMTQuMzIxNiAxNC4yMTYzIDE4LjQ1MjkgOS4xNTc3MSAxOC40NTI5Wk0xLjQ4OTI2IDc1SDE2Ljk3NTFWMjVIMS40ODkyNlY3NVoiIGZpbGw9IndoaXRlIi8+Cjwvc3ZnPgo=)](https://linkedin.com/in/michael-bel)
67
+ [![](https://img.shields.io/badge/Gmail-D14836?style=for-the-badge&logo=gmail&logoColor=white)](mailto:michaelvel24865@gmail.com)
68
+ [![](https://img.shields.io/badge/Boosty-F15F2C?style=for-the-badge&logo=boosty&logoColor=F6F6F6)](https://boosty.to/michaelbel)
69
+ [![](https://img.shields.io/badge/YouTube-FF0000?style=for-the-badge&logo=youtube&logoColor=white)](https://www.youtube.com/@michaelbely)
70
+ [![](https://img.shields.io/badge/LeetCode-282828?style=for-the-badge&logo=leetcode&logoColor=FFA116)](https://leetcode.com/u/michaelbel)
71
+ ```
72
+
73
+ Примеры бейджей ресурсов/сообщества:
74
+
75
+ ```markdown
76
+ [![](https://img.shields.io/badge/Telegram_Канал-24A1DE?&style=for-the-badge&logo=data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNDciIGhlaWdodD0iMzkiIHZpZXdCb3g9IjAgMCA0NyAzOSIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZmlsbC1ydWxlPSJldmVub2RkIiBjbGlwLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik0zLjE3MjkyIDE2LjQ2NjRDMTUuNTYyNSAxMS4wNjg0IDIzLjgyNDIgNy41MDk3NSAyNy45NTggNS43OTAzOEMzOS43NjA3IDAuODgxMjMxIDQyLjIxMzIgMC4wMjg0NTkzIDQzLjgxMTcgMC4wMDAzMDAyMzhDNDQuMTYzMiAtMC4wMDU4OTMwMyA0NC45NDkzIDAuMDgxMjM3OSA0NS40NTg1IDAuNDk0NDJDNDUuODg4NSAwLjg0MzMwMyA0Ni4wMDY4IDEuMzE0NTkgNDYuMDYzNCAxLjY0NTM3QzQ2LjEyIDEuOTc2MTUgNDYuMTkwNSAyLjcyOTY4IDQ2LjEzNDUgMy4zMTg0NkM0NS40OTQ5IDEwLjAzODcgNDIuNzI3NCAyNi4zNDcgNDEuMzE5NCAzMy44NzM4QzQwLjcyMzcgMzcuMDU4NyAzOS41NTA2IDM4LjEyNjUgMzguNDE1IDM4LjIzMUMzNS45NDY5IDM4LjQ1ODIgMzQuMDcyOCAzNi42IDMxLjY4MjMgMzUuMDMzQzI3Ljk0MTggMzIuNTgxIDI1LjgyODYgMzEuMDU0NyAyMi4xOTc4IDI4LjY2MkMxOC4wMDE3IDI1Ljg5NjggMjAuNzIxOCAyNC4zNzcxIDIzLjExMzIgMjEuODkzM0MyMy43MzkgMjEuMjQzMyAzNC42MTMzIDExLjM1MjMgMzQuODIzOCAxMC40NTVDMzQuODUwMSAxMC4zNDI4IDM0Ljg3NDUgOS45MjQ1MSAzNC42MjYgOS43MDM2NEMzNC4zNzc1IDkuNDgyNzYgMzQuMDEwNyA5LjU1ODI5IDMzLjc0NjEgOS42MTgzNkMzMy4zNzA5IDkuNzAzNTEgMjcuMzk1MyAxMy42NTMxIDE1LjgxOTMgMjEuNDY3M0MxNC4xMjMyIDIyLjYzMiAxMi41ODY4IDIzLjE5OTUgMTEuMjEwNCAyMy4xNjk3QzkuNjkyODkgMjMuMTM2OSA2Ljc3Mzg4IDIyLjMxMTcgNC42MDM5MSAyMS42MDY0QzEuOTQyMzQgMjAuNzQxMiAtMC4xNzMwMTkgMjAuMjgzOCAwLjAxMTE4MTUgMTguODE0NEMwLjEwNzEyNCAxOC4wNDkxIDEuMTYxMDQgMTcuMjY2NCAzLjE3MjkyIDE2LjQ2NjRaIiBmaWxsPSJ3aGl0ZSIvPgo8L3N2Zz4K)](https://t.me/+QBkL98rFVfs0ZGNi)
77
+ [![](https://img.shields.io/badge/Карьерный_Роадмап-2C2C2E?style=for-the-badge&logo=notion&logoColor=white)](https://michaelbel.notion.site/ANDROID-CAREER-689a0c8ce744419f8f53446e5e68e8f1)
78
+ ```
79
+
80
+ ---
81
+
82
+ ### Полный пример
83
+
84
+ ```markdown
85
+ [![last-commit](https://img.shields.io/github/last-commit/michaelbel/total?style=for-the-badge&logo=github&labelColor=3F464F)](https://github.com/michaelbel/total/commits)
86
+
87
+ Веб-калькулятор, который складывает числа, введённые столбиком, и сразу показывает итоговую сумму.
88
+
89
+ <div align="left">
90
+ <img src="cover.png" alt="Cover">
91
+ </div>
92
+
93
+ ## Technologies
94
+ [![js](https://img.shields.io/badge/javascript-F7E018.svg?style=for-the-badge&logo=javascript&logoColor=black)](https://github.com/michaelbel/total)
95
+ [![html](https://img.shields.io/badge/html-E34F26.svg?style=for-the-badge&logo=html5&logoColor=white)](https://github.com/michaelbel/total)
96
+ [![css](https://img.shields.io/badge/css-663399.svg?style=for-the-badge&logo=css&logoColor=white)](https://github.com/michaelbel/total)
97
+ ```
@@ -0,0 +1,65 @@
1
+ # Правила структуры репозитория
2
+
3
+ Каждый GitHub-репозиторий должен следовать этой структуре.
4
+
5
+ ## .github/FUNDING.yml
6
+
7
+ ```yaml
8
+ custom: [
9
+ boosty.to/michaelbel,
10
+ tbank.ru/cf/5YrkxI1CsmO,
11
+ https://pay.cloudtips.ru/p/fce67f60,
12
+ https://yoomoney.ru/fundraise/1CSMJ5M9RKB.250919
13
+ ]
14
+ ```
15
+
16
+ После push: перейди в **Settings → General → Sponsorships** и включи кнопку Sponsor.
17
+
18
+ ## .github/CODEOWNERS
19
+
20
+ ```
21
+ * @michaelbel
22
+ ```
23
+
24
+ ## .idea/icon.svg
25
+
26
+ Директория `.idea/` должна содержать `icon.svg` (иконку проекта). Сама директория в gitignore, кроме
27
+ этого файла.
28
+
29
+ ## .gitignore
30
+
31
+ ```
32
+ .claude/
33
+ .idea/
34
+ !.idea/icon.svg
35
+ ```
36
+
37
+ Cuckcoder сам исключение из этого пункта: его корень одновременно служит пользователю как `~/.claude`
38
+ (глобальные настройки), поэтому вместо этого blacklist-паттерна использует whitelist `.gitignore` —
39
+ см. `.gitignore` в корне репозитория.
40
+
41
+ ## Файлы инструкций для AI
42
+
43
+ - `AGENTS.md` — основной файл инструкций (закоммичен, реальный файл)
44
+ - `CLAUDE.md` — симлинк → `AGENTS.md`
45
+ - `GEMINI.md` — симлинк → `AGENTS.md`
46
+
47
+ Создание симлинков:
48
+
49
+ ```bash
50
+ ln -s AGENTS.md CLAUDE.md
51
+ ln -s AGENTS.md GEMINI.md
52
+ ```
53
+
54
+ ---
55
+
56
+ ### Чек-лист при настройке нового репозитория
57
+
58
+ - [ ] `.github/FUNDING.yml` со всеми четырьмя ссылками на донаты
59
+ - [ ] Sponsorships включены в GitHub Settings
60
+ - [ ] `.github/CODEOWNERS` с `* @michaelbel`
61
+ - [ ] `.idea/icon.svg` присутствует и отслеживается
62
+ - [ ] `.gitignore` с `.claude/`, `.idea/`, `!.idea/icon.svg`
63
+ - [ ] `AGENTS.md` закоммичен
64
+ - [ ] `CLAUDE.md` симлинк → `AGENTS.md`
65
+ - [ ] `GEMINI.md` симлинк → `AGENTS.md`
@@ -0,0 +1,14 @@
1
+ ---
2
+ paths:
3
+ - "**/*.gradle.kts"
4
+ - "**/*.gradle"
5
+ - "**/AndroidManifest.xml"
6
+ - "**/*.kt"
7
+ ---
8
+
9
+ # Правила KMP
10
+
11
+ - При обновлении версии приложения обновляй её во всех таргетах: `androidApp/build.gradle.kts`
12
+ (`versionName`), `desktopApp/build.gradle.kts` (`desktopVersionName`),
13
+ `iosAppCompose/build.gradle.kts` (`iosVersionName`) и `iosApp/Configuration/Version.xcconfig`
14
+ (`MARKETING_VERSION`).
@@ -0,0 +1,48 @@
1
+ ---
2
+ paths:
3
+ - "**/*.gradle.kts"
4
+ - "**/*.gradle"
5
+ - "**/AndroidManifest.xml"
6
+ - "**/*.kt"
7
+ ---
8
+
9
+ # Правила Kotlin
10
+
11
+ - Допускай длину строки Kotlin-кода до 150 символов; не переноси код только ради стандартного
12
+ ограничения IDE в 100 символов.
13
+ - Предпочитай `when` вместо цепочек `if / else if` при выражении ветвящейся логики.
14
+ - При возврате одной из двух веток предпочитай `return when { ... }` вместо
15
+ `return if (...) ... else ...`.
16
+ - В ветках `when` используй однострочную форму без фигурных скобок только если вся ветка помещается
17
+ на одной строке: `condition -> statement`; если тело ветки многострочное, оборачивай его в
18
+ фигурные скобки, даже если оно содержит только одну инструкцию.
19
+ - В ветках `when` не ставь `launch` сразу после `->`; оборачивай тело ветки в фигурные скобки и
20
+ вызывай `launch { ... }` внутри блока ветки, даже если `launch` — единственная инструкция.
21
+ - В `sealed interface` и `sealed class` объявляй все записи `data object` перед любыми записями
22
+ `data class`.
23
+ - В `sealed interface` держи короткие объявления `data class` с параметрами на одной строке:
24
+ `data class Example(val param: Int) : ExampleInterface`.
25
+ - Если у `sealed class` нет параметров конструктора и все её члены — `object` или `data object`,
26
+ используй вместо неё `sealed interface`.
27
+ - Добавляй импорты в секцию импортов вместо использования полностью квалифицированных имён inline;
28
+ например, предпочитай `import androidx.compose.ui.graphics.Color` с
29
+ `containerColor = Color.Transparent` вместо
30
+ `containerColor = androidx.compose.ui.graphics.Color.Transparent`.
31
+ - Не ставь пробел перед `:` в объявлениях наследования или делегирования класса; пиши
32
+ `class Foo: Bar`, а не `class Foo : Bar`.
33
+ - Всегда пиши функции с `{}` и `return`; никогда не используй `=` для тела функции.
34
+ - Не используй модификатор видимости `internal` в коде на Kotlin.
35
+ - Не выделяй локальные вспомогательные функции только ради удаления нескольких повторяющихся строк;
36
+ предпочитай прямолинейный код преждевременной абстракции.
37
+ - Каждый файл должен содержать не более одной API-модели, аннотированной одновременно
38
+ `@Serializable` и `@SerialName`; дополнительные модели переноси в отдельные файлы.
39
+ - Используй `lastIndex` вместо `size - 1` или `size.minus(1)` при обращении к последнему индексу
40
+ коллекции.
41
+ - Размещай экспериментальные opt-in аннотации только на уровне файла, например
42
+ `@file:OptIn(ExperimentalMaterial3Api::class)`, а не на отдельных объявлениях.
43
+ - Если `companion object` и все его константы используются только в пределах одного файла, объявляй
44
+ и `companion object`, и каждую константу как `private`:
45
+ `private companion object { private const val LIMIT = 10 }`.
46
+ - При создании экземпляра конструктора data class всегда используй именованные аргументы для каждого
47
+ параметра, по одному на строку; не полагайся на позиционные аргументы, особенно для `Boolean` и
48
+ nullable-параметров, где смысл не очевиден из одного значения.
@@ -0,0 +1,36 @@
1
+ ---
2
+ paths:
3
+ - "**/*.gradle.kts"
4
+ - "**/*.gradle"
5
+ - "**/AndroidManifest.xml"
6
+ - "**/*.kt"
7
+ ---
8
+
9
+ # Правила LazyList
10
+
11
+ - Передавай `innerPadding` из `Scaffold` в `contentPadding`, а не как `Modifier.padding`; комбинируй
12
+ с дополнительными отступами через оператор `+`:
13
+ `contentPadding = innerPadding + PaddingValues(bottom = 72.dp)`.
14
+ - Используй `Arrangement.spacedBy()` для равномерных отступов между однородными элементами списка;
15
+ не вставляй `Spacer` между каждым элементом.
16
+ - Используй явный `Spacer` только в конце списка, чтобы создать отступ под последним элементом
17
+ (например, место под floating action button).
18
+ - Используй `items(...)` или `items(count = ...)` для рендеринга коллекций в lazy-списках; не
19
+ используй цикл с `forEachIndexed` с созданием отдельных блоков `item {}` для каждого элемента.
20
+ - Используй сырые стабильные id как ключи элементов lazy-списка, например
21
+ `key = { item -> item.id }`; не строй строковые ключи вроде `"item-${item.id}"`, когда id уже
22
+ стабилен.
23
+ - Когда элементу списка нужен его индекс, например для определения последнего элемента, используй
24
+ `items(count = items.size, key = { index -> items[index].id })` и читай `val item = items[index]`
25
+ внутри содержимого элемента.
26
+ - Определяй последний элемент lazy-списка через `index != items.lastIndex`; не сравнивай id
27
+ элементов с `items.last().id`.
28
+ - Не добавляй пустые строки между соседними блоками `item {}`.
29
+ - Никогда не добавляй горизонтальный padding отдельным элементам списка, если все элементы имеют
30
+ одинаковый padding; вместо этого помещай его в `contentPadding` через
31
+ `PaddingValues(horizontal = 16.dp)`.
32
+ - Рендери состояния загрузки/контента/ошибки/пустого экрана внутри одного lazy-списка; не разветвляй
33
+ через `when`/`if` на отдельные экземпляры lazy-списка для каждого состояния. Размещай элементы
34
+ каждого состояния за `if (state.isLoading) { ... }`, `if (state.isContentVisible) { ... }` и т.д.
35
+ внутри одного тела списка, а прокрутку для нестандартных состояний контролируй через
36
+ `userScrollEnabled`.
@@ -0,0 +1,19 @@
1
+ ---
2
+ paths:
3
+ - "**/*.gradle.kts"
4
+ - "**/*.gradle"
5
+ - "**/AndroidManifest.xml"
6
+ - "**/*.kt"
7
+ ---
8
+
9
+ # Правила обработки ошибок в MVI
10
+
11
+ - Импортируй вложенные исключения use case напрямую в ViewModel, например
12
+ `import shared.domain.usecase.ItemsDetailsUseCase.ItemsDetailsException`.
13
+ - В `catch` ViewModel обрабатывай специфичное сетевое исключение фичи раньше более общих исключений:
14
+ сбрасывай связанный `Job` запроса в `null` и отправляй `SnackbarErrorMessage` с
15
+ `throwable.message`.
16
+ - В `catch` ViewModel обрабатывай более общие значения `ClientException`, отправляя
17
+ `SnackbarErrorMessage(throwable.message)`, обрабатывай `RoomException` и `RoomSQLiteException`,
18
+ отправляя `SnackbarErrorMessage(throwable.message.orEmpty())`, а неизвестные ошибки делегируй в
19
+ `super.catch(throwable)`.