@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.
- package/assets/rules/architecture.md +15 -0
- package/assets/rules/bottom-sheet.md +20 -0
- package/assets/rules/compose-color.md +22 -0
- package/assets/rules/compose-constraintlayout.md +18 -0
- package/assets/rules/compose-screen.md +33 -0
- package/assets/rules/compose-spacing.md +39 -0
- package/assets/rules/compose.md +54 -0
- package/assets/rules/dialog.md +33 -0
- package/assets/rules/domain.md +36 -0
- package/assets/rules/filesystem.md +3 -0
- package/assets/rules/git.md +11 -0
- package/assets/rules/github-readme.md +97 -0
- package/assets/rules/github-repo.md +65 -0
- package/assets/rules/kmp.md +14 -0
- package/assets/rules/kotlin.md +48 -0
- package/assets/rules/lazylist.md +36 -0
- package/assets/rules/mvi-error-handling.md +19 -0
- package/assets/rules/mvi-state.md +31 -0
- package/assets/rules/mvi.md +39 -0
- package/assets/rules/navigation.md +20 -0
- package/assets/rules/network.md +36 -0
- package/assets/rules/preview.md +31 -0
- package/assets/rules/realtime.md +39 -0
- package/assets/rules/resource.md +18 -0
- package/assets/rules/room.md +33 -0
- package/assets/rules/scaffold.md +25 -0
- package/assets/rules/shimmer.md +34 -0
- package/assets/rules/textfield.md +31 -0
- package/assets/rules/typography.md +32 -0
- package/assets/rules/usecase.md +60 -0
- package/assets/rules/workflow.md +17 -0
- package/assets/rules/workmanager.md +44 -0
- package/assets/skills/create-data-layer/SKILL.md +60 -0
- package/assets/skills/create-datastore-preference/SKILL.md +80 -0
- package/assets/skills/create-domain-mapper/SKILL.md +30 -0
- package/assets/skills/create-feature-alert-dialog/SKILL.md +549 -0
- package/assets/skills/create-feature-bottom-sheet/SKILL.md +300 -0
- package/assets/skills/create-feature-scaffold-screen/SKILL.md +74 -0
- package/assets/skills/create-feature-scaffold-screen/references/form-screen.md +20 -0
- package/assets/skills/create-feature-scaffold-screen/references/network-only-screen.md +20 -0
- package/assets/skills/create-feature-scaffold-screen/references/paginated-screen.md +29 -0
- package/assets/skills/create-feature-scaffold-screen/references/room-backed-screen.md +23 -0
- package/assets/skills/create-feature-scaffold-screen/references/static-screen.md +13 -0
- package/assets/skills/create-guide/SKILL.md +169 -0
- package/assets/skills/create-guide/references/android-project.md +120 -0
- package/assets/skills/create-guide/references/androidx.md +49 -0
- package/assets/skills/create-guide/references/existing-guides.yaml +60 -0
- package/assets/skills/create-guide/references/github.md +71 -0
- package/assets/skills/create-guide/references/guide-defaults.yaml +51 -0
- package/assets/skills/create-guide/references/notion.md +121 -0
- package/assets/skills/create-guide/references/output-contract.md +79 -0
- package/assets/skills/create-guide/references/research.md +64 -0
- package/assets/skills/create-guide/references/validation.md +59 -0
- package/assets/skills/create-guide/scripts/validate_guide.py +167 -0
- package/assets/skills/create-guide/templates/guide-manifest.yaml +67 -0
- package/assets/skills/create-guide/templates/implementation-plan.md +30 -0
- package/assets/skills/create-guide/templates/notion-page.md +46 -0
- package/assets/skills/create-guide/templates/repository/AGENTS.md +7 -0
- package/assets/skills/create-guide/templates/repository/README.md +32 -0
- package/assets/skills/create-guide/templates/repository/SOURCES.md +32 -0
- package/assets/skills/create-guide/templates/validation-report.md +30 -0
- package/assets/skills/create-guide/tests/test_validate_guide.py +97 -0
- package/assets/skills/create-ktor-endpoint/SKILL.md +33 -0
- package/assets/skills/create-notification-flow/SKILL.md +103 -0
- package/assets/skills/create-offline-outbox/SKILL.md +110 -0
- package/assets/skills/create-paging-flow/SKILL.md +96 -0
- package/assets/skills/create-project-from-template/SKILL.md +91 -0
- package/assets/skills/create-project-from-template/assets/icons/android.svg +12 -0
- package/assets/skills/create-project-from-template/assets/icons/compose.svg +34 -0
- package/assets/skills/create-project-from-template/assets/icons/jetpack.svg +10 -0
- package/assets/skills/create-project-from-template/references/myapplication-checklist.md +92 -0
- package/assets/skills/create-room-storage/SKILL.md +39 -0
- package/assets/skills/create-shared-component/SKILL.md +160 -0
- package/assets/skills/create-signalr-channel/SKILL.md +39 -0
- package/assets/skills/create-usecase/SKILL.md +142 -0
- package/assets/skills/create-workmanager-task/SKILL.md +41 -0
- package/assets/skills/github-repo-settings/SKILL.md +152 -0
- package/assets/skills/interview-me/SKILL.md +150 -0
- package/dist/errors.js +49 -0
- package/dist/frontmatter.js +44 -0
- package/dist/github.js +23 -0
- package/dist/index.js +6 -0
- package/dist/server.js +131 -0
- package/dist/source/bundled.js +78 -0
- package/dist/source/cache.js +61 -0
- package/dist/source/github-source.js +68 -0
- package/dist/source/github.js +185 -0
- package/dist/source/index.js +36 -0
- package/dist/source/types.js +1 -0
- package/dist/validation.js +33 -0
- package/dist/version.js +26 -0
- package/package.json +38 -0
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: create-room-storage
|
|
3
|
+
description: >-
|
|
4
|
+
Use when пользователь просит добавить или изменить законченную единицу Room-хранилища: entity,
|
|
5
|
+
DAO, Pojo/relations, регистрацию в AppDatabase, version и migration. Не используй для сетевого
|
|
6
|
+
endpoint, mapper, use case или полного data flow; выбирай соответствующий отдельный skill.
|
|
7
|
+
metadata:
|
|
8
|
+
author: michaelbel
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
# Создание Room-хранилища
|
|
12
|
+
|
|
13
|
+
Сначала изучи существующие `AppDatabase`, DAO, entity, migrations и соглашения имён таблиц в
|
|
14
|
+
целевом проекте. Сохраняй их стиль и меняй только части схемы, нужные задаче.
|
|
15
|
+
|
|
16
|
+
## Результат
|
|
17
|
+
|
|
18
|
+
- Создай по одному файлу на `Entity` и Pojo-модель.
|
|
19
|
+
- Создай или расширь один тематический `@Dao` с нужными `Flow`- и suspend-операциями.
|
|
20
|
+
- Зарегистрируй entity и DAO в `AppDatabase`.
|
|
21
|
+
- При любом изменении таблицы/entity увеличь `DATABASE_VERSION` и добавь migration, если проект не
|
|
22
|
+
использует явно destructive/schema-managed стратегию.
|
|
23
|
+
- Обнови schema fixtures/exported schema, если они отслеживаются проектом.
|
|
24
|
+
|
|
25
|
+
## Инварианты
|
|
26
|
+
|
|
27
|
+
- Объявляй ключи в `@Entity(primaryKeys = [...])`; не используй `@PrimaryKey` на свойствах.
|
|
28
|
+
- Располагай обычные `fun`, включая методы с `Flow`, перед `suspend fun`.
|
|
29
|
+
- Для flow без параметров предпочитай существующую в проекте форму `@get:Query val`; для flow с
|
|
30
|
+
параметрами используй функцию.
|
|
31
|
+
- Добавляй `@Transaction` методам, возвращающим Pojo с отношениями.
|
|
32
|
+
- Предоставляй nullable `select` и отдельный `selectNotNull`, только когда оба контракта реально
|
|
33
|
+
нужны вызывающему коду.
|
|
34
|
+
- Не создавай DAO-метод, который императивно вызывает другие DAO-методы. Несколько записей собирай
|
|
35
|
+
через `AppDatabase.withTransaction` в use case; один DAO-вызов транзакцией не оборачивай.
|
|
36
|
+
- Выбирай `@Upsert`, `@Insert`, `@Update` или `@Delete` по требуемой конфликтной семантике, а не
|
|
37
|
+
автоматически.
|
|
38
|
+
|
|
39
|
+
Проверь компиляцию Room/KSP целевого модуля и migration/schema-тесты, если они есть.
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: create-shared-component
|
|
3
|
+
description: >-
|
|
4
|
+
Use when пользователь просит создать переиспользуемый Compose UI-компонент для
|
|
5
|
+
`shared/ui/components`, или говорит "create a shared component", "add a reusable component", "new
|
|
6
|
+
UI component". Строит либо простой composable с обычными типизированными параметрами (для
|
|
7
|
+
универсальных layout-примитивов), либо composable с `{Component}State`-холдером (для компонентов
|
|
8
|
+
конкретной предметной области, просто вынесенных в отдельный файл), плюс его preview. Не используй
|
|
9
|
+
для диалога; используй вместо этого
|
|
10
|
+
[create-feature-alert-dialog](../create-feature-alert-dialog/SKILL.md). Не используй для bottom
|
|
11
|
+
sheet; используй вместо этого
|
|
12
|
+
[create-feature-bottom-sheet](../create-feature-bottom-sheet/SKILL.md). Не используй для полного
|
|
13
|
+
экрана, привязанного к ViewModel; используй вместо этого
|
|
14
|
+
[create-feature-scaffold-screen](../create-feature-scaffold-screen/SKILL.md).
|
|
15
|
+
metadata:
|
|
16
|
+
author: michaelbel
|
|
17
|
+
---
|
|
18
|
+
|
|
19
|
+
# Новый общий компонент
|
|
20
|
+
|
|
21
|
+
Требует зависимость `androidx.compose.foundation`.
|
|
22
|
+
|
|
23
|
+
Создаёт переиспользуемый Compose-компонент проекта. Замени `{Component}` на имя компонента
|
|
24
|
+
(например, `SharedCard`, `BarcodeFrameBox`), а `{package}` на целевой пакет.
|
|
25
|
+
|
|
26
|
+
## Фаза 1: разместить файл компонента
|
|
27
|
+
|
|
28
|
+
Один файл на компонент — `{Component}.kt`. Размещай его прямо в `shared/ui/components`, если это
|
|
29
|
+
универсальный layout-примитив или обёртка (как `SharedRow`, `SharedColumn`); для компонента
|
|
30
|
+
конкретной предметной области создавай тематическую подпапку, например `shared/ui/components/scan`.
|
|
31
|
+
|
|
32
|
+
## Фаза 2: выбери форму компонента
|
|
33
|
+
|
|
34
|
+
- Универсальный layout-примитив или обёртка, не завязанная на конкретную предметную область (как
|
|
35
|
+
`SharedRow`, `SharedColumn`) — без State, обычные параметры.
|
|
36
|
+
- Компонент, который не такой общий, а просто вынесен в отдельный файл и представляет конкретный
|
|
37
|
+
предметный смысл (иконка-пресет, карточка определённого содержимого и т. д.) — с
|
|
38
|
+
`{Component}State` наверху файла.
|
|
39
|
+
|
|
40
|
+
## Универсальный примитив (без State)
|
|
41
|
+
|
|
42
|
+
```kotlin
|
|
43
|
+
package {package}
|
|
44
|
+
|
|
45
|
+
import androidx.compose.runtime.Composable
|
|
46
|
+
import androidx.compose.ui.Modifier
|
|
47
|
+
import androidx.compose.ui.tooling.preview.Preview
|
|
48
|
+
|
|
49
|
+
@Composable
|
|
50
|
+
fun {Component}(
|
|
51
|
+
title: String,
|
|
52
|
+
onClick: () -> Unit,
|
|
53
|
+
modifier: Modifier = Modifier
|
|
54
|
+
) {
|
|
55
|
+
// component content
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
@Preview(showBackground = true)
|
|
59
|
+
@Composable
|
|
60
|
+
private fun {Component}Preview() {
|
|
61
|
+
{Component}(
|
|
62
|
+
title = "Example",
|
|
63
|
+
onClick = {}
|
|
64
|
+
)
|
|
65
|
+
}
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
## Компонент с State
|
|
69
|
+
|
|
70
|
+
Наверху файла размести `{Component}State` — `data class`, если данные варьируются свободно, или
|
|
71
|
+
`sealed class` с несколькими `data object`-вариантами, если у компонента всего пара
|
|
72
|
+
фиксированных пресетов. Сам компонент принимает один параметр `state`; `modifier` добавляй вторым
|
|
73
|
+
параметром, только если он реально нужен.
|
|
74
|
+
|
|
75
|
+
```kotlin
|
|
76
|
+
package {package}
|
|
77
|
+
|
|
78
|
+
// Добавь все необходимые импорты
|
|
79
|
+
|
|
80
|
+
sealed class {Component}State(
|
|
81
|
+
val iconRes: Int,
|
|
82
|
+
val iconModifier: Modifier
|
|
83
|
+
) {
|
|
84
|
+
data object SampleA: {Component}State(
|
|
85
|
+
iconRes = AppIcons.SampleA,
|
|
86
|
+
iconModifier = Modifier.size(107.dp, 131.dp)
|
|
87
|
+
)
|
|
88
|
+
|
|
89
|
+
data object SampleB: {Component}State(
|
|
90
|
+
iconRes = AppIcons.SampleB,
|
|
91
|
+
iconModifier = Modifier.size(164.dp, 164.dp)
|
|
92
|
+
)
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
@Composable
|
|
96
|
+
fun {Component}(
|
|
97
|
+
state: {Component}State
|
|
98
|
+
) {
|
|
99
|
+
Box(
|
|
100
|
+
modifier = Modifier
|
|
101
|
+
.size(192.dp)
|
|
102
|
+
.clip(CircleShape)
|
|
103
|
+
.background(MaterialTheme.colorScheme.surface),
|
|
104
|
+
contentAlignment = Alignment.Center
|
|
105
|
+
) {
|
|
106
|
+
Icon(
|
|
107
|
+
painter = painterResource(state.iconRes),
|
|
108
|
+
contentDescription = null,
|
|
109
|
+
modifier = state.iconModifier,
|
|
110
|
+
tint = Color.Unspecified
|
|
111
|
+
)
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
@PreviewWrapper(ThemeWrapper::class)
|
|
116
|
+
@Preview
|
|
117
|
+
@Composable
|
|
118
|
+
private fun {Component}Preview(
|
|
119
|
+
@PreviewParameter({Component}StatePreviewParameterProvider::class) state: {Component}State
|
|
120
|
+
) {
|
|
121
|
+
{Component}(
|
|
122
|
+
state = state
|
|
123
|
+
)
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
private class {Component}StatePreviewParameterProvider: PreviewParameterProvider<{Component}State> {
|
|
127
|
+
override val values: Sequence<{Component}State> = sequenceOf(
|
|
128
|
+
{Component}State.SampleA,
|
|
129
|
+
{Component}State.SampleB
|
|
130
|
+
)
|
|
131
|
+
}
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
Правила:
|
|
135
|
+
- Универсальный примитив принимает свои данные как обычные типизированные параметры — не
|
|
136
|
+
оборачивай их в `{Component}State`, даже если параметров несколько.
|
|
137
|
+
- Компонент, который не такой общий, а просто вынесен в отдельный файл, оборачивает свои данные в
|
|
138
|
+
`{Component}State` наверху того же файла и принимает единственный параметр `state`; `modifier`
|
|
139
|
+
добавляй вторым параметром только при реальной необходимости.
|
|
140
|
+
- Порядок параметров универсального примитива: сначала обязательные (без значения по умолчанию),
|
|
141
|
+
затем `modifier: Modifier = Modifier`, затем остальные параметры со значением по умолчанию, а
|
|
142
|
+
завершающей идёт обязательная trailing-лямбда `content`, если компонент оборачивает содержимое
|
|
143
|
+
(например `content: @Composable ColumnScope.() -> Unit`).
|
|
144
|
+
- Каждый компонент находится в своём файле; не объявляй несколько компонентов в одном файле.
|
|
145
|
+
- `@PreviewWrapper(ThemeWrapper::class)` используй только для компонента с `{Component}State` —
|
|
146
|
+
ему нужна реальная тема приложения (иконки, цвета). Для универсального примитива без State
|
|
147
|
+
preview — обычный `@Preview`, без `PreviewWrapper`.
|
|
148
|
+
- Указывай `showBackground = true`, только если у компонента нет собственного видимого фона —
|
|
149
|
+
например `Text` или список на прозрачном фоне (`SharedRow`, `SharedColumn`,
|
|
150
|
+
`SharedLazyColumn`). Если компонент сам рисует себе фон или форму (как `SharedDragHandle` или
|
|
151
|
+
`{Component}` с State выше), `showBackground` не нужен.
|
|
152
|
+
- Не создавай preview вовсе, если статичный снимок ничего не покажет без реального взаимодействия
|
|
153
|
+
или внешнего состояния — например обёртка видимости (`SharedAnimatedVisibility`), хост снекбара
|
|
154
|
+
без активного снекбара (`SharedSnackbarHost`) или модальный bottom sheet.
|
|
155
|
+
- Для компонента с `{Component}State` всегда создавай приватный
|
|
156
|
+
`{Component}StatePreviewParameterProvider: PreviewParameterProvider<{Component}State>` в том же
|
|
157
|
+
файле. Для универсального примитива без State создавай `PreviewParameterProvider`, только если у
|
|
158
|
+
компонента есть несколько содержательно разных состояний, которые стоит показать; иначе preview
|
|
159
|
+
вызывает компонент напрямую с представительными литеральными значениями.
|
|
160
|
+
- Preview-функция всегда `private`.
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: create-signalr-channel
|
|
3
|
+
description: >-
|
|
4
|
+
Use when пользователь просит добавить или изменить SignalR hub/channel, realtime DataSource,
|
|
5
|
+
обработчики server events либо подключить канал к session lifecycle. Не используй для Ktor HTTP
|
|
6
|
+
endpoint, Ktor WebSocket или общего socket-решения на другом протоколе.
|
|
7
|
+
metadata:
|
|
8
|
+
author: michaelbel
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
# Создание SignalR-канала
|
|
12
|
+
|
|
13
|
+
Сначала изучи общую connection factory/infrastructure, существующие `RealtimeDataSource` и
|
|
14
|
+
`StartRealtimeUseCase`. Переиспользуй их retry, authentication и cleanup; не создавай второй
|
|
15
|
+
параллельный менеджер соединений.
|
|
16
|
+
|
|
17
|
+
## Реализация
|
|
18
|
+
|
|
19
|
+
- Создай один DataSource на один hub/domain и зарегистрируй server event handlers до `start`.
|
|
20
|
+
- Выполни команды подписки после успешного подключения.
|
|
21
|
+
- Для сигнала «authoritative state изменилось» используй приватный `MutableSharedFlow` и публичный
|
|
22
|
+
read-only `SharedFlow`; его допустимо conflated/drop только когда следующий reload получает полное
|
|
23
|
+
состояние. Для каждого обязательного payload-события используй lossless `Channel`/очередь либо
|
|
24
|
+
durable Room-запись, а не `SharedFlow(replay = 0)`.
|
|
25
|
+
- По устойчивому событию вызывай обычный use case для reload/update Room, оставляя Room источником
|
|
26
|
+
истины. Не обновляй UI напрямую payload-ом, если событие должно переживать пересоздание экрана.
|
|
27
|
+
- Подключи запуск DataSource и сбор его событий в `StartRealtimeUseCase`, чтобы смена session через
|
|
28
|
+
отменяемый flow закрывала старые соединения и запускала новые с актуальным токеном.
|
|
29
|
+
- Запусти collectors до connect/initial subscription, например принятым в проекте undispatched
|
|
30
|
+
start; альтернативно гарантируй initial reload после подключения, который перекрывает возможную
|
|
31
|
+
потерю первого invalidation-сигнала.
|
|
32
|
+
- Используй общий retry/backoff. Никогда не поглощай `CancellationException`; cleanup выполняй в
|
|
33
|
+
`finally`, при необходимости в `NonCancellable` с ограниченным timeout.
|
|
34
|
+
- Не допускай двух экземпляров одного hub для одной session и не повторяй retry после logout.
|
|
35
|
+
- Логируй lifecycle и ошибки без access token и чувствительного event payload.
|
|
36
|
+
|
|
37
|
+
Проверь сценарии connect/event/reload, reconnect, session change/logout и отмену во время retry или
|
|
38
|
+
cleanup. Если инфраструктуры realtime ещё нет, отдели shared connection factory от первого
|
|
39
|
+
domain-specific DataSource, не смешивая их ответственность.
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: create-usecase
|
|
3
|
+
description: >-
|
|
4
|
+
Use when пользователь просит добавить один `UseCase` или `FlowUseCase` в
|
|
5
|
+
`shared/domain/usecase` — одну suspend-операцию или один наблюдаемый Flow — или говорит "add a use
|
|
6
|
+
case", "new UseCase", "new FlowUseCase". Предполагает, что требуемые endpoint, DAO/entity и
|
|
7
|
+
мапперы уже существуют. Не используй для их создания, Worker/realtime lifecycle или ViewModel;
|
|
8
|
+
выбери соответствующий атомарный skill либо create-data-layer для составного потока.
|
|
9
|
+
metadata:
|
|
10
|
+
author: michaelbel
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
# Новый UseCase
|
|
14
|
+
|
|
15
|
+
Создаёт один `UseCase` или `FlowUseCase` в `shared/domain/usecase`.
|
|
16
|
+
|
|
17
|
+
## Граница ответственности
|
|
18
|
+
|
|
19
|
+
Этот skill оформляет одну business-операцию или один наблюдаемый поток и их `Result`/dispatcher
|
|
20
|
+
семантику. Он не создаёт endpoint, request/response, Room entity/DAO, mapper, Worker, realtime
|
|
21
|
+
connection или Screen. Для reload по realtime и фоновой синхронизации use case содержит
|
|
22
|
+
переиспользуемую business-логику, а realtime data source или Worker только инициирует его.
|
|
23
|
+
|
|
24
|
+
## Выбор базового класса
|
|
25
|
+
|
|
26
|
+
- Используй `UseCase<P, R>` для одноразовых suspend-операций.
|
|
27
|
+
- Используй `FlowUseCase<P, R>` для наблюдаемых потоков.
|
|
28
|
+
- Не создавай обёртки Repository или Interactor для новой работы.
|
|
29
|
+
|
|
30
|
+
## Зависимости конструктора
|
|
31
|
+
|
|
32
|
+
Внедряй конкретные зависимости напрямую:
|
|
33
|
+
- `NetworkService` для вызовов Ktor.
|
|
34
|
+
- `AppDatabase`, когда нужна транзакция Room.
|
|
35
|
+
- Классы DAO для чтения/записи Room.
|
|
36
|
+
- Классы DataStore для сохраняемых настроек.
|
|
37
|
+
- Другие use case при композиции поведения.
|
|
38
|
+
- `SharedDispatchers` как не-`private` параметр, передаваемый в базовый класс.
|
|
39
|
+
|
|
40
|
+
## Параметры
|
|
41
|
+
|
|
42
|
+
- Без входных данных: используй `Unit` и оставляй `execute(params: Unit)`.
|
|
43
|
+
- Один входной параметр: используй domain-тип как `P`, переименуй override-параметр и добавь
|
|
44
|
+
`@file:Suppress("PARAMETER_NAME_CHANGED_ON_OVERRIDE")`.
|
|
45
|
+
- Два и более входных параметра: добавь вложенный `data class Params(...)` и используй его как `P`.
|
|
46
|
+
|
|
47
|
+
## Шаблон одноразового UseCase
|
|
48
|
+
|
|
49
|
+
```kotlin
|
|
50
|
+
@file:Suppress("PARAMETER_NAME_CHANGED_ON_OVERRIDE")
|
|
51
|
+
|
|
52
|
+
class ThingsDetailsUseCase @Inject constructor(
|
|
53
|
+
private val networkService: NetworkService,
|
|
54
|
+
private val thingDao: ThingDao,
|
|
55
|
+
dispatchers: SharedDispatchers
|
|
56
|
+
): UseCase<ThingId, Unit>(dispatchers.io) {
|
|
57
|
+
|
|
58
|
+
override suspend fun execute(thingId: ThingId) {
|
|
59
|
+
handleResponse(
|
|
60
|
+
request = {
|
|
61
|
+
val request = ThingsDetailsRequest(thingId)
|
|
62
|
+
networkService.thingsDetails(request)
|
|
63
|
+
},
|
|
64
|
+
onSuccess = { data ->
|
|
65
|
+
thingDao.upsert(data.entity)
|
|
66
|
+
},
|
|
67
|
+
onFailure = { error -> throw ThingsDetailsException(error.message) }
|
|
68
|
+
)
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
data class ThingsDetailsException(
|
|
72
|
+
override val message: String
|
|
73
|
+
): AppNetworkException(message)
|
|
74
|
+
}
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
Замени `AppNetworkException` на базовое сетевое исключение целевого проекта. Имя use case и
|
|
78
|
+
вложенного исключения выводи из endpoint path или имени метода `NetworkService`, а не из intent
|
|
79
|
+
экрана.
|
|
80
|
+
|
|
81
|
+
## Шаблон Flow
|
|
82
|
+
|
|
83
|
+
```kotlin
|
|
84
|
+
@file:Suppress("PARAMETER_NAME_CHANGED_ON_OVERRIDE")
|
|
85
|
+
|
|
86
|
+
class ThingEntityFlowUseCase @Inject constructor(
|
|
87
|
+
private val thingDao: ThingDao,
|
|
88
|
+
dispatchers: SharedDispatchers
|
|
89
|
+
): FlowUseCase<ThingId, ThingEntity>(dispatchers.io) {
|
|
90
|
+
|
|
91
|
+
override fun execute(thingId: ThingId): Flow<ThingEntity> {
|
|
92
|
+
return thingDao.selectFlow(thingId)
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
## Шаблон значения сети
|
|
98
|
+
|
|
99
|
+
Используй это, когда сетевой ответ нужен как значение внутри `execute`.
|
|
100
|
+
|
|
101
|
+
```kotlin
|
|
102
|
+
@file:Suppress("PARAMETER_NAME_CHANGED_ON_OVERRIDE")
|
|
103
|
+
|
|
104
|
+
class ThingsDetailsUseCase @Inject constructor(
|
|
105
|
+
private val networkService: NetworkService,
|
|
106
|
+
dispatchers: SharedDispatchers
|
|
107
|
+
): UseCase<ThingId, ThingEntity>(dispatchers.io) {
|
|
108
|
+
|
|
109
|
+
override suspend fun execute(thingId: ThingId): ThingEntity {
|
|
110
|
+
val data = handleResponseResult {
|
|
111
|
+
val request = ThingsDetailsRequest(thingId)
|
|
112
|
+
networkService.thingsDetails(request)
|
|
113
|
+
}.getOrElse { throwable ->
|
|
114
|
+
if (throwable is CancellationException) throw throwable
|
|
115
|
+
throw ThingsDetailsException(throwable.message.orEmpty())
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
return data.entity
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
data class ThingsDetailsException(
|
|
122
|
+
override val message: String
|
|
123
|
+
): AppNetworkException(message)
|
|
124
|
+
}
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
Здесь преобразование ошибки в endpoint-specific exception является содержательной классификацией,
|
|
128
|
+
а не повторным оборачиванием того же `Result`. Всегда пробрасывай `CancellationException` без
|
|
129
|
+
преобразования.
|
|
130
|
+
|
|
131
|
+
## Обязательное поведение
|
|
132
|
+
|
|
133
|
+
- `UseCase.execute` возвращает сырой `R`, а не `Result<R>`.
|
|
134
|
+
- Выбрасывай domain-специфичные исключения при ошибках; позволяй базовому `UseCase` конвертировать
|
|
135
|
+
их в `Result.failure`.
|
|
136
|
+
- Используй `handleResponseResult(...).getOrThrow()`, когда generic exception достаточно; для
|
|
137
|
+
endpoint-specific exception используй `getOrElse`, пробрось `CancellationException` и выбрось
|
|
138
|
+
конкретный тип.
|
|
139
|
+
- Вызывай другие use case через `.getOrThrow()`.
|
|
140
|
+
- Вызывай одноразовые use case из ViewModel через `.getOrThrow()` внутри `launch { ... }`.
|
|
141
|
+
- Не добавляй `withContext` или `flowOn`; базовые классы сами обрабатывают диспетчеры.
|
|
142
|
+
- Создавай `val request = ...` перед каждым вызовом `networkService`.
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: create-workmanager-task
|
|
3
|
+
description: >-
|
|
4
|
+
Use when пользователь просит создать или изменить Android WorkManager-задачу, CoroutineWorker,
|
|
5
|
+
её enqueue/cancel use case, constraints, unique work или retry policy. Не используй для обычного
|
|
6
|
+
suspend use case без фонового планирования или для foreground service.
|
|
7
|
+
metadata:
|
|
8
|
+
author: michaelbel
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
# Создание WorkManager-задачи
|
|
12
|
+
|
|
13
|
+
Перед изменениями изучи существующие worker, Hilt/WorkManager setup и способы вызова use case в
|
|
14
|
+
целевом проекте. Определи семантику задачи: one-time или periodic, unique identity, условия запуска,
|
|
15
|
+
временные и постоянные ошибки, отмена и идемпотентность.
|
|
16
|
+
|
|
17
|
+
## Реализация
|
|
18
|
+
|
|
19
|
+
- Создай тонкий `CoroutineWorker`, который валидирует input, вызывает существующий бизнес-use case
|
|
20
|
+
через `.getOrThrow()` и преобразует результат в `success`, `retry` или `failure`. Для чисто
|
|
21
|
+
системной операции вроде показа notification допустим прямой Android API без бизнес-логики.
|
|
22
|
+
- Следуй существующей Hilt-конвенции: `@HiltWorker`/`@AssistedInject` при настроенном
|
|
23
|
+
`HiltWorkerFactory` либо существующий Hilt `EntryPoint`; не смешивай оба подхода.
|
|
24
|
+
- Создай отдельный scheduling `UseCase` и, когда сценарий требует отмены, cancellation `UseCase`.
|
|
25
|
+
UI/ViewModel взаимодействуют с ними, а не с `WorkManager` и не с `Worker.enqueue()`.
|
|
26
|
+
- Используй stable unique work name/tag только при реальном identity задачи. Выбери
|
|
27
|
+
`ExistingWorkPolicy` или `ExistingPeriodicWorkPolicy` по тому, должна ли новая заявка сохранять,
|
|
28
|
+
заменять или продолжать старую.
|
|
29
|
+
- Добавь только необходимые `Constraints`, delay/interval и backoff. Для временных ошибок ограничь
|
|
30
|
+
повторы через `runAttemptCount`, если продукт не требует неограниченного retry.
|
|
31
|
+
- Передавай через `Data` небольшие primitive/string значения. Для payload и очереди операций
|
|
32
|
+
используй Room и передавай worker стабильный id.
|
|
33
|
+
- Пробрасывай `CancellationException`; не превращай отмену в результат работы.
|
|
34
|
+
- Держи операцию идемпотентной, поскольку повторный запуск возможен после частичного выполнения.
|
|
35
|
+
- Для account/session-scoped задачи включи owner/session generation в identity и input. Подключи
|
|
36
|
+
отмену к logout, но не полагайся на неё как на барьер: worker повторно проверяет актуальную сессию
|
|
37
|
+
перед запросом и перед Room commit и завершает устаревшую работу без записи.
|
|
38
|
+
|
|
39
|
+
Проверь, что worker доступен фабрике приложения, scheduling и cancellation используют одинаковый
|
|
40
|
+
identity, а тесты покрывают хотя бы permanent error, transient retry и success, если в проекте есть
|
|
41
|
+
WorkManager test infrastructure.
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: github-repo-settings
|
|
3
|
+
description: >-
|
|
4
|
+
Use when the user asks to configure a GitHub repository after creation — set its description
|
|
5
|
+
and topics, disable Wikis, Issues, Discussions, Projects and Pull Requests, hide Releases,
|
|
6
|
+
Packages and Deployments from the repository home page, enable Sponsorships, or says "configure
|
|
7
|
+
repo settings" or "set up GitHub repository". Applies and verifies repository-level settings
|
|
8
|
+
step by step through `gh`, the documented GitHub REST API and browser UI. Do not use for tracked
|
|
9
|
+
repository files such as README.md, FUNDING.yml, CODEOWNERS or `.github/workflows`.
|
|
10
|
+
metadata:
|
|
11
|
+
author: michaelbel
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
# Настройка GitHub-репозитория
|
|
15
|
+
|
|
16
|
+
Приведи уже созданный GitHub-репозиторий к стандартному состоянию. Это внешние изменения на
|
|
17
|
+
GitHub; они не разрешают редактировать tracked files, менять visibility, Actions, secrets,
|
|
18
|
+
collaborators, branch protection, Pages, merge policy или security settings.
|
|
19
|
+
|
|
20
|
+
## Целевое состояние
|
|
21
|
+
|
|
22
|
+
- задано короткое фактическое description;
|
|
23
|
+
- задано 3–6 релевантных lowercase topics;
|
|
24
|
+
- Wikis, Issues, Discussions, Projects и Pull Requests выключены;
|
|
25
|
+
- Releases, Packages и Deployments скрыты из блока About на главной странице;
|
|
26
|
+
- Sponsorships включены, если валидный `.github/FUNDING.yml` уже находится в default branch;
|
|
27
|
+
- GitHub Actions остаётся включённым.
|
|
28
|
+
|
|
29
|
+
## 1. Определи точный репозиторий
|
|
30
|
+
|
|
31
|
+
1. Если пользователь передал `OWNER/REPO` или URL, используй его.
|
|
32
|
+
2. Иначе получи remote из текущего проекта и разреши его через
|
|
33
|
+
`gh repo view --json nameWithOwner -q .nameWithOwner`.
|
|
34
|
+
3. Перед любым write покажи в update точный `OWNER/REPO`. Не работай с репозиторием, который
|
|
35
|
+
определился неоднозначно, и не полагайся на случайный current directory.
|
|
36
|
+
4. Выполни `gh auth status`, затем проверь
|
|
37
|
+
`gh repo view OWNER/REPO --json viewerPermission,isArchived`. Нужен admin-доступ; archived
|
|
38
|
+
repository не изменяй.
|
|
39
|
+
|
|
40
|
+
Не печатай auth token, secrets или полный environment.
|
|
41
|
+
|
|
42
|
+
## 2. Собери исходное состояние
|
|
43
|
+
|
|
44
|
+
Сначала выполни read-only проверки:
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
gh repo view OWNER/REPO \
|
|
48
|
+
--json nameWithOwner,description,repositoryTopics,hasWikiEnabled,hasIssuesEnabled,hasDiscussionsEnabled,hasProjectsEnabled,viewerPermission,isArchived
|
|
49
|
+
|
|
50
|
+
gh api repos/OWNER/REPO \
|
|
51
|
+
-H "X-GitHub-Api-Version: 2026-03-10" \
|
|
52
|
+
--jq '{has_pull_requests,default_branch,visibility}'
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
Проверь default branch на наличие `.github/FUNDING.yml`, не изменяя файл. Текущее состояние нужно
|
|
56
|
+
для итогового diff и чтобы не выполнять лишние write-запросы.
|
|
57
|
+
|
|
58
|
+
## 3. Подготовь description и topics
|
|
59
|
+
|
|
60
|
+
1. Возьми назначение проекта из README и фактического кода; не придумывай возможности продукта.
|
|
61
|
+
2. Сформулируй description одним коротким предложением без точки в конце, badges и marketing copy.
|
|
62
|
+
3. Выбери 3–6 topics по платформе, языку, UI/framework и назначению проекта. Topics должны быть
|
|
63
|
+
lowercase, не длиннее 50 символов и содержать только буквы, цифры и дефисы.
|
|
64
|
+
4. Сохрани релевантные существующие topics и удали только устаревшие или относящиеся к шаблону.
|
|
65
|
+
Если корректный итоговый набор нельзя вывести из проекта, согласуй его до write-запроса.
|
|
66
|
+
|
|
67
|
+
## 4. Примени CLI-настройки
|
|
68
|
+
|
|
69
|
+
Сначала установи description и отключи функции, которые поддерживает `gh repo edit`:
|
|
70
|
+
|
|
71
|
+
```bash
|
|
72
|
+
gh repo edit OWNER/REPO \
|
|
73
|
+
--description "PROJECT_DESCRIPTION" \
|
|
74
|
+
--enable-wiki=false \
|
|
75
|
+
--enable-issues=false \
|
|
76
|
+
--enable-discussions=false \
|
|
77
|
+
--enable-projects=false
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
Затем приведи topics к согласованному точному набору. Endpoint заменяет весь список, поэтому не
|
|
81
|
+
запускай его до проверки текущих topics:
|
|
82
|
+
|
|
83
|
+
```bash
|
|
84
|
+
gh api --method PUT repos/OWNER/REPO/topics \
|
|
85
|
+
-H "X-GitHub-Api-Version: 2026-03-10" \
|
|
86
|
+
-F 'names[]=TOPIC_ONE' \
|
|
87
|
+
-F 'names[]=TOPIC_TWO' \
|
|
88
|
+
-F 'names[]=TOPIC_THREE'
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
Отключи Pull Requests через документированный REST field, которого пока нет среди флагов
|
|
92
|
+
`gh repo edit`:
|
|
93
|
+
|
|
94
|
+
```bash
|
|
95
|
+
gh api --method PATCH repos/OWNER/REPO \
|
|
96
|
+
-H "X-GitHub-Api-Version: 2026-03-10" \
|
|
97
|
+
-F has_pull_requests=false
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
Не объединяй эти операции с изменением visibility, default branch или merge settings. При `401`,
|
|
101
|
+
`403` или `404` остановись, перепроверь target/auth/permission и не повторяй write вслепую.
|
|
102
|
+
|
|
103
|
+
## 5. Примени UI-настройки
|
|
104
|
+
|
|
105
|
+
Используй доступный browser control с существующей авторизованной GitHub-сессией. Если browser
|
|
106
|
+
недоступен или пользователь не авторизован, не пытайся обходить login: перечисли оставшиеся шаги.
|
|
107
|
+
|
|
108
|
+
1. Открой главную страницу `https://github.com/OWNER/REPO`.
|
|
109
|
+
2. В блоке **About** нажми шестерёнку.
|
|
110
|
+
3. В **Include in the home page** выключи показанные **Releases**, **Packages** и **Deployments**,
|
|
111
|
+
затем сохрани. Если конкретного переключателя нет, не имитируй его через private API — отметь
|
|
112
|
+
его как отсутствующий в текущем UI.
|
|
113
|
+
4. Открой **Settings → General → Features**.
|
|
114
|
+
5. Если `.github/FUNDING.yml` существует в default branch, включи **Sponsorships**. Не создавай и
|
|
115
|
+
не редактируй funding file в рамках этого skill.
|
|
116
|
+
|
|
117
|
+
Не используй private/undocumented endpoint для UI-only toggles: интерфейс может изменить внутренний
|
|
118
|
+
запрос без обратной совместимости.
|
|
119
|
+
|
|
120
|
+
## 6. Проверь результат
|
|
121
|
+
|
|
122
|
+
Повтори CLI-проверки из шага 2 и сравни их с целевым состоянием. Отдельно проверь topics:
|
|
123
|
+
|
|
124
|
+
```bash
|
|
125
|
+
gh api repos/OWNER/REPO/topics \
|
|
126
|
+
-H "X-GitHub-Api-Version: 2026-03-10" \
|
|
127
|
+
--jq '.names'
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
В браузере обнови страницы About и Settings → General и убедись, что UI-переключатели сохранились.
|
|
131
|
+
Не считай задачу завершённой только по успешному exit code команды.
|
|
132
|
+
|
|
133
|
+
В финальном ответе кратко перечисли:
|
|
134
|
+
|
|
135
|
+
- настроенный `OWNER/REPO`;
|
|
136
|
+
- итоговые description и topics;
|
|
137
|
+
- подтверждённые выключенные функции;
|
|
138
|
+
- статус Releases/Packages/Deployments и Sponsorships;
|
|
139
|
+
- шаги, которые не удалось выполнить из-за permissions или отсутствия browser session.
|
|
140
|
+
|
|
141
|
+
## Актуальность API
|
|
142
|
+
|
|
143
|
+
Workflow основан на текущих официальных интерфейсах:
|
|
144
|
+
|
|
145
|
+
- [`gh repo edit`](https://cli.github.com/manual/gh_repo_edit);
|
|
146
|
+
- [REST: Update a repository](https://docs.github.com/en/rest/repos/repos?apiVersion=2026-03-10#update-a-repository);
|
|
147
|
+
- [REST: Replace all repository topics](https://docs.github.com/en/rest/repos/repos?apiVersion=2026-03-10#replace-all-repository-topics);
|
|
148
|
+
- [Disabling pull requests](https://docs.github.com/en/repositories/managing-your-repositorys-settings-and-features/enabling-features-for-your-repository/disabling-pull-requests);
|
|
149
|
+
- [Displaying a sponsor button](https://docs.github.com/en/repositories/managing-your-repositorys-settings-and-features/customizing-your-repository/displaying-a-sponsor-button-in-your-repository).
|
|
150
|
+
|
|
151
|
+
Если GitHub отклоняет `X-GitHub-Api-Version: 2026-03-10` или документированный field, сначала
|
|
152
|
+
перепроверь актуальную официальную документацию. Не переключайся автоматически на private API.
|